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: