]> git.saurik.com Git - apple/boot.git/blobdiff - i386/libsaio/disk.c
boot-93.tar.gz
[apple/boot.git] / i386 / libsaio / disk.c
index b08678df17c755583328275a3bdad0f4abd54bb0..7c70cf8465a412faebca4e6dd643163393ffbe18 100644 (file)
  * All rights reserved.
  */
 
  * All rights reserved.
  */
 
-#define DRIVER_PRIVATE
-
-#include "sys/types.h"
-#include "legacy/disk.h"
-#include "legacy/fdisk.h"
 #include "libsaio.h"
 #include "libsaio.h"
-#include "memory.h"
-
-/*
- * Type and constant definitions.
- */
-typedef struct disk_blk0   boot_sector;
-#define BOOT_SIGNATURE     DISK_SIGNATURE
-#define PART_TYPE_EXT      0x05
-#define PART_TYPE_APPLE    0xa8
-#define UFS_FRONT_PORCH    0
-
-#if            DEBUG
-#define DPRINT(x)       { printf x; }
-#define DSPRINT(x)      { printf x; sleep(1); }
-#else
-#define DPRINT(x)
-#define DSPRINT(x)
-#endif
-
-/*
- * Function prototypes.
- */
-extern void   spinActivityIndicator();
-static void   diskActivityHook();
-static int    Biosread(int biosdev, int secno);
-static struct fdisk_part * find_partition(u_int8_t type,
-                                          u_int8_t biosdev,
-                                          BOOL     mba);
+#include "fdisk.h"
 
 /*
  * diskinfo unpacking.
  */
 
 /*
  * diskinfo unpacking.
  */
-#define SPT(di)         ((di) & 0xff)
-#define HEADS(di)       ((((di)>>8) & 0xff) + 1)
-#define SPC(di)         (SPT(di) * HEADS(di))
-#define BPS             512     /* sector size of the device */
-#define N_CACHE_SECS    (BIOS_LEN / BPS)
+#define SPT(di)          ((di) & 0xff)
+#define HEADS(di)        ((((di)>>8) & 0xff) + 1)
+#define SPC(di)          (SPT(di) * HEADS(di))
 
 
-/*
- * Stores the geometry of the disk in order to
- * perform LBA to CHS translation for non EBIOS calls.
- */
-static struct diskinfo {
-    int spt;            /* sectors per track */
-    int spc;            /* sectors per cylinder */
-} diskinfo;
-
-/*
- * Globals variables.
- */
-int     label_secsize = BPS;
-char *  b[NBUFS];
-daddr_t blknos[NBUFS];
-struct  iob iob[NFILES];
+#define BPS              512     /* sector size of the device */
+#define N_CACHE_SECS     (BIOS_LEN / BPS)
+#define UFS_FRONT_PORCH  0
 
 /*
 
 /*
- * intbuf points to the start of the sector cache. BIOS calls will
- * store the sectors read into this memory area. If cache_valid
- * is TRUE, then intbuf contents are valid. Otherwise, ignore the
- * cache and read from disk.
+ * trackbuf points to the start of the track cache. Biosread()
+ * will store the sectors read from disk to this memory area.
  *
  *
- * biosbuf points to a sector within the sector cache.
+ * biosbuf points to a sector within the track cache, and is
+ * updated by Biosread().
  */
  */
-static char * const intbuf = (char *)ptov(BIOS_ADDR);
-static BOOL   cache_valid  = FALSE;
-static char * biosbuf;
+static const char * const trackbuf = (char *) ptov(BIOS_ADDR);
+static const char * biosbuf;
 
 
-/*==========================================================================
- * 
+/*
+ * Map a disk drive to bootable volumes contained within.
  */
  */
-void
-devopen(char * name, struct iob * io)
-{
-    static int          last_biosdev = -1;
-    static daddr_t      last_offset  = 0;
+struct DiskBVMap {
+    int                biosdev;  // BIOS device number (unique)
+    BVRef              bvr;      // chain of boot volumes on the disk
+    int                bvrcnt;   // number of boot volumes
+    struct DiskBVMap * next;     // linkage to next mapping
+};
 
 
-    struct fdisk_part * part;
-    long                di;
+static struct DiskBVMap * gDiskBVMap  = NULL;
+static struct disk_blk0 * gBootSector = NULL;
 
 
-    io->i_error = 0;
-    io->dirbuf_blkno = -1;
+extern long HFSInitPartition(CICell ih);
+extern long HFSLoadFile(CICell ih, char * filePath);
+extern long HFSGetDirEntry(CICell ih, char * dirPath, long * dirIndex,
+                           char ** name, long * flags, long * time);
 
 
-    // Use cached values if possible.
-    //
-    if (io->biosdev == last_biosdev) {
-        io->i_boff = last_offset;
-        return;
-    }
+extern long UFSInitPartition(CICell ih);
+extern long UFSLoadFile(CICell ih, char * filePath);
+extern long UFSGetDirEntry(CICell ih, char * dirPath, long * dirIndex,
+                           char ** name, long * flags, long * time);
 
 
-    // initialize disk parameters -- spt and spc
-    // must do this before doing reads from the device.
+extern void spinActivityIndicator();
 
 
-    di = get_diskinfo(io->biosdev);
-    if (di == 0) {
-        io->i_error = ENXIO;
-        return;
-    }
+static void getVolumeDescription(BVRef bvr, char * str, long strMaxLen);
 
 
-    diskinfo.spt = SPT(di);
-    diskinfo.spc = diskinfo.spt * HEADS(di);
-
-    // FIXME - io->partition is ignored. Doesn't make sense anymore.
-    //         we also don't overwrite the 'name' argument.
-    //         Whats special about "$LBL" ?
-
-    part = find_partition(PART_TYPE_APPLE, io->biosdev, FALSE);
-    if (part == NULL) {
-        io->i_error = EIO;
-        DSPRINT(("Unable to find partition: IO error\n"));
-    } else {
-        last_offset  = io->i_boff = part->relsect + UFS_FRONT_PORCH/BPS;
-        last_biosdev = io->biosdev;
-        DSPRINT(("partition offset: %x\n", io->i_boff));
-    }
-}
+//==========================================================================
 
 
-/*==========================================================================
- * 
- */
-void devflush()
+static int getDiskGeometry( int biosdev, int * spt, int * spc )
 {
 {
-    cache_valid = FALSE;   // invalidate the sector cache (intbuf)
-}
-
-/*==========================================================================
- * 
- */
-int devread(struct iob * io)
-{
-    long      sector;
-    int       offset;
-//  int       dev;
-
-    io->i_flgs |= F_RDDATA;
+    static int cached_biosdev = -1;
+    static int cached_spt = 0;
+    static int cached_spc = 0;
+    
+    if ( biosdev != cached_biosdev )
+    {
+        long di = get_diskinfo(biosdev);
+        if (di == 0) return (-1); // BIOS call error
 
 
-    io->i_error = 0;        // assume the best
+        cached_spt = SPT(di);
+        cached_spc = cached_spt * HEADS(di);
 
 
-//  dev = io->i_ino.i_dev;
+        DEBUG_DISK(("%s: %d sectors, %d heads\n",
+                    __FUNCTION__, cached_spt, (int)HEADS(di)));
+    }
 
 
-    sector = io->i_bn * (label_secsize/BPS);
+    *spt = cached_spt;
+    *spc = cached_spc;
 
 
-    for (offset = 0; offset < io->i_cc; offset += BPS) {
+    return 0;
+}
 
 
-        io->i_error = Biosread(io->biosdev, sector);
-        if (io->i_error)
-            return (-1);
+//==========================================================================
+// Maps (E)BIOS return codes to message strings.
 
 
-        /* copy 1 sector from the internal buffer biosbuf into buf */
-        bcopy(biosbuf, &io->i_ma[offset], BPS);
+struct NamedValue {
+    unsigned char value;
+    const char *  name;
+};
 
 
-        sector++;
-    }
+static const char * getNameForValue( const struct NamedValue * nameTable,
+                                     unsigned char value )
+{
+    const struct NamedValue * np;
 
 
-    io->i_flgs &= ~F_TYPEMASK;
+    for ( np = nameTable; np->value; np++)
+        if (np->value == value)
+            return np->name;
 
 
-    return (io->i_cc);
+    return NULL;
 }
 
 }
 
-/*==========================================================================
- * Maps (E)BIOS return codes to message strings.
- */
-struct bios_error_info {
-    int          errno;
-    const char * string;
-};
-
 #define ECC_CORRECTED_ERR 0x11
 
 #define ECC_CORRECTED_ERR 0x11
 
-static struct bios_error_info bios_errors[] = {
-    {0x10, "Media error"},
-    {0x11, "Corrected ECC error"},
-    {0x20, "Controller or device error"},
-    {0x40, "Seek failed"},
-    {0x80, "Device timeout"},
-    {0xAA, "Drive not ready"},
-    {0x00, 0}
+static const struct NamedValue bios_errors[] = {
+    { 0x10, "Media error"                },
+    { 0x11, "Corrected ECC error"        },
+    { 0x20, "Controller or device error" },
+    { 0x40, "Seek failed"                },
+    { 0x80, "Device timeout"             },
+    { 0xAA, "Drive not ready"            },
+    { 0x00, 0                            }
 };
 
 };
 
-static const char *
-bios_error(int errno)
+static const char * bios_error(int errnum)
 {
 {
-    struct bios_error_info * bp;
-    
-    for (bp = bios_errors; bp->errno; bp++) {
-        if (bp->errno == errno)
-            return bp->string;
-    }
-    return "Error 0x%02x";   // No string, print error code only
+    static char  errorstr[] = "Error 0x00";
+    const char * errname;
+
+    errname = getNameForValue( bios_errors, errnum );
+    if ( errname ) return errname;
+
+    sprintf(errorstr, "Error 0x%02x", errnum);
+    return errorstr;   // No string, print error code only
 }
 
 }
 
-/*==========================================================================
- * Use BIOS INT13 calls to read the sector specified. This function will
- * also perform read-ahead to cache a few subsequent sector to the sector
- * cache.
- *
- * The fields in diskinfo structure must be filled in before calling this
- * function.
- *
- * Return:
- *   Return code from INT13/F2 or INT13/F42 call. If operation was
- *   successful, 0 is returned.
- *
- */
-static int
-Biosread(int biosdev, int secno)
+//==========================================================================
+// Use BIOS INT13 calls to read the sector specified. This function will
+// also perform read-ahead to cache a few subsequent sector to the sector
+// cache.
+// 
+// Return:
+//   0 on success, or an error code from INT13/F2 or INT13/F42 BIOS call.
+
+static int Biosread( int biosdev, unsigned int secno )
 {
 {
-    static int xbiosdev, xcyl, xhead, xsec, xnsecs;
-    
-    extern unsigned char uses_ebios[];
+    static int xbiosdev, xcyl, xhead;
+    static unsigned int xsec, xnsecs;
+    static BOOL cache_valid = FALSE;
 
 
-    int  rc;
+    int  rc = -1;
     int  cyl, head, sec;
     int  spt, spc;
     int  tries = 0;
 
     int  cyl, head, sec;
     int  spt, spc;
     int  tries = 0;
 
-    DSPRINT(("Biosread %d \n", secno));
+    // DEBUG_DISK(("Biosread dev %x sec %d \n", biosdev, secno));
 
     // To read the disk sectors, use EBIOS if we can. Otherwise,
     // revert to the standard BIOS calls.
 
     // To read the disk sectors, use EBIOS if we can. Otherwise,
     // revert to the standard BIOS calls.
-    //
-    if ((biosdev >= BIOS_DEV_HD) && uses_ebios[biosdev - BIOS_DEV_HD]) {
+
+    if ((biosdev >= kBIOSDevTypeHardDrive) &&
+        (uses_ebios[biosdev - kBIOSDevTypeHardDrive] & EBIOS_FIXED_DISK_ACCESS))
+    {
         if (cache_valid &&
             (biosdev == xbiosdev) &&
             (secno >= xsec) &&
             (secno < (xsec + xnsecs)))
         {
         if (cache_valid &&
             (biosdev == xbiosdev) &&
             (secno >= xsec) &&
             (secno < (xsec + xnsecs)))
         {
-            biosbuf = intbuf + (BPS * (secno - xsec));
+            biosbuf = trackbuf + (BPS * (secno - xsec));
             return 0;
         }
 
         xnsecs = N_CACHE_SECS;
         xsec   = secno;
             return 0;
         }
 
         xnsecs = N_CACHE_SECS;
         xsec   = secno;
+        cache_valid = FALSE;
 
         while ((rc = ebiosread(biosdev, secno, xnsecs)) && (++tries < 5))
         {
             if (rc == ECC_CORRECTED_ERR) {
                 /* Ignore corrected ECC errors */
 
         while ((rc = ebiosread(biosdev, secno, xnsecs)) && (++tries < 5))
         {
             if (rc == ECC_CORRECTED_ERR) {
                 /* Ignore corrected ECC errors */
+                rc = 0;
                 break;
             }
             error("  EBIOS read error: %s\n", bios_error(rc), rc);
                 break;
             }
             error("  EBIOS read error: %s\n", bios_error(rc), rc);
@@ -294,10 +217,8 @@ Biosread(int biosdev, int secno)
             sleep(1);
         }
     }
             sleep(1);
         }
     }
-    else {
-        spt = diskinfo.spt;    // From previous INT13/F8 call.
-        spc = diskinfo.spc;
-
+    else if ( getDiskGeometry(biosdev, &spt, &spc) == 0 )
+    {
         cyl  = secno / spc;
         head = (secno % spc) / spt;
         sec  = secno % spt;
         cyl  = secno / spc;
         head = (secno % spc) / spt;
         sec  = secno % spt;
@@ -309,24 +230,26 @@ Biosread(int biosdev, int secno)
             (sec >= xsec) &&
             (sec < (xsec + xnsecs)))
         {
             (sec >= xsec) &&
             (sec < (xsec + xnsecs)))
         {
-            // this sector is in intbuf cache
-            biosbuf = intbuf + (BPS * (sec - xsec));
+            // this sector is in trackbuf cache
+            biosbuf = trackbuf + (BPS * (sec - xsec));
             return 0;
         }
 
         // Cache up to a track worth of sectors, but do not cross a
         // track boundary.
             return 0;
         }
 
         // Cache up to a track worth of sectors, but do not cross a
         // track boundary.
-        //
+
         xcyl   = cyl;
         xhead  = head;
         xsec   = sec;
         xnsecs = ((sec + N_CACHE_SECS) > spt) ? (spt - sec) : N_CACHE_SECS;
         xcyl   = cyl;
         xhead  = head;
         xsec   = sec;
         xnsecs = ((sec + N_CACHE_SECS) > spt) ? (spt - sec) : N_CACHE_SECS;
+        cache_valid = FALSE;
 
         while ((rc = biosread(biosdev, cyl, head, sec, xnsecs)) &&
                (++tries < 5))
         {
             if (rc == ECC_CORRECTED_ERR) {
                 /* Ignore corrected ECC errors */
 
         while ((rc = biosread(biosdev, cyl, head, sec, xnsecs)) &&
                (++tries < 5))
         {
             if (rc == ECC_CORRECTED_ERR) {
                 /* Ignore corrected ECC errors */
+                rc = 0;
                 break;
             }
             error("  BIOS read error: %s\n", bios_error(rc), rc);
                 break;
             }
             error("  BIOS read error: %s\n", bios_error(rc), rc);
@@ -337,162 +260,388 @@ Biosread(int biosdev, int secno)
     }
 
     // If the BIOS reported success, mark the sector cache as valid.
     }
 
     // If the BIOS reported success, mark the sector cache as valid.
-    //
+
     if (rc == 0) {
         cache_valid = TRUE;
     }
     if (rc == 0) {
         cache_valid = TRUE;
     }
-    biosbuf  = intbuf;
+    biosbuf  = trackbuf;
     xbiosdev = biosdev;
     
     xbiosdev = biosdev;
     
-    diskActivityHook();
+    spinActivityIndicator();
 
     return rc;
 }
 
 
     return rc;
 }
 
-/*==========================================================================
- * Replace this function if you want to change
- * the way disk activity is indicated to the user.
- */
-void
-diskActivityHook(void)
+//==========================================================================
+
+static int readBytes( int biosdev, unsigned int blkno,
+                      unsigned int byteCount, void * buffer )
 {
 {
-    spinActivityIndicator();
+
+    char * cbuf = (char *) buffer;
+    int    error;
+    int    copy_len;
+
+    DEBUG_DISK(("%s: dev %x block %x [%d] -> 0x%x...", __FUNCTION__,
+                biosdev, blkno, byteCount, (unsigned)cbuf));
+
+    for ( ; byteCount; cbuf += BPS, blkno++ )
+    {
+        error = Biosread( biosdev, blkno );
+        if ( error )
+        {
+            DEBUG_DISK(("error\n"));
+            return (-1);
+        }
+
+        copy_len = (byteCount > BPS) ? BPS : byteCount;
+        bcopy( biosbuf, cbuf, copy_len );
+        byteCount -= copy_len;
+    }
+
+    DEBUG_DISK(("done\n"));
+
+    return 0;    
 }
 
 }
 
-/*==========================================================================
- * Returns YES if the partition type specified is an extended fdisk
- * partition.
- */
-static BOOL
-isExtendedPartition(u_int8_t type)
+//==========================================================================
+
+static int isExtendedFDiskPartition( const struct fdisk_part * part )
 {
 {
-       int i;
-
-       u_int8_t extended_partitions[] = {
-               0x05,   /* Extended */
-               0x0f,   /* Win95 extended */
-               0x85,   /* Linux extended */
-       };
-
-       for (i = 0;
-                i < sizeof(extended_partitions)/sizeof(extended_partitions[0]);
-                i++)
-       {
-               if (extended_partitions[i] == type)
-                       return YES;
-       }
-       return NO;
+    static unsigned char extParts[] =
+    {
+        0x05,   /* Extended */
+        0x0f,   /* Win95 extended */
+        0x85,   /* Linux extended */
+    };
+
+    int i;
+
+    for (i = 0; i < sizeof(extParts)/sizeof(extParts[0]); i++)
+    {
+        if (extParts[i] == part->systid) return 1;
+    }
+    return 0;
 }
 
 }
 
