DDR memory bit errors with SocKit (Cyclone V SoC device)

The problem

There seems to be a minor DDR memory reliability issue with the SocKit, having the 5CSXFC6D6F31C8NES device marked “F AAAAU1319A”.

This can be detected by copying pseudorandom data from one buffer to another repeatedly, and then comparing the data between the buffers. The buffers must be large, to make sure the cache is flushed all the time. A single bit is flipped typically after a few Gigabytes of copied data or so.

A simple test program demonstrating this is at the bottom of this post. It should be compiled for Linux. The program accepts one single argument, which is the buffer size to use (in bytes).

This is what a typical session looks like:

# time ./memtest 16777216
Initialized lsr_state to 7ffeb059
On byte count 2985973160, position 0x7e3699, memcpy() length 16714658:

Destination:
7e3680 0d 1a 35 6b d6 ad 5a b5 6a d5 aa 55 aa 54 a9 52
7e3690 a4 48 91 22 45 8b 17 2e 5d bb 74 e8 d0 a1 42 85
7e36a0 0b 16 2c 59 b2 64 c8 90 21 43 87 0f 1e 3d 7b f6
7e36b0 ed da b4 68 d1 a2 44 88 10 20 40 80 01 03 07 0e
7e36c0 1d 3a 75 eb d7 af 5f bf 7e fd fb f6 ed db b6 6d
7e36d0 da b5 6a d4 a8 50 a1 43 87 0e 1c 38 70 e1 c3 86
7e36e0 0c 18 30 61 c2 85 0b 16 2d 5b b7 6e dc b9 72 e4
7e36f0 c9 93 27 4f 9f 3f 7f fe fd fa f5 eb d7 ae 5c b9
7e3700 72 e5 cb 96 2c 58 b1 63 c7 8e 1c 39 73 e6 cc 98

Source:
7e3680 0d 1a 35 6b d6 ad 5a b5 6a d5 aa 55 aa 54 a9 52
7e3690 a4 48 91 22 45 8b 17 2e 5d ba 74 e8 d0 a1 42 85
7e36a0 0b 16 2c 59 b2 64 c8 90 21 43 87 0f 1e 3d 7b f6
7e36b0 ed da b4 68 d1 a2 44 88 10 20 40 80 01 03 07 0e
7e36c0 1d 3a 75 eb d7 af 5f bf 7e fd fb f6 ed db b6 6d
7e36d0 da b5 6a d4 a8 50 a1 43 87 0e 1c 38 70 e1 c3 86
7e36e0 0c 18 30 61 c2 85 0b 16 2d 5b b7 6e dc b9 72 e4
7e36f0 c9 93 27 4f 9f 3f 7f fe fd fa f5 eb d7 ae 5c b9
7e3700 72 e5 cb 96 2c 58 b1 63 c7 8e 1c 39 73 e6 cc 98

real    1m0.834s
user    1m0.710s
sys    0m0.070

In this test run, an error was detected after about 60 seconds and almost 3 GB of data (2985973160 bytes, to be exact). Since we’re dealing with rare events, both the time and byte count may vary significantly until an error occurs. This can run for several minutes without anything happening too.

It may be significant to do this test after the system has been powered up from cold (i.e. been unpowered for a few minutes).

As seen above, the program dumps the hex data around the error, and points out the offset in the failed attempt, where the error was detected, 0x7e3699 in the case above. And indeed, the source buffer had the value 0xba, but in the destination buffer it was 0xbb. One single bit was flipped. It seems like it’s bits 0 and 1 that tend to turn out ’1′ instead of ’0′, but let’s skip the witchcraft.

It seems like the bit flipping occurs on writing to the memory, so the error is recorded in the DDR memory’s memory array, as opposed to a momentary error while reading. This speculation is backed by a test not shown in the program listed below, in which a second test is run when an error is detected. In this second test, the buffers are compared only by reading. The error was found consistently through several runs of this second test, indicating that the error is in fact written in memory, and not read wrong. Since the entire buffer was compared on each read-only comparison, finding the same error consistently cannot be attributed to caching.

The processor was configured as in the soc_system.qsys file included in soc_system_13_0_0_06252013_90253.tar.gz, which can be downloaded as a reference design for Linaro Desktop at Rocketboards. To be specific, the hps_0 settings for the memory interface and other hardware peripherals was bytewise identical (the bridges to FPGA had different settings, but that isn’t relevant to this issue).

A few words about terminations

It’s possible to eliminate these bit errors by modifying the ODT settings of the DDR memory. But let’s first explain what it’s all about.

As the signals going between the Cyclone chip and the DDR memory switch extremely fast, the short copper wires that connect these two devices are passing through electromagnetic waves, rather than steady voltages. These wires are analyzed in the same terms as antennas and waveguides, with the goal of reducing back-and-forth reflections, and damping them as fast as possible.

One of the means for reducing reflections is to place resistors, called terminations, at the ends of these wires. In order to achieve a good result and avoid a dense placement of a lot of components on the board, these resistors are often included on the chip’s silicon. In other words, it’s an On Die Termination (ODT). Whether they should be applied, and what resistance they have is programmable, both on the FPGA’s side and on the DDR memory. The choice is usually made in conjunction with running electromagnetic simulations on the PCB’s physical layout, and picking values that produce good waveforms. If this crucial part in the PCB design process is done improperly, memory corruption occurs, sometimes to the level of rendering the system useless, and sometimes causing rare bit flipping, as experienced with the SocKit.

There are four major parameters influencing the signal integrity:

  • The termination on the Cyclone V device: Whether applied, and its resistance
  • The Nominal ODT of the DDR memory: Whether applied, and its resistance. The term “nominal” is just a fancy word to distinguish from the one listed next;
  • The Dynamic ODT of the DDR memory: Whether applied, and its resistance. This optional feature allows programming a different resistance which is applied only when the data lines are used for a write operation (i.e. the lines are driven by the FPGA). When this feature is disabled (“off”) the Nominal ODT’s setting holds all the time.
  • The Output Drive Strength or Output Impedance of the DDR memory: This controls the current applied when the DDR memory drives the wires either high or low. The magnitude is given in terms of an equivalent resistor, connected either to the power supply or to ground.

Except for the item above, all parameters are set in Qsys by editing the HPS block, on the SDRAM tab, going to the “Memory Parameters” sub-tab.

When the reference design is followed, the Cyclone device is programmed to apply a 50 Ohm termination on all data wires. This is a result of the reference resistor on the board, R295 connected to D27, which is 100 Ohms.

The DDR is programmed to a nominal termination of RZQ/4 = 60 Ohms. The dynamic termination is enabled and set to RZQ/4 = 60 Ohms as well. The output drive strength is RZQ/7 = 34 Ohms. These figures are derived from the reference resistors to the memories, R288 and R269, both 240 Ohms.

There’s something peculiar about setting the dynamic ODT to the same value as the nominal, as turning the dynamic termination off altogether should have the same effect, in theory. As seen below, reality has it’s own say about this.

Tweaking termination settings

By all means, the correct way to set up the parameters related to signal integrity is applying the correct values that were chosen when the PCB was designed, based upon proper simulations. Since the performance of the reference design’s settings aren’t satisfactory, there’s no choice but tweaking the parameters until the bit errors vanish, hoping that the new setting will work well on other boards and throughout a reasonable temperature range. This is not a desired solution, but a last resort.

Several settings were tried out. I have to admit that I was surprised how little effect these settings had: The system had no problem booting in any of the experiments I made, and the difference was only sensed while running heavy tests.

The following three settings appeared to result in no errors (each one described with the one change relative to the reference design):

  • Output drive strength set to RZQ/6 = 40 Ohms
  • ODT completely turned off (Nominal and dynamic alike)
  • Disabling dynamic ODT off only.

Things that didn’t reduce errors: Setting the dynamic ODT to RZQ/2, setting the nominal ODT to RZQ/2 or RZQ/6, or disabling the nominal ODT while leaving the dynamic ODT as before.

The only change that could make sense is reducing the output drive strength: Recall that the errors were most likely generated on writes to the memory. One possible reason for bit flipping is that reflections keep running on the wires from a previous read operation when the lines are turned over for writing, so that the voltage levels of bits intended for writing is disrupted by this noise. Reducing the memory’s driving current obviously reduces the this noise as well.

Turning off the ODT should increase the reflections, so it’s not clear why this helped. And disabling the dynamic ODT should make any difference at all, since the nominal resistance is the same anyhow. Nevertheless, it was verified that this change made a difference.

When dealing with signal integrity without the proper simulation tools, it’s not rare that one can’t explain why one action helped and another didn’t.

As for my own attempt to solve the problem, I initially chose reducing the drive strength to RZQ/6. At least, this change doesn’t contradict common sense. But extensive tests exposed a bit error for each ~1 TB of data handled (this is very crude error rate estimation). Turning off ODT completely ran through the same longer test with no errors detected at all, so this was the setting I chose. This might be specific to my own board, though.

The program

So here it is, if you want to try it out yourself. It’s a hack of pieces of code I had around, so it’s not really top-notch software engineering… (and WordPress killed the indentation)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

static unsigned int lsr_state;
static long long count = 0;
static char rand_state[32];

void randseed() {
 int fd;
 fd = open("/dev/urandom", O_RDONLY);

 if (fd < 0) {
 perror("open");
 exit(1);
 }

 if (!initstate(0, rand_state, sizeof(rand_state))) {
 fprintf(stderr, "Call to initstate() failed.\n");
 exit(1);
 }

 if (read(fd, rand_state, sizeof(rand_state) ) != sizeof(rand_state)) {
 fprintf(stderr, "Failed to read from /dev/urandom\n");
 exit(1);
 }

 close(fd);

 if (!setstate(rand_state)) {
 fprintf(stderr, "Call to setstate() failed.\n");
 exit(1);
 }
}

void hexprint (unsigned char *buf, int at, unsigned long int n) {
 int i, j, from, to;

 from = (at & 0xfffffff0) - 16;
 if (from < 0)
 from = 0;

 to = (at & 0xfffffff0) + 127;
 if (to >= n)
 to = n-1;

 for (i=from; i<to; i+=16) {
 printf("%04x", i);

 for (j=i; ((j<(i+16)) && (j<=to)); j++)
 printf(" %02x", buf[j]);

 printf("\n");
 }
}

void exit_program(int sig) {
 fprintf(stderr, "memtest: Checked %lld bytes\n", count);

 exit(0);
}

int main(int argc, char *argv[]) {

 int bufsize, startpos1, startpos2, bytecount, i, bit;
 unsigned char *buf, *destbuf, *p, *b1, *b2;

 if (argc != 2) {
 fprintf(stderr, "Usage: %s buffer-size\n", argv[0]);
 exit(1);
 }

 (void) signal(SIGINT, exit_program);
 (void) signal(SIGQUIT, exit_program);
 (void) signal(SIGTERM, exit_program);
 (void) signal(SIGALRM, exit_program);

 bufsize = atoi(argv[1]);

 if (bufsize < 65536) {
 fprintf(stderr, "Bufsize %d too small (at least 65536)\n", bufsize);
 exit(1);    
 }

 if (!(buf = malloc(bufsize))) {
 fprintf(stderr, "Failed to allocate %d bytes for buffer\n", bufsize);
 exit(1);
 }

 if (!(destbuf = malloc(bufsize))) {
 fprintf(stderr, "Failed to allocate %d bytes for buffer\n", bufsize);
 exit(1);
 }

 randseed();

 do {
 lsr_state = random();
 fprintf(stderr, "Initialized lsr_state to %08x\n", lsr_state);
 } while (lsr_state == 0);

 for (i=0; i<bufsize; i++) {
 p = (unsigned char *) &lsr_state;

 buf[i] = *p++;

 bit = ((lsr_state >> 19) ^ (lsr_state >> 2)) & 0x01;

 lsr_state = (lsr_state << 1) | bit;

 if (lsr_state == 0) {
 fprintf(stderr, "Huh? The LSR state is zero!\n");
 exit(1);
 }
 }

 while (1) {
 startpos1 = random() & 0x7fff;
 startpos2 = random() & 0x7fff;
 bytecount = bufsize - 32768 - (random() & 0x7fff);

 b1 = destbuf + startpos1;
 b2 = buf + startpos2;

 memcpy(b1, b2, bytecount);

 for (i=0; i<bytecount; i++, count++)
 if (*b1++ != *b2++) {
 printf("On byte count %lld, position 0x%x, memcpy() length %d:\n",
 count, i, bytecount);

 printf("\nDestination:\n");
 hexprint(destbuf + startpos1, i, bufsize);
 printf("\nSource:\n");
 hexprint(buf + startpos2, i, bufsize);

 exit(1);
 }
 }
 return 0;
}

Experimenting with SDC/Tcl wildcards: Quartus TimingQuest Timing Analyzer

Wildcards

There is a certain confusion regarding how wildcards are matched in the SDC file (in fact, by the Tcl commands), which is why full paths are often used. This causes overloaded SDC files that don’t survive changes in the hierarchy.

For example, regarding get_pins, the SDC and TimeQuest API Reference Manual page 2-15 states that pipe characters (“|”) are treated as special characters, and are therefore not matched against the “*” wildcard in the default mode. So by default, the full path has to be given, except for specific strings within hierarchies.

The -hierarchical flag somewhat helps by allowing relative names (skip the beginning of the path).

For a classic Tcl match, where ‘*’ can match a pipe character, use -compatibility_mode.

But what about get_clocks?

Experimenting

One significant advantage of Tcl scripting over Xilinx’ UCF is that one can try out the expressions in a Tcl shell. Some basics can be found in page 3-17 of the Quartus II Handbook, vol.1, chapter 3.

More inspiration can be taken from the examples in the SDC and TimeQuest API Reference Manual. It may also be helpful to look at the Quartus II Scripting Reference Manual.

From the command line, using the “-s” flag:

$ quartus_sta -s

After the welcome note, open the project (after fitting) and create a timing netlist:

tcl> project_open myproject
tcl> create_timing_netlist
tcl> read_sdc
tcl> update_timing_netlist

for effectively opening myproject.qsf. The two latter are required for get_clocks to work. One can go e.g.

tcl> get_clocks -long_help

to get some help (same text as in the manuals).

It’s possible to test what matches which command. For example, to list the PLL-derived clocks (based upon the signal’s name):

tcl> set mypins [ get_pins -compatibility_mode *|divclk ]
tcl> foreach_in_collection pin $mypins { puts [get_pin_info -name $pin] }

The Tcl shell will print something like “_col0″ in the middle to indicate that a collection has been set up. This collection is accessed through $mypins. The second command prints the matched pins to the console.

Or for those who prefer one-liners (all pins on top-level):

tcl> query_collection -all [ get_pins * ]

The “-all” flag overrides the default limit of 20 elements. To have each printed on a separate line,

tcl> foreach i [ query_collection -all [ get_pins -hierarchical * ] ] { puts "Pin: $i" }

The -hierarchical flag is important. Without it, only the toplevel pins are given (even for just [ get_pins ]). Counterintuitive, but nevertheless true. The -compatibility_mode flag is also fine (used a lot in this post) but is Quartus specific.

The counterpart for “-all” is “-limit 1″, which fetches only the first element.

So what about get_clocks?

Listing all clocks in the design on separate lines:

tcl> foreach i [ query_collection -all [ get_clocks ] ] { puts "$i" }

Or use foreach_in_collection:

tcl > foreach_in_collection i [ get_clocks ] { puts [ get_clock_info $i -name ] }

Note that get_clock_info can obtain information other than just the name.

Alternatively, use “join” instead of “foreach”:

tcl> join [ query_collection -all [ get_clocks ] ] "\n"

If nothing is printed, and instead it says

Warning (332173): Ignored filter: * could not be matched with a clock

it’s most likely because read_sdc and update_timing_netlist haven’t been issued, as mentioned above.

tcl> foreach i [ query_collection -all [ get_clocks *|vga_pll|*|divclk] ] { puts $i }

which, surprisingly enough worked in the convenient way: The wildcards matched pipe characters, so one can, in fact, use this simple format in SDC files, e.g.

set_false_path -from [get_clocks *|vga_pll|*|divclk] -to [get_clocks *|bus_pll|*|divclk]
set_false_path -from [get_clocks *|bus_pll|*|divclk] -to [get_clocks *|vga_pll|*|divclk]

for setting up false paths between two clocks that are derived from a common reference with PLLs, the wrong way.

The correct way is with set_clock_groups, but that’s not what this post is about… And by the way, Quartus doesn’t have a shortcut to include derived clocks in set_clock_groups, like in Vivado. So the PLLs’ output clocks must be named explicitly (but wildcards help).

Getting all kind of info

If we’re at it, all kind of info can be obtained on cells in the design. For example, the location of certain instances in the design (pins etc. can also be obtained with different parameters to get_cell_info):

foreach_in_collection cell [ get_cells -compatibility_mode *rx_pma.rx_cdr] { puts "[get_cell_info $cell -location ]: [get_cell_info $cell -name]" }

QSF scripting

Pointing at specific cells by virtue of expressions works within a script, but not in the QSF file. For example, this works as a script

set_instance_assignment -name CDR_BANDWIDTH_PRESET High -to [ get_cells -compatibility_mode *|xcvr_inst|*rx_pma.rx_cdr]

but fails in a QSF file.

It’s possible to retrieve the already existing assignments. Try

tcl> get_instance_assignment -help
tcl> get_all_assignments -long_help

The latter gives some interesting examples on scanning existing assignments. In particular, turning one of the examples into a (very long) one-liner, once can go

tcl> foreach_in_collection asgn_id [get_all_assignments -type instance -name *] { set from [get_assignment_info $asgn_id -from] ; set to [get_assignment_info $asgn_id -to] ; set name   [get_assignment_info $asgn_id -name] ; set value  [get_assignment_info $asgn_id -value] ; puts "$name ($from -> $to) = $value" }

in order to list all instance assignments (i.e. echo the QSF’s assignments).

Same for all global assignments (and there are many):

foreach_in_collection asgn_id [get_all_assignments -type global -name *] { set entity [get_assignment_info $asgn_id -entity] ; set name [get_assignment_info $asgn_id -name] ; set value  [get_assignment_info $asgn_id -value] ; puts "$entity: $name = $value" }

Not clear what it’s useful for, but anyhow

Running a nested X-Windows server

Why?

It’s sometimes desired to run an X-Windows program in a separate “screen” but not actually have another screen. The expensive way is to bring up a whole virtual server. But if it’s fine to run the program on the same computer, all we want is to have a window, in which the program is confined.

This is handy if the program has a tendency to steal focus with popups all the time.

It’s also useful for opening windows from a remote machine, and the regular X server refuses despite being generous with “xhost +”. The nested server isn’t picky with who it’s hosting.

Some installations

First, install Xnest if it’s not already installed, e.g. (as root)

# yum install Xnest

It’s also possible to install a very simple (and somewhat yucky) window manager

# yum install twm

Action

Then open a new window, which turns into a new X server:

$ Xnest -s 0 -ac :1 -geometry 1900x1020+5+0&

The dimensions given by the “geometry” flag are those making a full screen coverage on my monitor. This varies, of course.

Launch a Window Manager and a terminal window in the new X server. The former is necessary to make windows movable, resizable, etc.

$ twm -display :1
$ DISPLAY=:1 gnome-terminal &

Note that apparently nothing happens after launching the first command, because there are no clients in the Xnest window.

And then use the terminal to run applications inside the nested X-window server.

twm too yucky?

The Gnome Window Manager can be used instead of the command issuing twm:

$ DISPLAY=:1 gnome-wm &

The reason not to use Gnome’s window manager is that it allows minimizing windows. If that is done accidentally, the window is lost (unless a bottom panel is generated, which starts to get a bit messy for a simple task).

 

A ghost process eating up all RAM?

After a “find” operation spanning the entire disk, I suddenly had System Monitor (2.28.0 of Fedora 12) telling me that 11.5 GB out of the existing 16 GB were used up, and the applet said 75% was “in use by programs”. Really. Virtually nothing was running on the system.

Just to be sure, I tried out

$ ps aux --sort -rss | less

(list processes sorted by resident memory) and as expected, no memory hog to be seen.

So what does the kernel (a home cooked 2.6.35.4) say for itself?

$ cat /proc/meminfo
MemTotal:       16463436 kB
MemFree:          130568 kB
Buffers:         2818532 kB
Cached:          1387344 kB
SwapCached:            0 kB
Active:          3368176 kB
Inactive:        2246912 kB
Active(anon):    1407872 kB
Inactive(anon):   339276 kB
Active(file):    1960304 kB
Inactive(file):  1907636 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:               368 kB
Writeback:             0 kB
AnonPages:       1409264 kB
Mapped:           174032 kB
Shmem:            337932 kB
Slab:           10246976 kB
SReclaimable:    9526744 kB
SUnreclaim:       720232 kB
KernelStack:        5112 kB
PageTables:        47880 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     8231716 kB
Committed_AS:    4241756 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      122564 kB
VmallocChunk:   34359572612 kB
HardwareCorrupted:     0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:      350080 kB
DirectMap2M:    16422912 kB

Hmmm… That’s the kernel eating up ~10 GB, most of which is reclaimable. This page seems to explain the issue: As the disk was scanned, directory and inode metadata was probably cached. So what if that takes nearly 10 GB?

The thing is that it gives a somewhat misleading picture of the computer’s state.

So let’s just tell the kernel to drop all those caches, as suggested in that page:

As root, just go

# echo 3 > /proc/sys/vm/drop_caches

and watch how the memory meter drops. This problem is supposed to be solved in kernel not as ancient as mine.

Gnome 2 failing to load an Applet for no apparent reason

Fedora 12: It started with some error message about something crashing, and the Workspace Switcher applet was gone.

I tried re-adding it to the panel, but I got an error window saying that the panel encountered a problem while loading “OAFIID:GNOME_WorkspaceSwitcherApplet”. And offered me to delete it from the panel. Or not delete it. Nothing helped. Even not restarting compiz.

I took a look in the panel configuration directory, just to find that there were multiple instances of the same applet:

$ grep -r GNOME_WorkspaceSwitcherApplet ~/.gconf | sort

.gconf/apps/panel/applets/applet_0/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_10/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_11/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_12/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_13/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_15/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_16/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_5/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_6/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_7/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_8/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue>
.gconf/apps/panel/applets/applet_9/%gconf.xml:        <stringvalue>OAFIID:GNOME_WorkspaceSwitcherApplet</stringvalue

In a normal setting, there are three lines here (why? Never mind).

The solution was surprisingly simple. Just find the process with the name gnome-panel. And kill it (with a plain kill, no special signal is necessary).

$ killall gnome-panel

The panel disappears reappears promptly. Maybe with duplicate applets, but hey, it finally works!

Linux kernel version magic: Adding the missing plus suffix

Trying to insmod a kernel module, which was just compiled against the true headers of the running kernel, I got:

# insmod mymodule.ko
insmod: error inserting 'mymodule.ko': -1 Invalid module format

And this in /var/log/syslog:

mymodule: version magic '3.3.0-xxx SMP preempt mod_unload ARMv7 ' should be '3.3.0-xxx+ SMP preempt mod_unload ARMv7 '

Really? Are you picking on me? Rejecting my module just because of a missing plus sign?!

The plus sign is there in the first place because the kernel was compiled with a git repository, which displayed modifications relative to the official tree. To avoid it, I could have compiled the kernel after renaming the .git directory to something else prior to compiling the kernel. But I forgot to do that.

In fact, I should have turned kernel magic versioning off altogether.

The plus sign is absent in the current kernel headers, because they were generated without the git repository (natively on the target). Hence the difference.

One could use modprobe –force, but isn’t it nicer to just get the version magic right?

This solution holds for the 3.3.0 kernel, but probably works on others as well.

The file you want to edit is /usr/src/kernels/{your version}/include/generated/utsrelease.h and just add the ‘+’ sign to the end of the version number. E.g.

#define UTS_RELEASE "3.3.0-xxx+"

And now all modules compiled against the kernel will load with no issues.

For those interested in the gory details: It’s mymodule.mod.c that is responsible for supplying the version magic. It’s compiled into mymodule.mod.o, and then fused together with mymodule.o to become mymodule.ko. This is what the famous “MODPOST” compilation stage does (in fact, it’s a make -f on scripts/Makefile.modpost, which in turn calls scripts/mod/modpost).

mymodule.mod.c gets the number by including linux/vermagic.h (actually, it includes other similar files as well) which in turn includes generated/utsrelease.h, which supplies the version magic explicitly.

Updating time zone information on a Linux machine

Since daylight saving time is an issue for political dispute in Israel, the time of switching back and forth changes all too often. To keep my computer on track, I need to update the timezone info file every now and then.

There are a lot of tutorials on the web about this, for example these two. Here’s a quick summary:

Check the current situation. Is the timezone file in sync with the real world?

$ zdump -v /etc/localtime | less

Ah, so it isn’t. Go to IANA’s site and download the data file (e.g. tzdata2013d.tar.gz). From within that bundle, fetch the file named “asia”. And go:

$ zic -d tmpdir asia

which creates a new directory, tmpdir, with several zone files inside.

Unfortunately, trying to run zdump on just a file doesn’t work very well. So I went to /usr/share/zoneinfo and removed the file called Israel as well as Asia/Jerusalem and Asia/Tel_Aviv, and the /etc/localtime as well. They were all the same, identical file.

Then copied tmpdir/Asia/Jerusalem into /usr/share/zoneinfo/Asia and

# ln -s /usr/share/zoneinfo/Asia/Jerusalem localtime

Plus, in /usr/share/zoneinfo/Asia, just to be safe:

# ln -s Jerusalem Tel_Aviv

and in /usr/share/zoneinfo:

# ln -s Asia/Jerusalem Israel

And a final check (same as before)

$ zdump -v /etc/localtime | less

A machine reboot may be in place to make all programs catch up with the new data. Or just restart key programs, if it doesn’t matter all that much.

Just to be 100% sure that the change is in effect, a one-liner script can be used. For example how is GMT time vs. local time compare 48 days from now?

$ perl -e '$t=time() + 86400*48; print scalar gmtime($t)." --> ".scalar localtime($t)."\n";'

Side notes: The sources for zic and other utilities are available next to the data file on IANA’s site. But any sane Linux distribution should have these installed already. Or at least at the repository. Also worth to note, that the syntax of these data files is pretty cool: It’s not just dates, but political rules.

 

Drupal 7: Making a global regular expression replacement

The goal: Create spam-harvesting safe mailto links anywhere in the pages, generated on the fly with slightly obfuscated JavaScript, so that harvesting bots don’t get the email addresses. To make it slightly more complicateded, I want a replacement pattern with a parameter, telling the replacement script which email address to inject.

In other words, if I wrote %mail{1}% somewhere in the page’s source, that is replaced by the second email address in a known list of addresses.

Of course there the Token module. But I wanted the replacement to depend on the token string. Globally.

I use arthemia, so this appears everywhere in the paths to files.

Bad attempt #1

I thought that dvessel’s suggestion on a pretty old page would do the job (and it looks like it was obvious to everyone which file to edit, sites/all/themes/arthemia/template.php). But that suggestion was good for Drupal 5.

The theme I used, arthemia, already has an arthemia_preprocess_page() function, but not an arthemia_render_template(). I followed the suggestion, and nothing happened, of course. Drupal 7 doesn’t look in that direction.

Bad attempt #2

There is this golden rule in Drupal, that you don’t edit the core functions. Well, well, it was time to break it again. This time with the core of the core: include/common.inc, modifying drupal_render. Yup, I couldn’t have broken that rule harder. But after a few hours of the regular Drupal ceremony of reading a lot of useless tips on the web, I thought it was best to just get the job done.

The main problem was that all hooks are related to a certain type of elements. There is no hook for “just before the HTML is handed over to the browser”. Or that’s the impression I got.

First, I added the script I wanted included in every page. It would prefer it to be in the <head> section (because that’s usually where global scripts are) and I would prefer it to be a piece of content. But again, hours of looking for the “Drupal” way to do it, I just added the following to the top of sites/all/themes/arthemia/page.tpl.php:

<script language="JavaScript">
<!--
function myfunction(i) {
 [ ... ]
}
-->
</script>

It’s an ugly solution, but it gets the job done. I could, in theory, plant it as the content of an unused part of the page, and then mangle the page’s structure as it was being processed. Too much work for the cute shortcut of altering the script from Drupal’s interface.

And now to finding the markups, and make them run the function above. Using the preg_replace_callback() function, render_template was edited to this in the end of the function (just before “return $output”):

// Mangle
 $output = preg_replace_callback("|%mail\{(\d+)\}%|",
   function ($matches) {
     return "<script language=\"JavaScript\">\n<!--\nmyfunction($matches[1]);\n-->\n</script>";
   },
   $output)

That worked well. Actually, too well. The replacement took place also in editing boxes, so the original markup text couldn’t be edited.

The correct way

OK, so I took one step back. Let’s use the regular hooking mechanism, after all, and define the hooks for the content and the footer. Good enough. And as a side effect, the core is left intact, as I only edit the theme’s page template file.

So instead of the changes above I added this to the top of sites/all/themes/arthemia/page.tpl.php:

<script language="JavaScript">
<!--
function myfunction(i) {

 [ ... ]
}
-->
</script>

<?php
function mail_post_render($content, $element) {
  $content = preg_replace_callback("|%mail\{(\d+)\}%|",
    function ($matches) {
      return "<script language=\"JavaScript\">\n<!--\nmyfunction($matches[1]);\n-->\n</script>";
    },
    $content);

 return $content;
}

if ($page['content']) {
 $page['content']['#post_render'] = array('mail_post_render');
}
if ($page['footer']) {
 $page['footer']['#post_render'] = array('mail_post_render');
}

?>

And that finally worked in a sane manner: The footer and content were mangled, and I was still able to edit the original text. Hurray!

As usual, a simple task took a full day to complete. Welcome to the wonderful world of Drupal.

Resetting a Windows XP/7/8 password, the Linux way

What happens if a Windows user loses his or her password? No problem, Windows was never meant to be secure. Only appear as if it was.

There are several automatic tools out there. I preferred running my Fedora-based LiveUSB and fix it while actually seeing what I’m doing. The whole thing is about modifying the SAM file used on authentication.

Note that the flow shown below failed. What did work, eventually, was unlocking the Administrator account, which doesn’t have any password to begin with. Why I failed to reset a user’s password is beyond me. Maybe because it had administrator’s privileges? Maybe because it was Windows 8?

Anyhow, first I installed chntpw. It’s an offline registry editor it says somewhere, but I never checked that.

# yum install chntpw

Following this guide, mount the partition and change directory to where the SAM file is (Windows/System32/config, like any NT machine). Note the capital letters in Windows and System32.

Then go (possibly as root)

$ chntpw SAM
chntpw version 0.99.6 080526 (sixtyfour), (c) Petter N Hagen
Hive <SAM> name (from header): <\SystemRoot\System32\Config\SAM>
ROOT KEY at offset: 0x001020 * Subkey indexing type is: 666c <lf>
Page at 0x6000 is not 'hbin', assuming file contains garbage at end
File size 262144 [40000] bytes, containing 5 pages (+ 1 headerpage)
Used for data: 222/17400 blocks/bytes, unused: 14/2920 blocks/bytes.

* SAM policy limits:
Failed logins before lockout is: 0
Minimum password length        : 0
Password history count         : 0
| RID -|---------- Username ------------| Admin? |- Lock? --|
| 01f4 | Administrator                  | ADMIN  | dis/lock |
| 01f5 | Guest                          |        | dis/lock |
| 03e9 | myself                         | ADMIN  | dis/lock |

---------------------> SYSKEY CHECK <-----------------------
SYSTEM   SecureBoot            : -1 -> Not Set (not installed, good!)
SAM      Account\F             : 0 -> off
SECURITY PolSecretEncryptionKey: -1 -> Not Set (OK if this is NT4)
Syskey not installed!

RID     : 0500 [01f4]
Username: Administrator
fullname:
comment : Built-in account for administering the computer/domain
homedir : 

User is member of 1 groups:
00000220 = Administrators (which has 2 members)

Account bits: 0x0211 =
[X] Disabled        | [ ] Homedir req.    | [ ] Passwd not req. |
[ ] Temp. duplicate | [X] Normal account  | [ ] NMS account     |
[ ] Domain trust ac | [ ] Wks trust act.  | [ ] Srv trust act   |
[X] Pwd don't expir | [ ] Auto lockout    | [ ] (unknown 0x08)  |
[ ] (unknown 0x10)  | [ ] (unknown 0x20)  | [ ] (unknown 0x40)  | 

Failed login count: 0, while max tries is: 0
Total  login count: 10

- - - - User Edit Menu:
 1 - Clear (blank) user password
 2 - Edit (set new) user password (careful with this on XP or Vista)
 3 - Promote user (make user an administrator)
 4 - Unlock and enable user account [probably locked now]
 q - Quit editing user, back to user select
Select: [q] > q

This shows a lot of info, including the list of users. The point is to reset a specific user. Changes made like this will affect all users.

To get just the list of users,

$ chntpw -l SAM
chntpw version 0.99.6 080526 (sixtyfour), (c) Petter N Hagen
Hive <SAM> name (from header): <\SystemRoot\System32\Config\SAM>
ROOT KEY at offset: 0x001020 * Subkey indexing type is: 666c <lf>
Page at 0x6000 is not 'hbin', assuming file contains garbage at end
File size 262144 [40000] bytes, containing 5 pages (+ 1 headerpage)
Used for data: 222/17400 blocks/bytes, unused: 14/2920 blocks/bytes.

* SAM policy limits:
Failed logins before lockout is: 0
Minimum password length        : 0
Password history count         : 0
| RID -|---------- Username ------------| Admin? |- Lock? --|
| 01f4 | Administrator                  | ADMIN  | dis/lock |
| 01f5 | Guest                          |        | dis/lock |
| 03e9 | myself                         | ADMIN  | dis/lock |

And now let’s modify only one user in the list

$ chntpw -u 'myself' SAM
chntpw version 0.99.6 080526 (sixtyfour), (c) Petter N Hagen
Hive <SAM> name (from header): <\SystemRoot\System32\Config\SAM>
ROOT KEY at offset: 0x001020 * Subkey indexing type is: 666c <lf>
Page at 0x6000 is not 'hbin', assuming file contains garbage at end
File size 262144 [40000] bytes, containing 5 pages (+ 1 headerpage)
Used for data: 222/17400 blocks/bytes, unused: 14/2920 blocks/bytes.

* SAM policy limits:
Failed logins before lockout is: 0
Minimum password length        : 0
Password history count         : 0
| RID -|---------- Username ------------| Admin? |- Lock? --|
| 01f4 | Administrator                  | ADMIN  | dis/lock |
| 01f5 | Guest                          |        | dis/lock |
| 03e9 | myself                         | ADMIN  | dis/lock |

---------------------> SYSKEY CHECK <-----------------------
SYSTEM   SecureBoot            : -1 -> Not Set (not installed, good!)
SAM      Account\F             : 0 -> off
SECURITY PolSecretEncryptionKey: -1 -> Not Set (OK if this is NT4)
Syskey not installed!

RID     : 1001 [03e9]
Username: myself
fullname:
comment :
homedir : 

User is member of 1 groups:
00000220 = Administrators (which has 2 members)

Account bits: 0x0214 =
[ ] Disabled        | [ ] Homedir req.    | [X] Passwd not req. |
[ ] Temp. duplicate | [X] Normal account  | [ ] NMS account     |
[ ] Domain trust ac | [ ] Wks trust act.  | [ ] Srv trust act   |
[X] Pwd don't expir | [ ] Auto lockout    | [ ] (unknown 0x08)  |
[ ] (unknown 0x10)  | [ ] (unknown 0x20)  | [ ] (unknown 0x40)  | 

Failed login count: 2, while max tries is: 0
Total  login count: 1

- - - - User Edit Menu:
 1 - Clear (blank) user password
 2 - Edit (set new) user password (careful with this on XP or Vista)
 3 - Promote user (make user an administrator)
 4 - Unlock and enable user account [probably locked now]
 q - Quit editing user, back to user select
Select: [q] > 1

Hives that have changed:
 #  Name
 0  <SAM>
Write hive files? (y/n) [n] : y
 0  <SAM> - OK

In this case it clears the password (the choice of “1″). In fact, it appears like the password was already cleared when I captured this log, but it did no good.

As mentioned above, the password was still required after this, so the operation failed. I also failed to change the password for this user.

Using perl for in-place search-and-replace

To make a long story short:

$ perl -pi.bak -e 's/this/that/g' `find inhere/ -type f`

or to delete entire lines that contain a certain substring:

$ perl -pi.bak -e 's/^.*?RUNTIME_PARAM.(?:OUTPUT|SHARED)DIR.*?[\n\r]+//gm' $(find . -iname \*.xci)

This will create backup files. Yes, you want them. Really. Otherwise, in particular under Windows, errors like this may show up:

Can't do inplace edit on inhere/example.c: Permission denied.

This may look harmless, but in fact the target file ends up complete deleted. So don’t go just perl -pi -e. It may go horribly wrong.

Another thing to watch out for is having the .git subdirectory included in the “find” statement. In particular, “find .” may lead to this. Messing up the git repository is no fun.

The in-place execution runs separately on each line, so it’s not useful for replacing things that span across more than one line. In that case, a “full” perl script should be used. For example, in order to remove multiple blank lines from C sources,

$ for i in *.c *.h ; do perl -e 'local $/; $a=<>; $a=~s/\n{3,}/\n\n/g; print $a;' < $i > $i.new; mv -f $i.new $i; done

This removes any occurrence of three or more newlines into two. It’s fine to have an empty line, but not more than one.