Subject: Re: port-i386/19192: cannot delete unused partition from boot selector list
To: None <netbsd-bugs@netbsd.org>
From: David Laight <david@l8s.co.uk>
List: netbsd-bugs
Date: 11/28/2002 16:05:49
> >Synopsis:       cannot delete unused partition from boot selector list

The fdisk.c from the following patch will fix it.
summary of changes:
- allow mbr_bootsel code to be loaded from a non-default location
- allow mbr_bootsel code to be updated without losing its config
- don't overwrite mbr_bootsel code with standard mbr code unless
  explicitily requested
- allow mbr_bootsel menu name to be removed from unused partition
- let -B be specified with other options
- make displayed timeout value match that typed in
- use a timeout of -1 to imply 'indefinite timeout' (needs new mbr_bootsel.S)
- unset BFL_EXTINT13 if it is no longer needed

The mbr_bootsel.S changes:
- fix timeouts of > 30 minutes
- 0xffff => indefinite timeout
- auto-determine whether to do CHS or LBA reads (based on sector #)
- less terse error messages
- redisplay menu after error
- pass sector number read through to next boot stage.
- disk read retries removed (I don't think they are necessary)

possible bug:
- bios call to 'reset disk system' shouldbe called in error path.

	David

Index: fdisk.c
===================================================================
RCS file: /cvsroot/basesrc/sbin/fdisk/fdisk.c,v
retrieving revision 1.57
diff -u -r1.57 fdisk.c
--- fdisk.c	2002/11/24 21:49:15	1.57
+++ fdisk.c	2002/11/28 15:47:06
@@ -386,8 +386,10 @@
 	if (sh_flag && (a_flag || i_flag || u_flag || f_flag || s_flag))
 		usage();
 
+#if 0
 	if (B_flag && (a_flag || i_flag || u_flag || f_flag || s_flag))
 		usage();
+#endif
 
 	if (partition == -1 && s_flag) {
 		warnx("-s flag requires a partition selected.");
@@ -633,14 +635,29 @@
 init_sector0(int start, int dopart)
 {
 	int i;
+	int copy_size =  sizeof(mboot.bootinst);
 
 #ifdef	DEFAULT_BOOTCODE
-	if (!bootsize)
+	if (!bootsize) {
+#ifdef __i386__
+	    if (B_flag)
+		bootsize = read_boot(DEFAULT_BOOTSELCODE, bootcode,
+		    sizeof bootcode);
+	    else
+#endif
 		bootsize = read_boot(DEFAULT_BOOTCODE, bootcode,
 		    sizeof bootcode);
+	}
 #endif
+#ifdef __i386__
+	if (((struct mbr_bootsel *)&mboot.bootinst[MBR_BOOTSELOFF])->magic ==
+								MBR_MAGIC 
+		&& ((struct mbr_bootsel *)&bootcode[MBR_BOOTSELOFF])->magic ==
+								 MBR_MAGIC)
+	    copy_size = MBR_BOOTSELOFF;
+#endif
 
-	memcpy(mboot.bootinst, bootcode, sizeof(mboot.bootinst));
+	memcpy(mboot.bootinst, bootcode, copy_size);
 	putshort(&mboot.signature, MBR_MAGIC);
 	
 	if (dopart)
@@ -744,36 +761,41 @@
 {
 	struct mbr_bootsel *mbs =
 	    (struct mbr_bootsel *)&mboot.bootinst[MBR_BOOTSELOFF];
-	int i, nused, firstpart = -1, item;
-	char desc[PARTNAMESIZE + 2], *p;
-	int timo, entry_changed = 0;
-
-	for (i = nused = 0; i < NMBRPART; ++i) {
-		if (mboot.parts[i].mbrp_typ != 0) {
-			if (firstpart == -1)
-				firstpart = i;
-			nused++;
-		}
-	}
+	int i, item;
+	char *name, *p;
+	int tmo;
+
+	for (item = 0; item < NMBRPART; item++)
+		if (mboot.parts[item].mbrp_typ != 0)
+			break;
 
-	if (nused == 0) {
+	if (item == NMBRPART) {
 		warnx("No used partitions found. Partition the disk first.");
 		return;
 	}
 
+	printf("\n");
+
 	if (mbs->magic != MBR_MAGIC) {
 		if (!yesno("Bootselector not yet installed. Install it now?")) {
 			warnx("Bootselector not installed.");
 			return;
 		}
-		bootsize = read_boot(DEFAULT_BOOTSELCODE, bootcode,
-		    sizeof bootcode);
+		/* read default bootselect code unless we already have one */
+		if (!bootsize || ((struct mbr_bootsel *)&bootcode
+				[MBR_BOOTSELOFF])->magic != MBR_MAGIC)
+			bootsize = read_boot(DEFAULT_BOOTSELCODE, bootcode,
+				sizeof bootcode);
 		memcpy(mboot.bootinst, bootcode, sizeof(mboot.bootinst));
 		bootsel_modified = 1;
 		mbs->flags |= BFL_SELACTIVE;
 	} else {
+		/* If bootfile was specified with -c, copy it in */
+		if (bootsize && ((struct mbr_bootsel *)&bootcode
+				[MBR_BOOTSELOFF])->magic == MBR_MAGIC)
+			memcpy(mboot.bootinst, bootcode, MBR_BOOTSELOFF);
 		if (mbs->flags & BFL_SELACTIVE) {
-			printf("The bootselector is installed and active.");
+			printf("The bootselector is installed and active.\n");
 			if (!yesno("Do you want to change its settings?")) {
 				if (yesno("Do you want to deactivate it?")) {
 					mbs->flags &= ~BFL_SELACTIVE;
@@ -793,71 +815,59 @@
 		}
 	}
 
-	printf("\n\nPartition table:\n");
-	for (i = 0; i < NMBRPART; i++) {
-		printf("%d: ", i);
-		print_part(i);
-	}
-
-	printf("\n\nCurrent boot selection menu option names:\n");
-	for (i = 0; i < NMBRPART; i++) {
-		if (mbs->nametab[i][0] != 0)
-			printf("%d: %s\n", i, &mbs->nametab[i][0]);
-		else
-			printf("%d: <UNUSED>\n", i);
-	}
-	printf("\n");
-
-	item = firstpart;
-
 editentries:
-	while (1) {
-		decimal("Change which entry (-1 quits)?", &item);
+	for (;; item++) {
+		if (item < 0 || item > NMBRPART)
+			item = -1;
+		printf("\nCurrent boot selection menu option names:\n");
+		for (i = 0; i < NMBRPART; i++) {
+			const char *n = mbs->nametab[i];
+			const char *t = get_type(mboot.parts[i].mbrp_typ);
+			if (*n == 0)
+				n = "<UNUSED>";
+			printf("partition %d: %-*s       (%s)\n",
+				i, PARTNAMESIZE, n, t);
+		}
+		printf("\n");
+
+		decimal("Change which partition (-1 quits)?", &item);
 		if (item == -1)
 			break;
 		if (item < 0 || item >= NMBRPART) {
-			printf("Invalid entry number\n");
-			item = -1;
-			continue;
-		}
-		if (mboot.parts[item].mbrp_typ == 0) {
-			printf("The partition entry is unused\n");
-			item = -1;
+			printf("Invalid partition number\n");
+			item = -2;
 			continue;
 		}
 
-		printf("Enter descriptions (max. 8 characters): ");
-		rewind(stdin);
-		if (!fgets(desc, PARTNAMESIZE + 1, stdin))
-			errx(1, "EOF");
-		fpurge(stdin);
-		p = strchr(desc, '\n');
-		if (p != NULL)
-			*p = 0;
-		strcpy(&mbs->nametab[item][0], desc);
-		entry_changed = bootsel_modified = 1;
+		name = mbs->nametab[item];
 
-		item++;
-	}
-
-	if (entry_changed)
-		printf("Boot selection menu option names are now:\n");
-
-	firstpart = -1;
-	for (i = 0; i < NMBRPART; i++) {
-		if (mbs->nametab[i][0] != 0) {
-			firstpart = i;
-			if (entry_changed)
-				printf("%d: %s\n", i, &mbs->nametab[i][0]);
+		if (mboot.parts[item].mbrp_typ == 0) {
+			if (*name == 0) {
+				printf("The partition entry is unused\n");
+				continue;
+			}
+			printf("Removing name from unused partition\n");
+			memset(name, 0, PARTNAMESIZE + 1);
 		} else {
-			if (entry_changed)
-				printf("%d: <UNUSED>\n", i);
+			printf("Enter description (max. %d characters): ",
+				PARTNAMESIZE);
+			rewind(stdin);
+			if (!fgets(name, PARTNAMESIZE + 1, stdin))
+				errx(1, "EOF");
+			fpurge(stdin);
+			p = strchr(name, '\n');
+			if (p != NULL)
+				memset(p, 0, name + PARTNAMESIZE + 1 - p);
 		}
+
+		bootsel_modified = 1;
 	}
-	if (entry_changed)
-		printf("\n");
+
+	for (i = 0; i < NMBRPART; i++)
+		if (mbs->nametab[i][0] != 0)
+			break;
 
-	if (firstpart == -1) {
+	if (i == NMBRPART) {
 		printf("All menu entries are now inactive.\n");
 		if (!yesno("Are you sure about this?"))
 			goto editentries;
@@ -873,11 +883,14 @@
 	bootsel_modified = 1;
 
 	/* The timeout value is in ticks, 18.2 Hz. Avoid using floats. */
-	timo = ((1000 * mbs->timeo) / 18200);
+	/* Ticks are probably 64k/3600 - so our long timers are sligtly
+	   out!  newer bootcode always waits for 1 tick, so treats 0xffff
+	   as wait forever. */
+	tmo = mbs->timeo == 0xffff ? -1 : (10 * mbs->timeo + 9) / 182;
 	do {
-		decimal("Timeout value", &timo);
-	} while (timo < 0 || timo > 3600);
-	mbs->timeo = (u_int16_t)((timo * 18200) / 1000);
+		decimal("Timeout value (0 to 3600 seconds, -1 => never)", &tmo);
+	} while (tmo < -1 || tmo > 3600);
+	mbs->timeo = tmo == -1 ? 0xffff : (tmo * 182) / 10;
 
 	printf("Select the default boot option. Options are:\n\n");
 	for (i = 0; i < NMBRPART; i++) {
@@ -907,6 +920,8 @@
 		mbs->defkey = SCAN_F1 + item;
 
 done:
+	/* scan active partitions to see if we need LBA reads */
+	mbs->flags &= ~BFL_EXTINT13;
 	for (i = 0; i < NMBRPART; i++) {
 		if (mboot.parts[i].mbrp_typ != 0 &&
 		   mboot.parts[i].mbrp_start >=
Index: mbr_bootsel/mbr_bootsel.S
===================================================================
RCS file: /cvsroot/basesrc/sbin/fdisk/mbr_bootsel/mbr_bootsel.S,v
retrieving revision 1.6
diff -u -r1.6 mbr_bootsel.S
--- mbr_bootsel/mbr_bootsel.S	2002/09/09 03:01:26	1.6
+++ mbr_bootsel/mbr_bootsel.S	2002/11/28 15:47:09
@@ -81,6 +81,13 @@
  * a 32-bit value (either data or addr) may be inserted in the generated
  * code, or where we would need a prefix but can do without (saving some
  * space).
+ *
+ * This code is optimised for space, bytes can still be extracted
+ * but it is getting harder!
+ * Writing it is hard work - you need to sort out which opcode
+ * bytes you want, then see whether gcc creates them or not.
+ * Having both the 286 and 386 programmers reference manuals helps!
+ * Fortunately there are only 450 bytes to play with!
  */
 
 /*
@@ -106,7 +113,8 @@
 
 #define SI_INDEX	4
 #define DI_INDEX	5
-#define BP_INDEX	7
+#define BP_INDEX	6	/* only with disp8/16 */
+#define BX_INDEX	7
 
 
 /*
@@ -134,19 +142,29 @@
 	.byte 0x8a ; .byte 0x40 | (reg << 3) | ireg ; .byte off
 #define movw_iregoff_reg(ireg,off,reg) \
 	.byte 0x8b ; .byte 0x40 | (reg << 3) | ireg ; .byte off
+#define movb_reg_iregoff(reg,ireg,off) \
+	.byte 0x88 ; .byte 0x40 | (reg << 3) | ireg ; .byte off
+#define movw_reg_iregoff(reg,ireg,off) \
+	.byte 0x89 ; .byte 0x40 | (reg << 3) | ireg ; .byte off
+
+#define mulb_ireg0(ireg) \
+	.byte 0xf6 ; .byte (4 << 3) | ireg
 
 #define movb_reg_mem(reg,mem) \
 	.byte 0x88 ; .byte 0x6 | (reg << 3) ; .word mem
 #define movb_mem_reg(mem,reg) \
 	.byte 0x8a ; .byte 0x6 | (reg << 3) ; .word mem
 
+#define movb_imm_mem(imm,mem) \
+	.byte 0xc6 ; .byte 0x06 ; .word mem ; .byte imm
+#define movw_imm_mem(imm,mem) \
+	.byte 0xc7 ; .byte 0x06 ; .word mem ; .word imm
+
 #define movl_imm_ireg0(imm,ireg) \
 	.byte 0x66 ; .byte 0xc7 ; .byte ireg ; .long imm
 #define movl_imm_iregoff(imm,ireg,off) \
 	.byte 0x66 ; .byte 0xc7 ; .byte 0x40 | ireg ; .byte off ; .long imm
 
-#define cmp_imm_ax(imm)	.byte 0x3d ; .word imm
-
 #define cmpb_imm_ireg0(imm,reg) \
 	.byte 0x80 ; .byte 0x38 + reg ; .byte imm
 
@@ -159,11 +177,19 @@
 #define cmpb_mem_reg(mem,reg) .byte 0x3a ; .byte 0x06 | (reg << 3) ; .word mem
 #define cmpw_imm_reg(imm,reg) .byte 0x81 ; .byte 0xf8 + reg ; .word imm
 
+#define cmpw_iregoff_reg(ireg,off,reg) \
+	.byte 0x3b ; .byte 0x40 | (reg << 3) | ireg ; .byte off
+
+#define testb_imm_mem(imm,mem) \
+	.byte 0xf6 ; .byte 0x06 ; .word mem ; .byte imm
+
+#define cmp_imm_ax(imm)	.byte 0x3d ; .word imm
 #define and_imm_ax(imm)	.byte 0x25 ; .word imm
 
 
 
 #define	BOOTADDR	0x7c00
+#define LOADADDR	0x0600		/* address were are linked to */
 
 /*
  * Each entry in the boot select table is a nul-terminated string
@@ -179,7 +205,8 @@
  * or inactive. There may never be more because of space constraints.
  */
 #define BFL_SELACTIVE	0x01
-#define BFL_EXTINT13	0x02
+#define BFL_EXTINT13	0x02	/* set by fdisk if LBA needed (deprecated) */
+#define BFL_READ_LBA	0x04	/* force LBA reads (in case geom mismatch) */
 
 /*
  * Scan values for the various keys we use, as returned by the BIOS
@@ -201,49 +228,51 @@
 #define ERR_INVPART	'1'		/* Invalid partition table */
 #define ERR_READ	'2'		/* Read error */
 #define ERR_NOOS	'3'		/* Magic no. check failed for part. */
+#define	ERR_KEY		'?'		/* unknown key press */
+#define	ERR_PTN		'p'		/* partition not defined */
 
 	.text
 /*
  * Move ourselves out of the way first.
+ * (to the address we are linked at - 0x600)
  */
 ENTRY(start)
-	data32
 	xorl	%eax, %eax
 	movl	%eax, %ss
-	movl	$BOOTADDR, %esp
+	movw_imm_reg(BOOTADDR, SP)
 	movl	%eax, %es
 	movl	%eax, %ds
-	xorl	%esi,%esi
 	movl	%esp, %esi
 	movw_imm_reg(_C_LABEL(start),DI)
 	movw_imm_reg(0x100,CX)
 	rep
 	movsl
-	jmpfar(0,1f)
+	jmpfar(0,1f)			/* leap into copy of code */
+
 /*
  * Sanity check the drive number passed by the BIOS. Some BIOSs may not
  * do this and pass garbage.
  */
 1:
 	cmpb	$MINDRV,%dl
-	jl	2f
+	jb	2f
 	cmpb	$MAXDRV,%dl
-	jle	3f
+	jbe	3f
 2:
-	movb $0x80,%dl
+	movb	$MINDRV,%dl		/* garbage in, boot disk 0 */
 3:
-	movb_reg_mem(DL,drvno)
+	push	%edx			/* save drive number */
 bootsel:
-	movb_mem_al(flags)
-	testb	$BFL_SELACTIVE,%al
-	jznear16(getactive)
+	testb_imm_mem( BFL_SELACTIVE, flags)
+	jz	getactive
+
 /*
  * The bootselector is active. Walk through the selector (name) table,
  * printing used entries.
  */
+bootsel_menu:
 	movw_imm_reg(nametab,DI)
 	movb	$0x31,%al
-	movb	$4,%cl
 1:
 	cmpb_imm_ireg0(0,DI_INDEX)
 	jz	2f
@@ -257,46 +286,42 @@
 2:
 	incb	%al
 	addl	$TABENTRYSIZE,%edi
-	loop	1b
+	cmpb	$0x35,%al
+	jne	1b
 
 /*
  * Get the initial time value for the timeout comparison. It is returned
- * by int 1a in cx:dx. Make sure to grab the whole 32 bit value, otherwise
- * it'd wrap around every hour. Now it'll only do so every 24 hours. Boots
- * should happen infrequent enough that this isn't a problem.
+ * by int 1a in cx:dx. We do sums modulo 2^16 so it doesn't matter if
+ * the counter wraps (which it does every hour) - so we can safely
+ * ignore 'cx'.
  *
  * Loop around checking for a keypress until we have one, or timeout is
  * reached.
  */
 	xorb	%ah,%ah
 	int	$0x1a
-	data32
-	shll	$16,%ecx
-	movl	%edx,%edi
-	data32
-	orl	%ecx,%edi
+	movl	%edx,%edi		/* start time to di */
 3:
-	movb	$1,%ah
-	int	$0x16
+	movb	$1,%ah			/* think this just looks to */
+	int	$0x16			/* see if a key has been pressed */
 	jnz	4f
 	xorb	%ah,%ah
-	int	$0x1a
-	data32
-	shll	$16,%ecx
-	data32
-	orl	%ecx,%edx
-	data32
+	int	$0x1a			/* current time to cx:dx */
 	subl	%edi,%edx
 	movw_mem_ax(timeout)
-	cmpl	%eax,%edx
-	jl	3b
-	movb_mem_al(defkey)
-	jmp	default
+	cmpl	%eax,%edx		/* always wait for 1 tick... */
+	jbe	3b			/* 0xffff means never timeout */
+	movb_mem_al(defkey)		/* timedout - pick default key */
+	jmp	check_key
 4:
 	xorb	%ah,%ah
-	int	$0x16
+	int	$0x16			/* 'read key', code ah, ascii al */
 	movb	%ah,%al
-default:
+
+/* We have a keycode, see what it means.
+   If we don't know we generate error '?' and go ask again
+*/
+check_key:
 /*
  * <enter> -> boot active partition.
  */
@@ -306,39 +331,28 @@
  * F1-F4 -> boot partition 1-4
  */
 	subb	$SCAN_F1,%al
-	cmpb	$9,%al
-	jg	4b
 	cmpb	$3,%al
-	jle	5f
+	jbe	check_good_ptn
+
 /*
  * F5-F10 -> boot disk 0-5. Check if the requested disk isn't above
  * the number of disks actually in the system as stored in 0:0475 by
- * the BIOS. This is always sector 0, so never use int13 extensions.
+ * the BIOS.
+ * If we trust loc 475, we needn't check the upper bound on the keystroke
+ * This is always sector 0, so always read using chs
  */
 	subb	$4,%al
 	cmpb_mem_reg(0x0475,AL)
-	jge	4b
-	movw_imm_reg(fakeent,SI)
+	jae	inv_key
+	movw_imm_reg(chs_zero,SI)	/* chs read sector zero info */
 	addb	$0x80,%al
-	movb	%al,%dl
-	jmp16(noext)
-5:
-/*
- * Check if the requested entry is actually active in the partition and
- * bootmenu table. If not, just do nothing.
- */
-	movw_imm_reg(parttab,SI)
-	and_imm_ax(0xff)
-	movl	%eax,%ebx
-	shll	$4,%eax
-	add	%eax,%esi
-	cmpb_imm_iregoff(0,SI_INDEX,4)
-	je	4b
-	movw_imm_reg(nametab,DI)
-	imul	$TABENTRYSIZE,%ebx
-	cmpb_imm_ireg0_bx(0,DI_INDEX)
-	je	4b
-	jmp	boot
+	pop	%edx			/* dump saved drive # */
+	push	%eax			/* replace with new */
+	jmp	read_chs
+
+inv_key:
+	movb	$ERR_KEY,%al
+	jmp	err_msg
 
 /*
  * Look for the (first) active partition
@@ -354,132 +368,238 @@
 /*
  * No active partition.
  */
-invpart:
 	movb	$ERR_INVPART,%al
-	jmp	errhang
+
+/* Something went wrong,
+   output single byte error code,
+   kill timeout - and hence require user interaction
+   redisplay menu and wait for user key
+*/
+err_msg:
+	movb_al_mem( errcod );
+	movw_imm_reg(errtxt,SI);
+	call16(putasciz)
+	movw_imm_mem( 0xffff, timeout );
+	jmp16( bootsel_menu )
 
 /*
+ * Check if the requested entry is actually active in the partition and
+ * bootmenu table. If not, just do nothing.
+ */
+check_good_ptn:
+	movw_imm_reg(parttab-0x10,SI)
+	movw_imm_reg(nametab-9,DI)
+1:	
+	addl	$0x10,%esi
+	addl	$9,%edi
+	subb	$1,%al
+	jnc	1b
+
+	movb_reg_iregoff(AL,SI_INDEX,4)
+	mulb_ireg0(DI_INDEX)
+	testl	%eax,%eax
+	movb	$ERR_PTN,%al			/* no partition */
+	je	err_msg
+
+/*
  * Active partition pointed to by si.
  * Read the first sector.
  *
- * First determine whether we have int13-extensions, by calling
- * int 13, function 41. Check for the magic number returned,
- * and the disk packet capability.
+ * We can either do a CHS (Cylinder Head Sector) or an LBA (Logical
+ * Block Address) read.  Always doing the LBA one
+ * would be nice - unfortunately not all systems support it.
+ * Also some may contain a separate (eg SCSI) bios that doesn't
+ * support it even when the main bios does.
+ *
+ * The safest thing seems to be to find out whether the sector we
+ * want is inside the CHS sector count.  If it is we use CHS, if
+ * outside we use LBA.
+ *
+ * There isn't much point checking if LBA support is present!  We
+ * only try to use it if it is necessary.
  */
 boot:
-	movb_mem_reg(drvno,DL)
-boot2:
+	data32
+	movw_iregoff_reg(SI_INDEX,8,BP)		/* get sector # */
+
+	testb_imm_mem( BFL_READ_LBA, flags )
+	jnz	read_lba			/* fdisk forced LBA read */
+/* get maximum (bios) sector */
+	pop	%edx				/* collect saved drive... */
+	push	%edx				/* ...number to dl */
+	movb	$8,%ah
+	int	$0x13				/* chs info */
+
+/* We now have to extract the chs values, increment c and h, and
+ * multiply together - getting a 32 bit result (top 8 bits are 0).
+ * heads = dh + 1
+ * sector = cl & 0x3f
+ * cylinder = ((cl & 0xc0) << 2 | ch) + 1
+ * Note that if dl was 0xff (unusual cos some systems have the 'bug'
+ * I'm writing!) then we get 0, but 256 heads isn't a real geometry
+ * so LBA will almost certainly work!
+ */
+	incb	%dh				/* now 1..255 (or zero) */
+	movb	%cl,%al
+	data32					/* we need eax[31:16] = 0 */
+	andl    $0x3f,%eax			/* ax now sectors 1..63 */
+	mulb	%dh				/* ax = heads * sectors */
+	data32
+	xchg	%eax,%ecx			/* 32bit count in ecx */
+	shrb	$6,%al
+	xchgb	%al,%ah				/* 10 bit cyl in ax */
+	incl	%eax				/* cylinders 1..1024 */
+	data32
+	cwde					/* extend ax into eax */
+	data32
+	mull	%ecx				/* sectors in eax */
+
+	data32
+	cmpl	%ebp,%eax			/* sector in CHS limit ? */
+	ja	read_chs
+
+/* leave this code - it is slightly shorter than the above,
+   someday someone might want to re-instate it! */
+#if 0
 	movb_mem_al(flags)
-	testb	$BFL_EXTINT13,%al
-	jz	noext
+	testb	$BFL_EXTINT13,%al		/* fdisk enable LBA? */
+	jz	read_chs
 
-	push	%esi
-	push	%edx
+/* First determine whether we have int13-extensions, by calling
+ * int 13, function 41. Check for the magic number returned,
+ * and the disk packet capability.
+ */
 	movw_imm_reg(0x55aa,BX)
 	movb	$0x41, %ah
 	int	$0x13
-	pop	%edx
-	pop	%esi
-	jc	noext
+	jc	read_chs			/* no int13 extensions */
 	cmpw_imm_reg(0xaa55,BX)
-	jnz	noext
+	jnz	read_chs
 	testb	$1, %cl
-	jz	noext
+	jz	read_chs			/* no LBA read */
+#endif
 
-/*
- * Modify the partition table entry to look like an int13-extension
- * parameter block, so we can feed it to the extended read call.
- * XXX this means that we can only use it once, we can't jump back
- * here if the read fails.
- */
-	movl_imm_ireg0(0x10010,SI_INDEX)
-	movl_imm_iregoff(BOOTADDR,SI_INDEX,4)
-	movl_imm_iregoff(0,SI_INDEX,12)
-	movb	$5, %dh
-1:
-	push	%esi
-	push	%edx
+/* Get sector number from partition table and copy into preset
+ * int13-extension parameter block for LBA disk read.
+ */
+read_lba:
+	movw_imm_reg(lba_info,SI)
+	data32
+	movw_reg_iregoff(BP,SI_INDEX,8)		/* sector # to ctl block */
 	movb	$0x42, %ah
+	pop	%edx				/* recover drive # */
+	jmp	do_int13
+
+err_msg1:
+	jmp	err_msg			/* too far for jumps below... */
+
+/*
+ * Sector below CHS limit
+ * Do a cylinder-head-sector read instead
+ */
+read_chs:
+	pop	%edx				/* recover drive # */
+	movb_iregoff_reg(SI_INDEX,1,DH)		/* head */
+	movw_iregoff_reg(SI_INDEX,2,CX)		/* ch=cyl, cl=sect */
+	movw_imm_reg(BOOTADDR,BX)		/* es:bx is buffer */
+	movw_imm_reg(0x201,AX)			/* command 2, 1 sector */
+do_int13:
+	push	%edx				/* save drive */
 	int	$0x13
-	pop	%edx
-	pop	%esi
-	jnc	ok
-	dec	%dh
-	jnz	1b
-rderr:
+
 	movb	$ERR_READ,%al
-errhang:
-	call16(putc)
-hang:
-	sti
-	jmp 	hang
+	jc	err_msg1
+
 /*
+ * Check signature for valid bootcode
+ */
+mbr_read_ok:
+	movw_mem_ax(BOOTADDR+0x1fe)
+	cmp_imm_ax(0xaa55)
+	movb	$ERR_NOOS,%al
+	jnz	err_msg1
+
+/* Just for fun we pass the sector number through to the next stage boot.
+ * It doesn't have to use it (indeed no other mbr code will generate) it,
+ * but it does let us have a netBSD pbr that can identify where it was
+ * read from!  This lets us use this code to select between two
+ * NetBSD system on the same physical driver.
+ * (If we've read the mbr of a different disk, it gets a random number
+ * - but it wasn't expecting anything...)
+*/
+	data32
+	movl	%ebp,%esi
+	pop	%edx				/* recover drive # */
+	jmp16(_C_LABEL(start) - LOADADDR + BOOTADDR)  /* CS is zero */
+
+/*
  * print a 0-terminated ASCII string. Address in si.
  */
 putasciz:
-	push	%eax
+	pusha
+putasciz_1:
 1:
 	lodsb
 	testb	%al, %al
 	jz	2f
-	call16(putc)
-	jmp	1b
-2:
-	pop	%eax
-	ret
-putc:
+
 	movb	$0xe, %ah
 	movb	$7, %bl
 	int	$0x10
-	ret
 
-/*
- * No int13-extensions available, try old the method to load the frirst
- * sector of the partition.
- */
-noext:
-	movb_iregoff_reg(SI_INDEX,1,DH)
-	movw_iregoff_reg(SI_INDEX,2,CX)
-load:
-	movw_imm_reg(5,DI)
-1:
-	movw_imm_reg(BOOTADDR,BX)
-	movw_imm_reg(0x201,AX)
-	push	%edi
-	push	%edx
-	int	$0x13
-	pop	%edx
-	pop	%edi
-	jnc	ok
-	dec	%edi
-	jnz	1b
-	jmp	rderr
+	jmp	1b
+2:
+	popa
+	ret
 
-/*
- * Check signature for valid bootcode
- */
-ok:
-	movw_mem_ax(BOOTADDR+0x1fe)
-	cmp_imm_ax(0xaa55)
-	jz	1f
-	movb	$ERR_NOOS,%al
-	jmp	errhang
-1:
-	jmpfar(0,BOOTADDR)
+#if 0
+/* This is useful for debugging - although you probably need to
+ * delete some code to fit it in!
+ * Doesn't really matter where SI points!
+ */
+dump_eax:
+	pusha				/* saves bottom 16 bits only! */
+	movw_imm_reg(_C_LABEL(start),SI)
+	movl	%esi,%edi
+	movw_imm_reg(8,CX);
+1:	data32
+	roll	$4,%eax
+	movl	%eax,%ebx
+	andb	$0x0f,%al
+	addb	$0x30,%al		/* 30..3f - clear AF */
+#if 1 /* 5 bytes to generate real hex... */
+	daa				/* 30..39,40..45 */
+	addb	$0xc0,%al		/* f0..f9,00..05 */
+	adcb	$0x40,%al		/* 30..39,41..45 */
+#endif
+	stosb
+	movl	%ebx,%eax
+	loop	1b
+	movw_imm_reg(0x20,AX)		/* space + null */
+	stosl				/* really stosw... */
+	jmp16(putasciz_1)		/* save 2 bytes! */
+#endif
 
+errtxt: .ascii	"Error "	/* runs into newline... */
+errcod: .ascii	"x"
 newline:
 	.asciz	"\r\n"
 prefix:
 	.asciz	"F1: "
-/*
- * Fake partition entry used to boot from other disks. First byte is
- * overloaded, it's also used as storage for the drive number. We're
- * not using that in the entry.
- */
-drvno:
-fakeent:
-	.byte 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00
-	.long 0x0000, 0x0001
+
+/* Control block for int-13 LBA read.
+   We need a xx,00,01,00 somewhere to load chs for sector zero,
+   by a complete fluke there is one here! */
+chs_zero:
+lba_info:
+	.long	0x10010
+	.word	BOOTADDR	/* offset in segment */
+	.word	0		/* segment */
+	.long	0x0000		/* sector # goes here... */
+	.long	0x0000
+
+/* Stuff from here on is overwritten by fdisk - the offset must
+   not change... */
 	. = _C_LABEL(start) + (0x1bc - NAMETABSIZE - 4)
 /*
  * Default action, as a keyvalue we'd normally read from the BIOS. This
@@ -491,36 +611,23 @@
 	.byte	0x01
 /*
  * Timeout value. 65536 ticks per hour, which is about 18.2 times per second.
+ * 0xffff means never timeout.
  */
 timeout:
 	.word	0x00b6
 /*
- * Space for name/select table and partition table. If DEBUG is defined,
- * use some values from one of my machines, for testing.
+ * Space for name/select table and partition table.
  */
 nametab:
-#ifdef DEBUG
-	.asciz	"Windows "
-	.asciz	"NetBSD  "
-	.byte	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-	.byte	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-#else
 	.fill	NAMETABSIZE,0x01,0x00
-#endif
+
 	. = _C_LABEL(start) + 0x1bc
 ourmagic:
 	.byte	0x55, 0xaa
+
 	. = _C_LABEL(start) + 0x1be
-#ifdef DEBUG
 parttab:
-	.word 0x0100
-	.word 0x0001, 0xde06, 0x1f3e, 0x003f, 0x0000, 0xd000, 0x0007, 0xde80
-	.word 0x1f3f, 0x24a5, 0xe57e, 0xd03f, 0x0007, 0x1c80, 0x006f, 0x0000
-	.word 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
-	.word 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
-#else
-parttab:
 	.fill	0x40,0x01,0x00
-#endif
+
 	. = _C_LABEL(start) + 0x1fe
 	.byte	0x55, 0xaa
-- 
David Laight: david@l8s.co.uk