-/*==========================================================================
- * Traverse the fdisk partition tables on disk until a partition is found
- * that matches the specified type.
- *
- * Arguments:
- *   type    - Partition type to search for (e.g. 0xa7 for NeXTSTEP).
- *   biosdev - BIOS device unit. 0x80 and up for hard-drive.
- *   mba     - If true, the partition found must be marked active.
- *
- * Return:
- *   A pointer to the matching partition entry in biosbuf memory.
- *   Note that the starting LBA field in the partition entry is
- *   modified to contain the absolute sector address, rather than
- *   the relative address.
- *   A NULL is returned if a match is not found.
- *
- * There are certain fdisk rules that allows us to simplify the search.
- *
- * - There can be 0-1 extended partition entry in any partition table.
- * - In the MBR, there can be 0-4 primary partitions entries.
- * - In the extended partition, there can be 0-1 logical partition entry.
- *
- */
-struct fdisk_part *
-find_partition(u_int8_t type, u_int8_t biosdev, BOOL mba)
+//==========================================================================
+
+static int getNextFDiskPartition( int biosdev, int * partno,
+                                  const struct fdisk_part ** outPart )
 {
 {
-#define MAX_ITERATIONS  128
+    static int                 sBiosdev = -1;
+    static int                 sNextPartNo;
+    static unsigned int        sExtBase;
+    static unsigned int        sExtDepth;
+    static struct fdisk_part * sExtPart;
+    struct fdisk_part *        part;
+
+    if ( sBiosdev != biosdev || *partno < 0 )
+    {
+        // Fetch MBR.
+        if ( readBootSector( biosdev, DISK_BLK0, 0 ) ) return 0;
+
+        sBiosdev    = biosdev;
+        sNextPartNo = 0;
+        sExtBase    = 0;
+        sExtDepth   = 0;
+        sExtPart    = NULL;
+    }
 
 
-    static u_int32_t    iter = 0;
-    static u_int32_t    offset_root;
-    static u_int32_t    offset;
+    while (1)
+    {
+        part  = NULL;
 
 
-    int                 n;
-    int                 rc;
-    boot_sector *       bootsect;
-    struct fdisk_part * match = 0;
-    struct fdisk_part * parts;
+        if ( sNextPartNo < FDISK_NPART )
+        {
+            part = (struct fdisk_part *) gBootSector->parts[sNextPartNo];
+        }
+        else if ( sExtPart )
+        {
+            unsigned int blkno = sExtPart->relsect + sExtBase;
 
 
-    if (iter == 0) {
-        if (rc = Biosread(biosdev, 0))  // Read MBR at sector zero.
-            return 0;
-        offset = 0;
+            // Save the block offset of the first extended partition.
+
+            if ( sExtDepth == 0 ) sExtBase = sExtPart->relsect;
+
+            // Load extended partition table.
+
+            if ( readBootSector( biosdev, blkno, 0 ) == 0 )
+            {
+                sNextPartNo = 0;
+                sExtDepth++;
+                sExtPart = NULL;
+                continue;
+            }
+        }
+
+        if ( part == NULL ) break;  // Reached end of partition chain.
+
+        // Advance to next partition number.
+
+        sNextPartNo++;
+
+        // Assume at most one extended partition per table.
+
+        if ( isExtendedFDiskPartition(part) )
+        {
+            sExtPart = part;
+            continue;
+        }
+
+        // Skip empty slots.
+
+        if ( part->systid == 0x00 )
+        {
+            continue;
+        }
+
+        // Change relative offset to an absolute offset.
+
+        part->relsect += sExtBase;
+
+        *outPart = part;
+        *partno  = sExtDepth ? (sExtDepth + 4) : sNextPartNo;
+
+        break;
     }
 
     }
 
-    bootsect = (boot_sector *) biosbuf;
-    if (bootsect->signature != BOOT_SIGNATURE)
-        return 0;
+    return (part != NULL);
+}
+
+//==========================================================================
 
 
-    // Find a primary or a logical partition that matches the partition
-    // type specified.
-    //
-    for (n = 0, parts = (struct fdisk_part *) bootsect->parts;
-         n < 4;
-         n++, parts++)
+static BVRef newFDiskBVRef( int biosdev, int partno, unsigned int blkoff,
+                            const struct fdisk_part * part,
+                            FSInit initFunc, FSLoadFile loadFunc,
+                            FSGetDirEntry getdirFunc, int probe )
+{
+    BVRef bvr = (BVRef) malloc( sizeof(*bvr) );
+    if ( bvr )
     {
     {
-        DSPRINT(("fdisk: [%d] %02x\n", iter, parts->systid));
+        bzero(bvr, sizeof(*bvr));
 
 
-        if (mba && ((parts->bootid & 0x80) == 0))
-            continue;
+        bvr->biosdev        = biosdev;
+        bvr->part_no        = partno;
+        bvr->part_boff      = blkoff;
+        bvr->part_type      = part->systid;
+        bvr->fs_loadfile    = loadFunc;
+        bvr->fs_getdirentry = getdirFunc;
+        bvr->description    = getVolumeDescription;
+
+        if ( part->bootid & FDISK_ACTIVE )
+            bvr->flags |= kBVFlagPrimary;
+
+        // Probe the filesystem.
+
+        if ( initFunc )
+        {
+            bvr->flags |= kBVFlagNativeBoot;
 
 
-        if (parts->systid == type) {
-            //
-            // Found it!!!
-            // Make the relsect field (LBA starting sector) absolute by
-            // adding in the offset.
-            //
-            parts->relsect += offset;
+            if ( probe && initFunc( bvr ) != 0 )
+            {
+                // filesystem probe failed.
 
 
-                       DSPRINT(("Found: %x (%d)\n", parts->relsect, parts->numsect));
+                DEBUG_DISK(("%s: failed probe on dev %x part %d\n",
+                            __FUNCTION__, biosdev, partno));
 
 
-            return parts;
+                free(bvr);
+                bvr = NULL;
+            }
+        }
+        else if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )
+        {
+            bvr->flags |= kBVFlagForeignBoot;
+        }
+        else
+        {
+            free(bvr);
+            bvr = NULL;
         }
     }
         }
     }
+    return bvr;
+}
 
 
-    // Find if there is an extended partition entry that points to
-    // an extended partition table. Note that we only allow up to
-    // one extended partition per partition table.
-    //
-    for (n = 0, parts = (struct fdisk_part *) bootsect->parts;
-         n < 4;
-         n++, parts++)
-    {
-        DSPRINT(("fdisk: [E%d] %02x\n", iter, parts->systid));
+//==========================================================================
+
+BVRef diskScanBootVolumes( int biosdev, int * countPtr )
+{
+    const struct fdisk_part * part;
+    struct DiskBVMap *        map;
+    int                       partno  = -1;
+    BVRef                     bvr;
+    BVRef                     booterUFS = NULL;
+    int                       spc, spt;
 
 
-        if (isExtendedPartition(parts->systid))
+    do {
+        // Find an existing mapping for this device.
+
+        for ( map = gDiskBVMap; map; map = map->next )
         {
         {
-            if (iter > MAX_ITERATIONS)  // limit recursion depth
-                return 0;
+            if ( biosdev == map->biosdev ) break;
+        }
+        if ( map ) break;
 
 
-            if (iter == 0)
-                offset = offset_root = parts->relsect;
-            else
-                offset = parts->relsect + offset_root;
+        // Create a new mapping.
 
 
-            iter++;
+        map = (struct DiskBVMap *) malloc( sizeof(*map) );
+        if ( map )
+        {
+            map->biosdev = biosdev;
+            map->bvr     = NULL;
+            map->bvrcnt  = 0;
+            map->next    = gDiskBVMap;
+            gDiskBVMap   = map;
 
 
-            // Load extended partition table.
-            //
-            if (((rc = Biosread(biosdev, offset)) == 0) &&
-                (bootsect->signature == BOOT_SIGNATURE))
+            // Create a record for each partition found on the disk.
+
+            while ( getNextFDiskPartition( biosdev, &partno, &part ) )
             {
             {
-                match = find_partition(type, biosdev, mba);
+                DEBUG_DISK(("%s: part %d [%x]\n", __FUNCTION__,
+                            partno, part->systid));
+
+                bvr = 0;
+
+                switch ( part->systid )
+                {
+                    case FDISK_UFS:
+                        bvr = newFDiskBVRef(
+                                      biosdev, partno,
+                                      part->relsect + UFS_FRONT_PORCH/BPS,
+                                      part,
+                                      UFSInitPartition,
+                                      UFSLoadFile,
+                                      UFSGetDirEntry,
+                                      0 );
+                        break;
+
+                    case FDISK_HFS:
+                        bvr = newFDiskBVRef(
+                                      biosdev, partno,
+                                      part->relsect,
+                                      part,
+                                      HFSInitPartition,
+                                      HFSLoadFile,
+                                      HFSGetDirEntry,
+                                      0 );
+                        break;
+
+                    case FDISK_BOOTER:
+                        if (getDiskGeometry(biosdev, &spt, &spc) != 0)
+                            break;
+
+                        booterUFS = newFDiskBVRef(
+                                      biosdev, partno,
+                                      ((part->relsect + spc - 1) / spc) * spc,
+                                      part,
+                                      UFSInitPartition,
+                                      UFSLoadFile,
+                                      UFSGetDirEntry,
+                                      0 );
+                        break;
+
+                    default:
+                        bvr = newFDiskBVRef(
+                                      biosdev, partno,
+                                      part->relsect,
+                                      part,
+                                      0, 0, 0, 0 );
+                        break;
+                }
+
+                if ( bvr )
+                {
+                    bvr->next = map->bvr;
+                    map->bvr  = bvr;
+                    map->bvrcnt++;
+                }
             }
             }
-            
-            iter--;
 
 
-            break;
+            // Booting from a CD with an UFS filesystem embedded
+            // in a booter partition.
+
+            if ( booterUFS )
+            {
+                if ( map->bvrcnt == 0 )
+                {
+                    map->bvr = booterUFS;
+                    map->bvrcnt++;
+                }
+                else free( booterUFS );
+            }
+        }
+    } while (0);
+
+    if (countPtr) *countPtr = map ? map->bvrcnt : 0;
+
+    return map ? map->bvr : NULL;
+}
+
+//==========================================================================
+
+static const struct NamedValue fdiskTypes[] =
+{
+    { 0x07,         "Windows NTFS"   },
+    { 0x0c,         "Windows FAT32"  },
+    { 0x83,         "Linux"          },
+    { FDISK_UFS,    "Apple UFS"      },
+    { FDISK_HFS,    "Apple HFS"      },
+    { FDISK_BOOTER, "Apple Boot/UFS" },
+    { 0x00,         0                }  /* must be last */
+};
+
+static void getVolumeDescription( BVRef bvr, char * str, long strMaxLen )
+{
+    unsigned char type = (unsigned char) bvr->part_type;
+    const char * name = getNameForValue( fdiskTypes, type );
+
+    if ( name )
+        sprintf( str, "hd(%d,%d) %s",
+                 BIOS_DEV_UNIT(bvr->biosdev), bvr->part_no, name );
+    else
+        sprintf( str, "hd(%d,%d) TYPE %02x",
+                 BIOS_DEV_UNIT(bvr->biosdev), bvr->part_no, type );
+}
+
+//==========================================================================
+
+int readBootSector( int biosdev, unsigned int secno, void * buffer )
+{
+    struct disk_blk0 * bootSector = (struct disk_blk0 *) buffer;
+    int                error;
+
+    if ( bootSector == NULL )
+    {
+        if ( gBootSector == NULL )
+        {
+            gBootSector = (struct disk_blk0 *) malloc(sizeof(*gBootSector));
+            if ( gBootSector == NULL ) return -1;
         }
         }
+        bootSector = gBootSector;
     }
     }
-    
-    return match;
+
+    error = readBytes( biosdev, secno, BPS, bootSector );
+    if ( error || bootSector->signature != DISK_SIGNATURE )
+        return -1;
+
+    return 0;
+}
+
+//==========================================================================
+// Handle seek request from filesystem modules.
+
+void diskSeek( BVRef bvr, long long position )
+{
+    bvr->fs_boff = position / BPS;
+}
+
+//==========================================================================
+// Handle read request from filesystem modules.
+
+int diskRead( BVRef bvr, long addr, long length )
+{
+    return readBytes( bvr->biosdev,
+                      bvr->fs_boff + bvr->part_boff,
+                      length,
+                      (void *) addr );
 }
 }