memtest86+という便利なプログラムを用いると、PC/AT互換機のメモリが壊れていないかどうかチェックすることが可能です。memtest86+は、フロッピーからブートしたり、ネットブートしたりすることが出来るので、PC/AT互換機のメモリテストには大変重宝します。

弊社でも、サーバの出荷前の検査や、EDACの動作チェックなど、頻繁に利用しています。
今回以下のような改良を行い、少し使いやすくしてみました。
    1.memtest86+はIntel i3000をサポートしていないため、Uncorrectable Errorは検出出来てもCorrectable Errorは検出できかったので、i3000をサポートするようにしました。
    2.シリアルポートの出力をCOMBにも送れるようにしました。
    3.シリアルポートで出力を見ている場合、画面の内容が更新された部分しか送られてこないため、途中から接続した場合に意味のある出力が得られません。そこで、一定の感覚で画面全体を更新するようにしました。

これらの問題を解消するためのパッチを書きましたので、以下に貼ります。

diff -Naur memtest86+-1.70.orig/config.h memtest86+-1.70/config.h
--- memtest86+-1.70.orig/config.h    2006-12-27 10:33:06.000000000 +0900
+++ memtest86+-1.70/config.h    2007-12-17 18:26:33.000000000 +0900
@@ -13,10 +13,10 @@
 /* SERIAL_CONSOLE_DEFAULT -  The default state of the serial console. */
 /*    This is normally off since it slows down testing.  Change to a 1 */
 /*    to enable. */
-#define SERIAL_CONSOLE_DEFAULT 0
+#define SERIAL_CONSOLE_DEFAULT 1
 
 /* SERIAL_BAUD_RATE - Baud rate for the serial console */
-#define SERIAL_BAUD_RATE 9600
+#define SERIAL_BAUD_RATE 19200
 
 /* BEEP_MODE - Beep on error. Default off, Change to 1 to enable */
 #define BEEP_MODE 0
@@ -34,3 +34,10 @@
 /*    Normally enabled */
 #define USB_WAR
 
+/* COMA - Using serial port A */
+#define COMA 0
+/* COMB - Using serial port B */
+#define COMB 1
+
+/* FLUSH_PER_TSTSEQ - Flush per test sequence */
+#define FLUSH_PER_TSTSEQ 1
diff -Naur memtest86+-1.70.orig/controller.c memtest86+-1.70/controller.c
--- memtest86+-1.70.orig/controller.c    2007-01-04 15:43:31.000000000 +0900
+++ memtest86+-1.70/controller.c    2007-12-17 18:26:33.000000000 +0900
@@ -682,6 +682,12 @@
 
 }
 
+static void setup_i3000(void)
+{
+  ctrl.mode = ECC_CORRECT;
+  ctrl.poll = 1;
+  pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xc8, 2, 0x0b03);
+}
 
 static void poll_i875(void)
 {
@@ -921,6 +927,41 @@
     }
 }
 
+static void poll_i3000(void)
+#define PAGE_SHIFT 12
+#define I3000_DEAP_PFN(edeap, deap)    ((((edeap) & 1) << (32 - PAGE_SHIFT)) | \
+                    ((deap) >> PAGE_SHIFT))
+{
+  unsigned short errsts;
+  unsigned char derrsyn;
+  unsigned char edeap;
+  unsigned int deap;
+  unsigned int page;
+
+  pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xc8, 2, (void *)&errsts);
+
+  if(errsts & 0x0b03) {
+    /* error found */
+    pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x70, 1, (void *)&edeap);
+    pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x58, 4, (void *)&deap);
+    pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x5c, 1, (void *)&derrsyn);
+  } else {
+    return;
+  }
+
+  page = I3000_DEAP_PFN(edeap, deap);
+  if(errsts & 0x0002) {
+    print_ecc_err(page, 0, 0, derrsyn, 0);
+  } else {
+    print_ecc_err(page, 0, 1, derrsyn, 0);
+  }
+
+  /* clear error bits */
+  pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xc8, 2, 0x0b03);
+
+  return;
+}
+
 static void poll_iE7520(void)
 {
     unsigned long ferr;
@@ -1331,6 +1372,10 @@
 
 }
 
+static void poll_fsb_i3000(void)
+{
+}
+
 static void poll_fsb_nf4ie(void) {
 
     double dramclock, dramratio, fsb;
@@ -1688,6 +1733,10 @@
 
 }
 
+static void poll_timings_i3000(void)
+{
+}
+
 static void poll_timings_i875(void) {
 
     ulong dev6, dev62;
@@ -2137,7 +2186,9 @@
     { 0x8086, 0x2774, "Intel i955X",          0, poll_fsb_i945, poll_timings_i925, setup_i925, poll_nothing},
     { 0x8086, 0x277C, "Intel i975X",          0, poll_fsb_i975, poll_timings_i925, setup_i925, poll_nothing},
     { 0x8086, 0x27A0, "Intel P965/G965",      0, poll_fsb_i965, poll_timings_i925, setup_i925, poll_nothing},
-    { 0x8086, 0x2790, "Intel Q963/Q965",      0, poll_fsb_i965, poll_timings_i925, setup_i925, poll_nothing}   
+    { 0x8086, 0x2790, "Intel Q963/Q965",      0, poll_fsb_i965, poll_timings_i925, setup_i925, poll_nothing},
+
+    { 0x8086, 0x2778, "Intel i3000", 1, poll_fsb_i3000, poll_timings_i3000, setup_i3000, poll_i3000}
 };
 
 static void print_memory_controller(void)
diff -Naur memtest86+-1.70.orig/main.c memtest86+-1.70/main.c
--- memtest86+-1.70.orig/main.c    2006-12-27 10:33:06.000000000 +0900
+++ memtest86+-1.70/main.c    2007-12-17 18:26:33.000000000 +0900
@@ -10,6 +10,8 @@
 
 #include "test.h"
 #include "defs.h"
+#include "config.h"
+#include "screen_buffer.h"
 #undef TEST_TIMES
 #define DEFTESTS 11
 
@@ -213,6 +215,9 @@
     if (v->testsel >= 0) {
         v->test = v->testsel;
     }
+#if FLUSH_PER_TSTSEQ
+    flush_screen();
+#endif
     dprint(LINE_TST, COL_MID+6, v->test, 2, 1);
     cprint(LINE_TST, COL_MID+9, tseq[v->test].msg);
     set_cache(tseq[v->test].cache);
diff -Naur memtest86+-1.70.orig/screen_buffer.c memtest86+-1.70/screen_buffer.c
--- memtest86+-1.70.orig/screen_buffer.c    2006-12-27 10:33:06.000000000 +0900
+++ memtest86+-1.70/screen_buffer.c    2007-12-17 18:26:33.000000000 +0900
@@ -108,6 +108,13 @@
     }
 }
 
+void flush_screen(void)
+{
+  int iy;
+  for(iy = 0; iy < SCREEN_Y; iy++) {
+    ttyprint(iy, 0, screen_buf[iy]);
+  }
+}
 
 void tty_print_screen(void)
 {
diff -Naur memtest86+-1.70.orig/screen_buffer.h memtest86+-1.70/screen_buffer.h
--- memtest86+-1.70.orig/screen_buffer.h    2006-12-27 10:33:06.000000000 +0900
+++ memtest86+-1.70/screen_buffer.h    2007-12-17 18:26:33.000000000 +0900
@@ -22,4 +22,5 @@
 void tty_print_line(int y, int x, const char *text);
 void tty_print_screen(void);
 void print_error(char *pstr);
+void flush_screen(void);
 #endif /* SCREEN_BUFFER_H_1D10F83B_INCLUDED */
diff -Naur memtest86+-1.70.orig/serial.h memtest86+-1.70/serial.h
--- memtest86+-1.70.orig/serial.h    2006-12-27 10:33:06.000000000 +0900
+++ memtest86+-1.70/serial.h    2007-12-17 18:26:33.000000000 +0900
@@ -14,6 +14,8 @@
 #ifndef _LINUX_SERIAL_REG_H
 #define _LINUX_SERIAL_REG_H
 
+#include "config.h"
+
 #define UART_RX        0    /* In:  Receive buffer (DLAB=0) */
 #define UART_TX        0    /* Out: Transmit buffer (DLAB=0) */
 #define UART_DLL    0    /* Out: Divisor Latch Low (DLAB=1) */
@@ -136,8 +138,16 @@
  */
 
 #include "io.h"
+
+#if COMA
 #define serial_echo_outb(v,a) outb((v),(a)+0x3f8)
 #define serial_echo_inb(a)    inb((a)+0x3f8)
+#endif /* COMA */
+#if COMB
+#define serial_echo_outb(v,a) outb((v),(a)+0x2f8)
+#define serial_echo_inb(a)    inb((a)+0x2f8)
+#endif /* COMB */
+
 #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
 /* Wait for transmitter & holding register to empty */
 #define WAIT_FOR_XMITR \

Supermicroのサーバ上では、IPMIカード(http://www.supermicro.com/products/accessories/addon/AOC-IPMI20-E.cfm)と、FreeIPMI(http://www.gnu.org/software/freeipmi/)というソフトウェアを用いると、ネットワーク越しにBIOSの出力等を見たり、電源のオンオフなどを行うことが出きるようになります。
今回memtest86+を改良したことで、以下のようにメモリテストをリモートで実行することが出きるようになりました。

FreeIPMIでのメモリテスト画面
Image


物理的に破損させたメモリで、エラーを検出しているところ

Image