svn commit: r185579 - in head: sys/boot/i386/boot0 usr.sbin/boot0cfg

Luigi Rizzo luigi at FreeBSD.org
Wed Dec 3 06:53:59 PST 2008


Author: luigi
Date: Wed Dec  3 14:53:59 2008
New Revision: 185579
URL: http://svn.freebsd.org/changeset/base/185579

Log:
  Another, hopefully final set of changes to boot0 and boot0cfg.
  
  boot0.S changes:
  
  + import a patch from Christoph Mallon to rearrange the various
    print functions and save another couple of bytes;
  
  + implement the suggestion in PR 70531 to enable booting from
    any valid partition because even the extended partitions that
    were previously in our kill list may contain a valid boot loader.
    This simplifies the code and saves some bytes;
  
  + followwing up PR 127764, implement conditional code to preserve
    the 'Volume ID' which might be used by other OS (NT, XP, Vista)
    and is located at offset 0x1b8. This requires a relocation of the
    parameter block within the boot sector -- there is no other
    possible workaround.
    To address this, boot0cfg has been updated to handle both
    versions of the boot code;
  
  + slightly rearrange the strings printed in the menus to make
    the code buildable with all options. Given the tight memory
    budget, this means that with certain options we need to
    shrink or remove certain labels.
  
  and especially:
  
  	make -DVOLUME_LABEL -DPXE the default options.
  
    This means that the newly built boot0 block will preserve the
    Volume ID, and has the (hidden) option F6 to boot from INT18/PXE.
    I think the extra functionality is well worth the change.
  
    The most visible difference here is that the 'Default: ' string
    now becomes 'Boot: ' (it can be reverted to the old value
    but then we need to nuke 1/2 partition name or entries to
    make up for the extra room).
  
  boot0cfg changes:
  
  + modify the code to recognise the new boot0 structure (with the
    relocated options block to make room for the Volume id).
  
  + add two options, '-i xxxx-xxxx' to set the volume ID, -e c
    to modify the character printed in case of bad input
  
  PR:		127764 70531
  Submitted by:	Christoph Mallon (portions)
  MFC after:	4 weeks

Modified:
  head/sys/boot/i386/boot0/Makefile
  head/sys/boot/i386/boot0/boot0.S
  head/usr.sbin/boot0cfg/boot0cfg.8
  head/usr.sbin/boot0cfg/boot0cfg.c

Modified: head/sys/boot/i386/boot0/Makefile
==============================================================================
--- head/sys/boot/i386/boot0/Makefile	Wed Dec  3 11:48:20 2008	(r185578)
+++ head/sys/boot/i386/boot0/Makefile	Wed Dec  3 14:53:59 2008	(r185579)
@@ -9,12 +9,14 @@ SRCS=	${PROG}.S
 # Additional options that you can specify with make OPTS="..."
 # (these only apply to boot0.S)
 #
+#	-DVOLUME_SERIAL	support volume serial number (NT, XP, Vista)
 #	-DSIO		do I/O using COM1:
 #	-DPXE		fallback to INT18/PXE with F6
 #	-DCHECK_DRIVE	enable checking drive number
 #	-DONLY_F_KEYS	accept only Fx keys in console
 #	-DTEST		print drive number on entry
 #
+OPTS ?= -DVOLUME_SERIAL -DPXE
 CFLAGS += ${OPTS}
 
 # Flags used in the boot0.S code:

Modified: head/sys/boot/i386/boot0/boot0.S
==============================================================================
--- head/sys/boot/i386/boot0/boot0.S	Wed Dec  3 11:48:20 2008	(r185578)
+++ head/sys/boot/i386/boot0/boot0.S	Wed Dec  3 14:53:59 2008	(r185579)
@@ -30,10 +30,16 @@
 #ifdef ONLY_F_KEYS	/* Only F1..F6, no digits on console */
 #endif
 
-#ifdef TEST		/* enable some test code */
-#ifndef ONLY_F_KEYS
-#define ONLY_F_KEYS	/* make room for the test code */
+#ifdef VOLUME_SERIAL	/* support Volume serial number */
+#define B0_BASE	0x1ae	/* move the internal data area */
+#define SAVE_MEMORY
+#else
+#define B0_BASE	0x1b2
 #endif
+
+#ifdef TEST		/* enable some test code */
+#define SAVE_MEMORY
+#define SAVE_MORE_MEMORY
 #endif
 
 /*
@@ -118,7 +124,7 @@
  * 	to change the value passed to the linker in the Makefile
  * PRT_OFF is the location of the partition table (from the MBR standard).
  * B0_OFF is the location of the data area, known to boot0cfg so
- *	it cannot be changed.
+ *	it cannot be changed. Computed as a negative offset from 0x200
  * MAGIC is the signature of a boot block.
  */
 
@@ -127,7 +133,7 @@
 		.set LOAD,0x7c00		# Load address
 
 		.set PRT_OFF,0x1be		# Partition table
-		.set B0_OFF,0x1b2		# Offset of boot0 data
+		.set B0_OFF,(B0_BASE-0x200)	# Offset of boot0 data
 
 		.set MAGIC,0xaa55		# Magic: bootable
 
@@ -144,15 +150,16 @@
  * We need to define them as constant as the assembler cannot
  * compute them in its single pass.
  */
-		.set _NXTDRV,	-0x48		# Next drive
-		.set _OPT,	-0x47 		# Default option
-		.set _SETDRV,	-0x46		# Drive to force
-		.set _FLAGS,	-0x45		# Flags
+		.set _NXTDRV,	B0_OFF+6	# Next drive
+		.set _OPT,	B0_OFF+7	# Default option
+		.set _SETDRV,	B0_OFF+8	# Drive to force
+		.set _FLAGS,	B0_OFF+9	# Flags
 		.set SETDRV,	0x20		# the 'setdrv' flag
 		.set NOUPDATE,	0x40		# the 'noupdate' flag
 		.set USEPACKET,	0x80		# the 'packet' flag
-		.set _TICKS,	-0x44		# Timeout ticks
-		.set _FAKE,0x0			# Fake partition table
+
+	/* ticks is at a fixed position */
+		.set _TICKS,	(PRT_OFF - 0x200 - 2)	# Timeout ticks
 		.set _MNUOPT, 0x10		# Saved menu entries
 
 		.set TLEN, (desc_ofs - bootable_ids)	# size of bootable ids
@@ -260,26 +267,16 @@ save_curdrive:	movb %dl, (%bp)			# Save 
 read_entry:	movb %ch,-0x4(%bx)		# Zero active flag (ch == 0)
 		btw %dx,_FLAGS(%bp)		# Entry enabled?
 		jnc next_entry			# No
-	/*
-	 * Lookup type in the 'non_bootable_ids' table, skip matching entries.
-	 * This is implemented is by setting %di to the start of the
-	 * exclude table, and %cl to the length of the table itself. After the
-	 * 'repne scasb' the zero flag is set if we found a match.
-	 * If not, %di points to the beginning of the 'valid' types,
-	 * which is what we need for the next check.
-	 */
 		movb (%bx),%al			# Load type
-		movw $non_bootable_ids,%di	# Lookup tables
-		movb $(bootable_ids-non_bootable_ids),%cl	# length
-		repne				# Exclude
-		scasb				#  partition?
-		je next_entry			# Yes, ignore it
+		test %al, %al			# skip empty partition
+		jz next_entry
 	/*
-	 * Now scan the table of bootable ids, which starts at %di and has
+	 * Scan the table of bootable ids, which starts at %di and has
 	 * length TLEN. On a match, %di points to the element following the
 	 * match; the corresponding offset to the description is $(TLEN-1)
 	 * bytes ahead. If we don't find a match, we hit the 'unknown' entry.
 	 */
+		movw $bootable_ids,%di		# Lookup tables
 		movb $(TLEN),%cl		# Number of entries
 		repne				# Locate
 		scasb				#  type
@@ -500,19 +497,10 @@ check_selection:
  *		the string at %si
  * putx:	print the option in %dl followed by the string at %di
  *		also record the drive as valid.
- * puts:	print the string at %si followed by a crlf
  * putn:	print a crlf
  * putstr:	print the string at %si
  * putchr:	print the char in al
  */
-putkey:
-#ifndef SIO
-		movb $'F',%al			# Display
-		callw putchr			#  'F'
-#endif
-		movb $'1',%al			# Prepare
-		addb %dl,%al			#  digit
-		jmp putstr.1			# Display the rest
 
 /*
  * Display the option and record the drive as valid in the options.
@@ -523,17 +511,24 @@ putx:		btsw %dx,_MNUOPT(%bp)		# Enable m
 		movw $item,%si			# Display
 		callw putkey			#  key
 		movw %di,%si			# Display the rest
-
-puts:		callw putstr			# Display string
+		callw putstr			# Display string
 
 putn:		movw $crlf,%si			# To next line
+		jmp putstr
 
+putkey:
+#ifndef SIO
+		movb $'F',%al			# Display
+		callw putchr			#  'F'
+#endif
+		movb $'1',%al			# Prepare
+		addb %dl,%al			#  digit
+
+putstr.1:	callw putchr			# Display char
 putstr:		lodsb				# Get byte
 		testb $0x80,%al 		# End of string?
-		jnz putstr.2			# Yes
-putstr.1:	callw putchr			# Display char
-		jmp putstr			# Continue
-putstr.2:	andb $~0x80,%al 		# Clear MSB
+		jz putstr.1			# No
+		andb $~0x80,%al 		# Clear MSB then print last 
 
 putchr:
 #ifndef SIO
@@ -601,38 +596,45 @@ crlf:		.ascii "\r";	     .byte '\n'|0x80
 
 /* Partition type tables */
 
-non_bootable_ids:
-	/*
-	 * These entries identify invalid or NON BOOT types and partitions.
-	 * 0: empty, 5: DOS3 ext. partition, 0xf: WIN95 ext partition
-	 */
-		.byte 0x0, 0x5, 0xf
 bootable_ids:
 	/*
-	 * These values indicate bootable types we know the names of.
-	 * 1: FAT12, 4: FAT16 <32M, 6: FAT16 > 32M, 7: NTFS
-	 * 0xb: FAT32, 0xc: FAT32-LBA, 0xe: FAT16-LBA, 
-	 * 0x83: linux, 0xa5: FreeBSD, 0xa6: netbsd, 0xa9:openbsd
-	 */
-		.byte 0x1, 0x6, 0x7, 0xb, 0xc, 0xe, 0x83
-		.byte 0xa5, 0xa6, 0xa9, 0x4
+	 * These values indicate bootable types we know about.
+	 * Corresponding descriptions are at desc_ofs:
+	 * Entries don't need to be sorted.
+	 */
+		.byte 0x1, 0x6, 0x7, 0xb, 0xc
+#ifndef SAVE_MEMORY
+		.byte 0xe
+#endif
+		.byte 0x83, 0xa5, 0xa6, 0xa9, 0x4
+#ifndef SAVE_MORE_MEMORY
+		.byte 0x5, 0xf
+#endif
+
 desc_ofs:
 	/*
 	 * Offsets that match the known types above, used to point to the
 	 * actual partition name. The last entry must point to os_misc,
 	 * which is used for non-matching names.
 	 */
-		.byte os_dos-.			# 1, DOS
-		.byte os_dos-.			# 6, DOS/WIN
-		.byte os_win-.			# 7, Windows
-		.byte os_win-.			# 11, Windows
-		.byte os_win-.			# 12, Windows
-		.byte os_win-.			# 14, Windows
+		.byte os_dos-.			#   1, FAT12 DOS
+		.byte os_dos-.			#   6, FAT16 <32M, DOS/WIN
+		.byte os_win-.			#   7, FAT16 >=32M Windows
+		.byte os_win-.			#  11, FAT32
+		.byte os_win-.			#  12, FAT32-LBA
+#ifndef SAVE_MEMORY
+		.byte os_win-.			#  14, FAT16-LBA
+#endif
 		.byte os_linux-.		# 131, Linux
 		.byte os_freebsd-.		# 165, FreeBSD
 		.byte os_bsd-.			# 166, OpenBSD
 		.byte os_bsd-.			# 169, NetBSD
-		.byte os_dos-.			# 4, DOS
+		.byte os_dos-.			#   4, FAT16 < 32M
+#ifndef SAVE_MORE_MEMORY
+		.byte os_ext-.			#   5, DOS Ext
+		.byte os_ext-.			#  15, DOS Ext-LBA
+#endif
+
 		.byte os_misc-. 		# Unknown
 
 	/*
@@ -641,15 +643,18 @@ desc_ofs:
 	 */
 os_misc:	.byte '?'|0x80
 os_dos:
-#if !defined(TEST)	/* DOS string only if room */
+#ifndef SAVE_MEMORY	/* DOS string only if room */
 		.ascii "DO";   .byte 'S'|0x80
 #endif
 os_win:		.ascii "WI";   .byte 'N'|0x80
 os_linux:	.ascii "Linu"; .byte 'x'|0x80
 os_freebsd:	.ascii "Free"
 os_bsd:		.ascii "BS";   .byte 'D'|0x80
+#ifndef SAVE_MORE_MEMORY
+os_ext:		.ascii "EX";   .byte 'T'|0x80
+#endif
 
-		.org B0_OFF,0x90
+		.org (0x200 + B0_OFF),0x90
 /*
  * The boot0 version 1.0 parameter table.
  * Do not move it nor change the "Drive " string, boot0cfg
@@ -663,6 +668,9 @@ nxtdrv:		.byte 0x0			# Next drive number
 opt:		.byte 0x0			# Option
 setdrv_num:	.byte 0x80			# Drive to force
 flags:		.byte FLAGS			# Flags
+#ifdef VOLUME_SERIAL
+		.byte 0xa8,0xa8,0xa8,0xa8       # Volume Serial Number
+#endif
 ticks:		.word TICKS			# Delay
 
 		.org PRT_OFF

Modified: head/usr.sbin/boot0cfg/boot0cfg.8
==============================================================================
--- head/usr.sbin/boot0cfg/boot0cfg.8	Wed Dec  3 11:48:20 2008	(r185578)
+++ head/usr.sbin/boot0cfg/boot0cfg.8	Wed Dec  3 14:53:59 2008	(r185579)
@@ -35,7 +35,9 @@
 .Op Fl Bv
 .Op Fl b Ar boot0
 .Op Fl d Ar drive
+.Op Fl e Ar bell character
 .Op Fl f Ar file
+.Op Fl i Ar volume-id
 .Op Fl m Ar mask
 .Op Fl o Ar options
 .Op Fl s Ar slice
@@ -96,10 +98,17 @@ which contains the specified
 Typically this will be 0x80 for the first hard drive, 0x81 for the
 second hard drive, and so on; however any integer between 0 and 0xff
 is acceptable here.
+.It Fl e Ar bell character
+Set the character to be printed in case of input error.
 .It Fl f Ar file
 Specify that a backup copy of the preexisting MBR should be written to
 .Ar file .
 This file is created if it does not exist, and replaced if it does.
+.It Fl i Ar volume-id
+Specifies a volume-id (in the form XXXX-XXXX) to be saved at location
+0x1b8 in the MBR. This information is sometimes used by NT, XP and Vista
+to identify the disk drive. The option is only compatible with version 2.00
+of the 512-byte boot block.
 .It Fl m Ar mask
 Specify slices to be enabled/disabled, where
 .Ar mask

Modified: head/usr.sbin/boot0cfg/boot0cfg.c
==============================================================================
--- head/usr.sbin/boot0cfg/boot0cfg.c	Wed Dec  3 11:48:20 2008	(r185578)
+++ head/usr.sbin/boot0cfg/boot0cfg.c	Wed Dec  3 14:53:59 2008	(r185579)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2008 Luigi Rizzo
  * Copyright (c) 1999 Robert Nordier
  * All rights reserved.
  *
@@ -44,13 +45,34 @@ __FBSDID("$FreeBSD$");
 
 #define MBRSIZE         512     /* master boot record size */
 
-#define OFF_VERSION	0x1b0	/* offset: version number */
-#define OFF_OPT		0x1b9	/* offset: default boot option */
-#define OFF_DRIVE	0x1ba	/* offset: setdrv drive */
-#define OFF_FLAGS       0x1bb   /* offset: option flags */
-#define OFF_TICKS       0x1bc   /* offset: clock ticks */
+#define OFF_VERSION	0x1b0	/* offset: version number, only boot0version */
+#define OFF_SERIAL	0x1b8	/* offset: volume serial number */
 #define OFF_PTBL        0x1be   /* offset: partition table */
 #define OFF_MAGIC       0x1fe   /* offset: magic number */
+/*
+ * Offsets to the parameters of the 512-byte boot block.
+ * For historical reasons they are set as macros
+ */
+struct opt_offsets {
+	int opt;
+	int drive;
+	int flags;
+	int ticks;
+};
+
+struct opt_offsets b0_ofs[] = {
+	{ 0x0, 0x0, 0x0, 0x0 },		/* no boot block */
+	{ 0x1b9, 0x1ba, 0x1bb, 0x1bc },	/* original block */
+	{ 0x1b5, 0x1b6, 0x1b7, 0x1bc },	/* NT_SERIAL block */
+};
+
+int b0_ver;		/* boot block version set by boot0bs */
+
+#define OFF_OPT		(b0_ofs[b0_ver].opt)	/* default boot option */
+#define OFF_DRIVE	(b0_ofs[b0_ver].drive)	/* setdrv drive */
+#define OFF_FLAGS       (b0_ofs[b0_ver].flags)	/* option flags */
+#define OFF_TICKS       (b0_ofs[b0_ver].ticks)	/* clock ticks */
+
 
 #define cv2(p)  ((p)[0] | (p)[1] << 010)
 
@@ -81,8 +103,12 @@ static int boot0version(const u_int8_t *
 static int boot0bs(const u_int8_t *);
 static void stropt(const char *, int *, int *);
 static int argtoi(const char *, int, int, int);
+static int set_bell(u_int8_t *, int, int);
 static void usage(void);
 
+unsigned vol_id[5];	/* 4 plus 1 for flag */
+
+int v_flag;
 /*
  * Boot manager installation/configuration utility.
  */
@@ -93,9 +119,9 @@ main(int argc, char *argv[])
     int boot0_size, mbr_size;
     const char *bpath, *fpath;
     char *disk;
-    int B_flag, v_flag, o_flag;
+    int B_flag, o_flag;
     int d_arg, m_arg, s_arg, t_arg;
-    int o_and, o_or;
+    int o_and, o_or, o_e = -1;
     int up, c;
 
     bpath = "/boot/boot0";
@@ -104,7 +130,7 @@ main(int argc, char *argv[])
     d_arg = m_arg = s_arg = t_arg = -1;
     o_and = 0xff;
     o_or = 0;
-    while ((c = getopt(argc, argv, "Bvb:d:f:m:o:s:t:")) != -1)
+    while ((c = getopt(argc, argv, "Bvb:d:e:f:i:m:o:s:t:")) != -1)
         switch (c) {
         case 'B':
             B_flag = 1;
@@ -118,9 +144,22 @@ main(int argc, char *argv[])
         case 'd':
             d_arg = argtoi(optarg, 0, 0xff, 'd');
             break;
+        case 'e':
+	    if (optarg[0] == '0' && optarg[1] == 'x')
+		sscanf(optarg, "0x%02x", &o_e);
+	    else
+		o_e = optarg[0];
+            break;
         case 'f':
             fpath = optarg;
             break;
+        case 'i':
+            if (sscanf(optarg, "%02x%02x-%02x%02x",
+		vol_id, vol_id+1, vol_id+2, vol_id+3) == 4)
+			vol_id[4] = 1;
+	    else
+		errx(1, "bad argument %s", optarg);
+            break;
         case 'm':
             m_arg = argtoi(optarg, 0, 0xf, 'm');
             break;
@@ -147,7 +186,10 @@ main(int argc, char *argv[])
     up = B_flag || d_arg != -1 || m_arg != -1 || o_flag || s_arg != -1
 	|| t_arg != -1;
 
-    /* open the disk and read in the existing mbr */
+    /* open the disk and read in the existing mbr. Either here or
+     * when reading the block from disk, we do check for the version
+     * and abort if a suitable block is not found.
+     */
     mbr_size = read_mbr(disk, &mbr, !B_flag);
 
     /* save the existing MBR if we are asked to do so */
@@ -164,6 +206,8 @@ main(int argc, char *argv[])
 	boot0_size = read_mbr(bpath, &boot0, 1);
         memcpy(boot0 + OFF_PTBL, mbr + OFF_PTBL,
 	    sizeof(struct dos_partition) * NDOSPART);
+	if (b0_ver == 2)	/* volume serial number support */
+	    memcpy(boot0 + OFF_SERIAL, mbr + OFF_SERIAL, 4);
     } else {
 	boot0 = mbr;
 	boot0_size = mbr_size;
@@ -191,6 +235,19 @@ main(int argc, char *argv[])
     if (t_arg != -1)
         mk2(boot0 + OFF_TICKS, t_arg);
 
+    /* set the bell char */
+    if (o_e != -1 && set_bell(boot0, o_e, 0) != -1)
+	up = 1;
+
+    if (vol_id[4]) {
+	if (b0_ver != 2)
+	    errx(1, "incompatible boot block, cannot set volume ID");
+	boot0[OFF_SERIAL] = vol_id[0];
+	boot0[OFF_SERIAL+1] = vol_id[1];
+	boot0[OFF_SERIAL+2] = vol_id[2];
+	boot0[OFF_SERIAL+3] = vol_id[3];
+	up = 1;	/* force update */
+    }
     /* write the MBR back to disk */
     if (up)
 	write_mbr(disk, 0, boot0, boot0_size);
@@ -208,6 +265,36 @@ main(int argc, char *argv[])
     return 0;
 }
 
+/* get or set the 'bell' character to be used in case of errors.
+ * Lookup for a certain code sequence, return -1 if not found.
+ */
+static int
+set_bell(u_int8_t *mbr, int new_bell, int report)
+{
+    /* lookup sequence: 0x100 means skip, 0x200 means done */
+    static unsigned seq[] =
+		{ 0xb0, 0x100, 0xe8, 0x100, 0x100, 0x30, 0xe4, 0x200 };
+    int ofs, i, c;
+    for (ofs = 0x60; ofs < 0x180; ofs++) { /* search range */
+	if (mbr[ofs] != seq[0])	/* search initial pattern */
+	    continue;
+	for (i=0;; i++) {
+	    if (seq[i] == 0x200) {	/* found */
+		c = mbr[ofs+1];
+		if (!report)
+		    mbr[ofs+1] = c = new_bell;
+		else
+		    printf("  bell=%c (0x%x)",
+			(c >= ' ' && c < 0x7f) ? c : ' ', c);
+		return c;
+	    }
+	    if (seq[i] != 0x100 && seq[i] != mbr[ofs+i])
+		break;
+	}
+    }
+    warn("bell not found");
+    return -1;
+}
 /*
  * Read in the MBR of the disk.  If it is boot0, then use the version to
  * read in all of it if necessary.  Use pointers to return a malloc'd
@@ -218,6 +305,7 @@ read_mbr(const char *disk, u_int8_t **mb
 {
     u_int8_t buf[MBRSIZE];
     int mbr_size, fd;
+    int ver;
     ssize_t n;
 
     if ((fd = open(disk, O_RDONLY)) == -1)
@@ -229,7 +317,7 @@ read_mbr(const char *disk, u_int8_t **mb
     if (cv2(buf + OFF_MAGIC) != 0xaa55)
         errx(1, "%s: bad magic", disk);
 
-    if (!boot0bs(buf)) {
+    if (! (ver = boot0bs(buf))) {
 	if (check_version)
 	    errx(1, "%s: unknown or incompatible boot code", disk);
     } else if (boot0version(buf) == 0x101) {
@@ -338,9 +426,11 @@ display_mbr(u_int8_t *mbr)
                 part[i].dp_size);
     printf("\n");
     version = boot0version(mbr);
-    printf("version=%d.%d  drive=0x%x  mask=0x%x  ticks=%u\noptions=",
+    printf("version=%d.%d  drive=0x%x  mask=0x%x  ticks=%u",
 	version >> 8, version & 0xff, mbr[OFF_DRIVE],
 	mbr[OFF_FLAGS] & 0xf, cv2(mbr + OFF_TICKS));
+    set_bell(mbr, 0, 1);
+    printf("\noptions=");
     for (i = 0; i < nopt; i++) {
 	if (i)
 	    printf(",");
@@ -349,6 +439,10 @@ display_mbr(u_int8_t *mbr)
 	printf("%s", opttbl[i].tok);
     }
     printf("\n");
+    if (b0_ver == 2)
+	printf("volume serial ID %02x%02x-%02x%02x\n",
+		mbr[OFF_SERIAL], mbr[OFF_SERIAL+1],
+		mbr[OFF_SERIAL+2], mbr[OFF_SERIAL+3]);
     printf("default_selection=F%d (", mbr[OFF_OPT] + 1);
     if (mbr[OFF_OPT] < 4)
 	printf("Slice %d", mbr[OFF_OPT] + 1);
@@ -364,16 +458,27 @@ display_mbr(u_int8_t *mbr)
 static int
 boot0version(const u_int8_t *bs)
 {
-    static u_int8_t idold[] = {0xfe, 0x45, 0xf2, 0xe9, 0x00, 0x8a};
-
     /* Check for old version, and return 0x100 if found. */
-    if (memcmp(bs + 0x1c, idold, sizeof(idold)) == 0)
-        return 0x100;
+    int v = boot0bs(bs);
+    if (v != 0)
+        return v << 8;
 
     /* We have a newer boot0, so extract the version number and return it. */
     return *(const int *)(bs + OFF_VERSION) & 0xffff;
 }
 
+/* descriptor of a pattern to match.
+ * Start from the first entry trying to match the chunk of bytes,
+ * if you hit an entry with len=0 terminate the search and report
+ * off as the version. Otherwise skip to the next block after len=0
+ * An entry with len=0, off=0 is the end marker.
+  */
+struct byte_pattern {
+    unsigned off;
+    unsigned len;
+    u_int8_t *key;
+};
+
 /*
  * Decide if we have valid boot0 boot code by looking for
  * characteristic byte sequences at fixed offsets.
@@ -381,23 +486,32 @@ boot0version(const u_int8_t *bs)
 static int
 boot0bs(const u_int8_t *bs)
 {
+    /* the initial code sequence */
     static u_int8_t id0[] = {0xfc, 0x31, 0xc0, 0x8e, 0xc0, 0x8e, 0xd8,
 			     0x8e, 0xd0, 0xbc, 0x00, 0x7c };
+    /* the drive id */
     static u_int8_t id1[] = {'D', 'r', 'i', 'v', 'e', ' '};
-    static struct {
-	unsigned off;
-	unsigned len;
-	u_int8_t *key;
-    } ident[2] = {
+    static struct byte_pattern patterns[] = {
         {0x0,   sizeof(id0), id0},
-        {0x1b2, sizeof(id1), id1}
+        {0x1b2, sizeof(id1), id1},
+        {1, 0, NULL},
+        {0x0,   sizeof(id0), id0},	/* version with NT support */
+        {0x1ae, sizeof(id1), id1},
+        {2, 0, NULL},
+        {0, 0, NULL},
     };
-    unsigned int i;
+    struct byte_pattern *p = patterns;
 
-    for (i = 0; i < sizeof(ident) / sizeof(ident[0]); i++)
-        if (memcmp(bs + ident[i].off, ident[i].key, ident[i].len))
-	    return 0;
-    return 1;
+    for (;  p->off || p->len; p++) {
+	if (p->len == 0)
+	    break;
+	if (!memcmp(bs + p->off, p->key, p->len))	/* match */
+	    continue;
+	while (p->len)	/* skip to next block */
+	    p++;
+    }
+    b0_ver = p->off;	/* XXX ugly side effect */
+    return p->off;
 }
 
 /*


More information about the svn-src-all mailing list