Subject: port-i386/7950: i386 port lacks full dos mbr support
To: None <gnats-bugs@gnats.netbsd.org>
From: Kip Rugger <kbr@pangea.ca>
List: netbsd-bugs
Date: 07/09/1999 15:52:07
>Number:         7950
>Category:       port-i386
>Synopsis:       i386 port lacks full dos mbr support
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    port-i386-maintainer (NetBSD/i386 Portmaster)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Fri Jul  9 15:50:01 1999
>Last-Modified:
>Originator:     Kip Rugger
>Organization:
	
>Release:        <NetBSD-current source date> NetBSD-1.4
>Environment:
	
System: NetBSD darkstar 1.4 NetBSD 1.4 (RUGGER) #17: Fri Jul 9 16:51:48 CDT 1999 kbr@darkstar:/usr/src/sys/arch/i386/compile/RUGGER i386


>Description:
	
	Installing NetBSD on a multi-boot host can be challenging.
	This is largely because the kernel cannot handle any but
	the simplest disk partitioning, and a potential user is
	forced to handcraft a disklabel before the install can
	even get started.

	The attached kernel patch purports to do the following:

	    o  recognize dos extended partitions of several varieties;

	    o  recognize dos "boot managers" and accomodate the changes
	       they make to the disk format;

	    o  allow the use a a LINUX swap partition for NetBSD swap;

	    o  build a suitable disk label for disks that lack one.

>How-To-Repeat:
	
>Fix:
	

--- /usr/src/sys/arch/i386/i386/disksubr.c.dist14	Fri May 21 23:04:14 1999
+++ /usr/src/sys/arch/i386/i386/disksubr.c	Fri Jul  9 16:50:35 1999
@@ -45,68 +45,265 @@
 
 #define	b_cylin	b_resid
 
-int fat_types[] = { MBR_PTYPE_FAT12, MBR_PTYPE_FAT16S,
-		    MBR_PTYPE_FAT16B, MBR_PTYPE_FAT32,
-		    MBR_PTYPE_FAT32L, MBR_PTYPE_FAT16L,
-		    -1 };
+static char * read_mbr __P((dev_t, void (*strat)(struct buf *),
+	struct disklabel *, struct mbr_partition *, int *, int));
 
-#define NO_MBR_SIGNATURE ((struct mbr_partition *) -1)
+/*
+**	attempt to convert the dos partition mess into a default disklabel.
+**
+**	- handle extended partitions (DOS, WIN95, LINUX)
+**	- handle boot managers (On Track, EZ Disk)
+**	- allow Linux swap partition for our swap
+*/
+static char *					/* OUT: err msg or NULL */
+read_mbr(dev, strat, lp, dp, bsd_label_part, build)
+	dev_t dev;				/* IN:  device */
+	void (*strat) __P((struct buf *));	/* IN:  strategy routine */
+	struct disklabel *lp;			/* INOUT:  disklabel */
+	struct mbr_partition *dp;		/* OUT:  dos mbr */
+	int * bsd_label_part;			/* OUT:  part'n for disklabel */
+	int build;				/* IN:   actually build label */
+{
+	char *	msg = 0;
+	enum { FIFO = 4 };
+	int	start[FIFO], size[FIFO], top, npart, loop;
+	enum	{ P_MBR, P_EXT, P_SWP };
+	char	ptype[FIFO];
+	struct buf *bp;
 
-static struct mbr_partition *
-mbr_findslice __P((struct mbr_partition* dp, struct buf *bp));
+	/* start, size, ptype, top form a fifo for blocks we examine */
+	start[0] = MBR_BBSECTOR;
+	size[0] = lp->d_secperunit;
+	ptype[0] = P_MBR;
+	top = 1;
 
+	npart = RAW_PART + 1;		/* number of parts in disklabel */
 
-/* 
- * Scan MBR for  NetBSD partittion.  Return NO_MBR_SIGNATURE if no MBR found
- * Otherwise, copy valid MBR partition-table into dp, and if a NetBSD
- * partition is found, return a pointer to it; else return  NULL.
- */
-static
-struct mbr_partition *
-mbr_findslice(dp, bp)
-	struct mbr_partition *dp;
-	struct buf *bp;
-{
-	struct mbr_partition *ourdp = NULL;
-	u_int16_t *mbrmagicp;
-	int i;
+	bp = geteblk(lp->d_secsize);
+	bp->b_dev = dev;
 
-	/* Note: Magic number is little-endian. */
-	mbrmagicp = (u_int16_t *)(bp->b_data + MBR_MAGICOFF);
-	if (*mbrmagicp != MBR_MAGIC)
-		return (NO_MBR_SIGNATURE);
-
-	/* XXX how do we check veracity/bounds of this? */
-	memcpy(dp, bp->b_data + MBR_PARTOFF, NMBRPART * sizeof(*dp));
-
-	/* look for NetBSD partition */
-	for (i = 0; i < NMBRPART; i++) {
-		if (dp[i].mbrp_typ == MBR_PTYPE_NETBSD) {
-			ourdp = &dp[i];
-			break;
+	loop = 10;			/* stop runaway execution */
+	while (top > 0 && --loop >= 0) {
+		struct mbr_partition * mbrp;
+		int	i, bsdpart;
+
+		/* read next block in fifo */
+read_mbr:
+		bp->b_blkno = start[0];
+read_ezd_mbr:
+		bp->b_cylin = bp->b_blkno/lp->d_secpercyl;
+		bp->b_bcount = lp->d_secsize;
+		bp->b_flags = B_BUSY | B_READ;
+		(*strat)(bp);
+		if (biowait(bp)) {
+			msg = "dos partition I/O error";
+			goto done;
+		}
+
+		/* here to check if partition is really a LINUX swap */
+		if (ptype[0] == P_SWP) {
+			char * sig = bp->b_data + lp->d_secsize - 10;
+			if (build && !memcmp(sig, "SWAP", 4)) {
+				/* OK for swap; don't use first page */
+				lp->d_partitions[1].p_offset +=8;
+				lp->d_partitions[1].p_size -=8;
+				lp->d_partitions[1].p_fstype = FS_SWAP;
+			}
+			goto pop_stack;
 		}
-	}
 
+		/* check dos partition table signature */
+		mbrp = (struct mbr_partition *) (bp->b_data + MBR_PARTOFF);
+		if (*(short *)(bp->b_data + MBR_MAGICOFF) != (short)MBR_MAGIC)
+			goto pop_stack;
+
+		/* scan non-extended partition table for a BSD partition.
+		** if more than one, use last one for fs root.   */
+		bsdpart = -1;
+		if (ptype[0] == P_MBR) {
+			if (dp)
+				memcpy(dp, mbrp, NMBRPART * sizeof(*mbrp));
+			for (i = 0; i < NMBRPART; i++)
+				if (mbrp[i].mbrp_typ == MBR_PTYPE_NETBSD)
+					bsdpart = i;
 #ifdef COMPAT_386BSD_MBRPART
-	/* didn't find it -- look for 386BSD partition */
-	if (!ourdp) {
+			if (bsdpart == -1)
+				for (i = 0; i < NMBRPART; i++)
+					if (mbrp[i].mbrp_typ==MBR_PTYPE_386BSD)
+						bsdpart = i;
+#endif
+		}
+
+		/* scan entries in a partition table */
 		for (i = 0; i < NMBRPART; i++) {
-			if (dp[i].mbrp_typ == MBR_PTYPE_386BSD) {
-				printf("WARNING: old BSD partition ID!\n");
-				ourdp = &dp[i];
- 				/*
-				 * If more than one matches, take last,
-				 * as NetBSD install tool does.
-				 */
+			struct mbr_partition * m = &mbrp[i];
+			struct partition * p;
+			int	j;
+
+			/* skip unused partitions */
+			if (m->mbrp_typ == 0 || m->mbrp_size == 0)
+				continue;
+
+			/* skip junk; legal in extended parts !!! */
+			if (m->mbrp_start + m->mbrp_size < m->mbrp_start ||
+			    m->mbrp_start + m->mbrp_size > size[0])
+				continue;
+
+			/* check for overlap with other partitions */
+			for (j = 0; j < npart; j++) {
+				u_int32_t	s, e;
+
+				p = &lp->d_partitions[j];
+				if (p->p_size == 0) continue;
+				if (j == 'c' - 'a' || j == 'd' - 'a') continue;
+
+				s = m->mbrp_start + start[0];
+				e = s + m->mbrp_size;
+
+				if ((p->p_offset <= s &&
+				    s < p->p_offset + p->p_size) ||
+				    (p->p_offset < e &&
+				    e < p->p_offset + p->p_size)) break;
+			}
+			if (j != npart) continue;
+
+			/* find spot in disklabel for this partition, if pos */
+			p = 0;
+			if (build) {
+				if (i == bsdpart) {
+					p = &lp->d_partitions[0];
+					p->p_fstype = FS_BSDFFS;
+				} else if (npart < MAXPARTITIONS) {
+					p = &lp->d_partitions[npart];
+					p->p_fstype = FS_UNUSED;
+				}
+			}
+
+			/* set extent in disklabel */
+			if (p) {
+				p->p_offset = m->mbrp_start + start[0];
+				p->p_size = m->mbrp_size;
+			}
+
+			switch (m->mbrp_typ) {
+
+			/* On Track boot manager gobbles 1st 63 sectors
+			** for itself and treats rest of disk like an
+			** extended partition.      */
+			case MBR_PTYPE_DM6:
+				start[0] = 63;
+				size[0] -= 63;
+				ptype[0] = P_MBR;
+				goto read_mbr;
+
+			/* EZ disk keeps real mbr in trk 0 sector 2 */
+			case MBR_PTYPE_EZD:
+				bp->b_blkno = 1;
+				goto read_ezd_mbr;
+
+			/* push extended partitions onto our fifo */
+			case MBR_PTYPE_EXT:
+			case MBR_PTYPE_EXT_LBA:
+			case MBR_PTYPE_LNX_EXT:
+				if (top == FIFO) continue;
+				ptype[top] = P_EXT;
+				start[top] = m->mbrp_start + start[0];
+				size[top++] = m->mbrp_size;
+				continue;
+
+			case MBR_PTYPE_NETBSD:
+			case MBR_PTYPE_386BSD:
+				if (p) {
+					p->p_fstype = FS_BSDFFS;
+					p->p_fsize = 1024;	/* defaults */
+					p->p_frag = 8;
+					p->p_cpg = 16;
+					if (p != lp->d_partitions) npart++;
+					else if (bsd_label_part)
+						*bsd_label_part = p->p_offset;
+				}
+				continue;
+
+			case MBR_PTYPE_FAT12:		/* DOS &c */
+			case MBR_PTYPE_FAT16S:
+			case MBR_PTYPE_FAT16B:
+			case MBR_PTYPE_FAT16L:
+			case MBR_PTYPE_FAT32:
+			case MBR_PTYPE_FAT32L:
+				if (p) p->p_fstype = FS_MSDOS, npart++;
+				continue;
+
+			case MBR_PTYPE_LNXEXT2:		/* LINUX */
+				if (p) p->p_fstype = FS_EX2FS, npart++;
+				continue;
+
 #if 0
-				break;
+			case MBR_PTYPE_NTFS:		/* NT */
+				if (p) p->p_fstype = FS_NTFS, npart++;
 #endif
+
+			/* Linux swap or Solaris --
+			** mark as unused for now (for safety), but check
+			** for the Linux swap signature at the end of the
+			** first 4K page         */
+			case MBR_PTYPE_LNXSWAP:
+				if (!lp->d_partitions[1].p_size) {
+					p = &lp->d_partitions[1];
+					p->p_offset = m->mbrp_start + start[0];
+					p->p_size = m->mbrp_size;
+					p->p_fstype = FS_UNUSED;
+					if (p->p_size > 8 && top != FIFO) {
+						start[top] = p->p_offset +
+						    (4096/lp->d_secsize - 1);
+						size[top] = 1;
+						ptype[top++] = P_SWP;
+					}
+					continue;
+				}
+				/*FALLTHROUGH*/
+			default:
+				if (p) p->p_fstype = FS_UNUSED, npart++;
+				continue;
 			}
+			break;
+		}
+pop_stack:
+		/* pop top of our fifo */
+		for (i = 1; i < top; i++) {
+			start[i-1] = start[i];
+			size[i-1] = size[i];
+			ptype[i-1] = ptype[i];
 		}
+		top--;
 	}
-#endif	/* COMPAT_386BSD_MBRPART */
 
-		return (ourdp);
+	/* misc housekeeping to finish the label we're building */
+	if (build) {
+		lp->d_partitions[2].p_offset = lp->d_partitions[0].p_offset;
+		lp->d_partitions[2].p_size = lp->d_partitions[0].p_size;
+		lp->d_partitions[2].p_fstype = FS_UNUSED;
+		lp->d_npartitions = npart;
+		lp->d_bbsize = 8192;		/* defaults */
+		lp->d_sbsize = 8192;
+		lp->d_checksum = 0;		/* renew checksum */
+		lp->d_checksum = dkcksum(lp);
+	}
+
+#ifdef DEBUG
+	{
+		int i;
+		for (i = 0; i < MAXPARTITIONS; i++) {
+			struct partition * p = &lp->d_partitions[i];
+			printf("part %c %d/%d/%d\n",
+			    i+'a', p->p_fstype, p->p_offset, p->p_size);
+		}
+	}
+#endif /* DEBUG */
+
+done:
+	bp->b_flags |= B_INVAL;
+	brelse(bp);
+	return msg;
 }
 
 
@@ -133,16 +330,17 @@
 	struct cpu_disklabel *osdep;
 {
 	struct mbr_partition *dp;
-	struct partition *pp;
 	struct dkbad *bdp;
 	struct buf *bp;
 	struct disklabel *dlp;
 	char *msg = NULL;
-	int dospartoff, cyl, i, *ip;
+	int dospartoff, i;
 
 	/* minimal requirements for archtypal disk label */
 	if (lp->d_secsize == 0)
 		lp->d_secsize = DEV_BSIZE;
+	if (lp->d_secpercyl == 0)
+		lp->d_secpercyl = 0x1fffffff;
 	if (lp->d_secperunit == 0)
 		lp->d_secperunit = 0x1fffffff;
 #if 0
@@ -169,79 +367,14 @@
 
 	/* do dos partitions in the process of getting disklabel? */
 	dospartoff = 0;
-	cyl = LABELSECTOR / lp->d_secpercyl;
-	if (!osdep)
-		goto nombrpart;
-	dp = osdep->dosparts;
-
-	/* read master boot record */
-	bp->b_blkno = MBR_BBSECTOR;
-	bp->b_bcount = lp->d_secsize;
-	bp->b_flags = B_BUSY | B_READ;
-	bp->b_cylin = MBR_BBSECTOR / lp->d_secpercyl;
-	(*strat)(bp);
-
-	/* if successful, wander through dos partition table */
-	if (biowait(bp)) {
-		msg = "dos partition I/O error";
-		goto done;
-	} else {
-		struct mbr_partition *ourdp = NULL;
-
-		ourdp = mbr_findslice(dp, bp);
-		if (ourdp ==  NO_MBR_SIGNATURE)
-			goto nombrpart;
-
-		for (i = 0; i < NMBRPART; i++, dp++) {
-			/* Install in partition e, f, g, or h. */
-			pp = &lp->d_partitions[RAW_PART + 1 + i];
-			pp->p_offset = dp->mbrp_start;
-			pp->p_size = dp->mbrp_size;
-			for (ip = fat_types; *ip != -1; ip++) {
-				if (dp->mbrp_typ == *ip)
-					pp->p_fstype = FS_MSDOS;
-			}
-			if (dp->mbrp_typ == MBR_PTYPE_LNXEXT2)
-				pp->p_fstype = FS_EX2FS;
-
-			/* is this ours? */
-			if (dp == ourdp) {
-				/* need sector address for SCSI/IDE,
-				 cylinder for ESDI/ST506/RLL */
-				dospartoff = dp->mbrp_start;
-				cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect);
-
-				/* update disklabel with details */
-				lp->d_partitions[2].p_size =
-				    dp->mbrp_size;
-				lp->d_partitions[2].p_offset = 
-				    dp->mbrp_start;
-#if 0
-				if (lp->d_ntracks != dp->mbrp_ehd + 1 ||
-				    lp->d_nsectors != DPSECT(dp->mbrp_esect)) {
-					printf("disklabel: BIOS sees chs %d/%d/%d as ",
-						lp->d_ncylinders, lp->d_ntracks,
-						lp->d_nsectors);
-					lp->d_ntracks = dp->mbrp_ehd + 1;
-					lp->d_nsectors = DPSECT(dp->mbrp_esect);
-					lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
-					lp->d_ncylinders = lp->d_secperunit / lp->d_secpercyl;
-					if (! lp->d_ncylinders)
-						lp->d_ncylinders = 1;
-					printf("%d/%d/%d\n",
-						lp->d_ncylinders, lp->d_ntracks,
-						lp->d_nsectors);
-				    }
-#endif
-			}
-		}
-		lp->d_npartitions = RAW_PART + 1 + i;
+	if (osdep) {
+		dp = osdep->dosparts;
+		msg = read_mbr(dev, strat, lp, dp, &dospartoff, 1);
 	}
 
-nombrpart:
 	/* next, dig out disk label */
 	bp->b_blkno = dospartoff + LABELSECTOR;
-	bp->b_cylin = cyl;
+	bp->b_cylin = bp->b_blkno/lp->d_secpercyl;
 	bp->b_bcount = lp->d_secsize;
 	bp->b_flags = B_BUSY | B_READ;
 	(*strat)(bp);
@@ -382,7 +515,7 @@
 	struct mbr_partition *dp;
 	struct buf *bp;
 	struct disklabel *dlp;
-	int error, dospartoff, cyl;
+	int error, dospartoff;
 
 	/* get a buffer and initialize it */
 	bp = geteblk((int)lp->d_secsize);
@@ -390,34 +523,11 @@
 
 	/* do dos partitions in the process of getting disklabel? */
 	dospartoff = 0;
-	cyl = LABELSECTOR / lp->d_secpercyl;
-	if (!osdep)
-		goto nombrpart;
-	dp = osdep->dosparts;
-
-	/* read master boot record */
-	bp->b_blkno = MBR_BBSECTOR;
-	bp->b_bcount = lp->d_secsize;
-	bp->b_flags = B_BUSY | B_READ;
-	bp->b_cylin = MBR_BBSECTOR / lp->d_secpercyl;
-	(*strat)(bp);
-
-	if ((error = biowait(bp)) == 0) {
-		struct mbr_partition *ourdp = NULL;
-
-		ourdp = mbr_findslice(dp, bp);
-		if (ourdp ==  NO_MBR_SIGNATURE)
-			goto nombrpart;
-
-		if (ourdp) {
-			/* need sector address for SCSI/IDE,
-			 cylinder for ESDI/ST506/RLL */
-			dospartoff = ourdp->mbrp_start;
-			cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect);
-		}
+	if (osdep) {
+		dp = osdep->dosparts;
+		read_mbr(dev, strat, lp, dp, &dospartoff, 0);
 	}
 
-nombrpart:
 #ifdef maybe
 	/* disklabel in appropriate location? */
 	if (lp->d_partitions[2].p_offset != 0
@@ -429,7 +539,7 @@
 
 	/* next, dig out disk label */
 	bp->b_blkno = dospartoff + LABELSECTOR;
-	bp->b_cylin = cyl;
+	bp->b_cylin = bp->b_blkno/lp->d_secpercyl;
 	bp->b_bcount = lp->d_secsize;
 	bp->b_flags = B_BUSY | B_READ;
 	(*strat)(bp);

--- /usr/src/sys/sys/disklabel_mbr.h.dist14	Tue Jun  8 16:15:27 1999
+++ /usr/src/sys/sys/disklabel_mbr.h	Tue Jun  8 16:41:37 1999
@@ -73,8 +73,11 @@
 #define	MBR_PTYPE_FAT32L	0x0c	/* 32-bit FAT, LBA-mapped */
 #define	MBR_PTYPE_FAT16L	0x0e	/* 16-bit FAT, LBA-mapped */
 #define	MBR_PTYPE_EXT_LBA	0x0f	/* extended partition, LBA-mapped */
+#define MBR_PTYPE_DM6		0x54	/* On Track disk manager */
+#define MBR_PTYPE_EZD		0x55	/* Easy Disk disk manager */
 #define MBR_PTYPE_LNXSWAP	0x82	/* Linux swap or Solaris */
 #define	MBR_PTYPE_LNXEXT2	0x83	/* Linux native */
+#define MBR_PTYPE_LNX_EXT	0x85	/* Linux extended partition */
 #define MBR_PTYPE_NTFS		0x87	/* Windows NT filesystem */
 
 /* Isolate the relevant bits to get sector and cylinder. */

>Audit-Trail:
>Unformatted: