[comp.os.msdos.programmer] How does BIOS find Boot Sectors?

ins760z@monu4.cc.monash.edu.au (mr c.r. hames) (05/15/91)

     How does the BIOS know how far in is the boot record from the start of
a harddisk that is going to be autobooted from?  Like how does it work out
how much partition info is before the boot record ?


Email please,

ins760z@monu4.cc.monash.edu.au    ( An Amiga owner who enjoys emulating others)

ericb@eecs.cs.pdx.edu (Eric Berggren) (05/15/91)

ins760z@monu4.cc.monash.edu.au (mr  c.r. hames) writes:


>     How does the BIOS know how far in is the boot record from the start of
>a harddisk that is going to be autobooted from?  Like how does it work out
>how much partition info is before the boot record ?


>Email please,

  I already e-mailed him, here's a copy of my letter...

  Well.. [in case you have not already received 30 messages in 30 seconds]
Located on absolute sector 0 and offset 0x1be (I believe) is the partition
table. In this table contains all the necessary information such as starting
and ending cylinders, heads, and sectors, as well as its size and absolute
starting sector (why the redundancies, I dunno, some fdisk programs only
set one of the parameters and not the others, which creates problems for
some OS's other than MS-DOS). When you boot the system, the fixed disk BIOS
does its magic and looks for a partition with a flag indicating it is the
active one (also conatained in each partition entry) When it finds an active
partition, it reads the starting sector, jumps to that and boots similarly to
a floppy disk, the first sector of each partition has their own boot block,
so you may have multiple operating systems on one drive and can boot off of
any one by simply changing the active flag and rebooting the machine.
  Now DOS 4.xx+ has logical partitions in a physical partition, so each
MS-DOS physical partition has a mini-partition table in it. Only the first
logical partition is bootable, the rest are assigned as consecutive drives.

  This is a very general overview of how it works. If you want to know more
about the gory details, lemme know, as I am writing my own disk partitioning
program that will enable me to manipulate any type of partition.

-e.b.

==============================================================================
  Eric Berggren             |         "Life is a Turing Test;
  Computer Science/Eng.     |           We're all automatons!"
  ericb@eecs.cs.pdx.edu     |              - (click, whir, buzz, chirp)

ericb@eecs.cs.pdx.edu (Eric Berggren) (05/23/91)

  I'm currently constructing a program that allows a user to customize
the partition table on a hard disk with any of the available 256 types
of partitions and the ability to specify any starting and ending position
(within reason). This is not a general purpose program, but rather intended
for somebody who needs to configure the drive in a particular fashion in
which many FDisk programs would not allow. For example, DOS 4.x+ only
configures partitions in megs, with no control over where they go. This
often causes problems for other operating system which do not recognize
parculiar sizes or starting position. Note this program only manages
physical partitions, not logical ones, such as used in MS-DOS 4.x+ (you
must use their fdisk program to create the logical ones). There is a
list of known partition types in the program. These are all of the partitions
that I know of; I borrowed them from Minix. Any other ones that you know
of that aren't included, lemme know. I have to put off the completion of the
project until the end of the term (early June). When finished, I will post it,
along with the Turbo-C source, in binaries.

  My only reference is The MS-DOS Programmer's Guide (Waite Group), [and
not everything stated in there is true but, so far, it works]. Here's the
core structures and routines from my program to help understand what goes
on. I'll admit it's all basically hacked with little comments; it'll be
cleaned up prior to posting.

  The partition table for a hard drive is stored on physical sector 0, at
offset 1BEh. To get the partition table, simply read sector 0 into a buffer,
then load in the 4 consecutive entries into the structure below starting at
0x1be. And that's all there is to that. This is all that can fit on the first
sector; where extended number of partitions are stored, I'm not suere, perhaps
the next sector. Now within the structure itself, there's a bit of encoding
going on. The lower 6 bits of the start_ and end_sec holds the starting sector,
while the upper MSB's hold the MSB's for the start_ and end_cyl. (here's
some fancy detailed graphics):

                   | c | c | s | s | s | s | s | s |        start_sec(tor)
             --------|   |
             V   V-------|
           | c | c | c | c | c | c | c | c | c | c |        start_cyl(inder)

Note that the maximum number cylinders that can be represented is 1024.
The extractions of these I have in the macros below.

  To indicate an active partition, place 0x80 into the active field, else
place 0x00 in it to indicate it being inactive. Only one should be active
at a time. The results of multiple active partitions or funny values, I'm
not sure.

  I have a small structure which holds parameters for the current drive. I
use function 08h (get drive parms) of Int 13h to get the info. The number
of sectors and cylinders are encoded just like above. From there, you can
calculate things like tracks (cylinders / heads) and size in megs, etc,
found in the getparms() routine.

  Any other questions, lemme know.

-e.b.

#define MAXPARTITIONS   4               /* max number of physical partitions*/
#define SECTSIZE        512             /* sector size (bytes)              */
#define PARTTABLEOFS    0x1be           /* partition table offset in boot   */

#define NUMPARTTYPES    256             /* number of different partitions   */
#define NOPARTITION     0x00            /* empty partition type             */
#define BADBLOCKPART    0xff            /* partition of bad blocks          */

#define ACTIVE_FLAG     0x80            /* partition active flag            */

#define StartCyl(p)     ( p.start_cyl + ( ( (long)p.start_sec & 0xc0 ) << 2 ) )
#define StartSec(p)     ( p.start_sec & 0x3f )
#define StartHead(p)    ( p.start_head )
#define EndCyl(p)       ( p.end_cyl + ( ( (long)p.end_sec & 0xc0 ) << 2 ) )
#define EndSec(p)       ( p.end_sec & 0x3f )
#define EndHead(p)      ( p.end_head )
#define StartSector(p)  ( (long)p.abs_sec )
#define NumSectors(p)   ( (long)p.num_sec )

typedef struct {
            unsigned char   active;     /* 80h is active; 00h if not        */
            unsigned char   start_head; /* head of first sector             */
            unsigned char   start_sec;  /* first sector + 2 MSB's of cyl    */
            unsigned char   start_cyl;  /* cylinder of first sector         */
            unsigned char   type;       /* partition type (OS who owns it)  */
            unsigned char   end_head;   /* head of last sector              */
            unsigned char   end_sec;    /* last sector + 2 MSB'd of cyl     */
            unsigned char   end_cyl;    /* cylinder of last sector          */
            unsigned long   abs_sec;    /* absolute starting sector         */
            unsigned long   num_sec;    /* total sectors in partition       */
    } Part_Entry;

struct {
        unsigned char   numdrives;      /* number of consecutive drives     */
        unsigned char   heads;          /* number of surfaces               */
        unsigned char   sect_trk;       /* number of sectors per track      */
        unsigned int    cylinders;      /* number of total cylinders        */
        unsigned long   tracks;         /* calculated number of total tracks*/
        unsigned long   sectors;        /* calculated number of total sects */
        unsigned int    megs;           /* calculated capacity in megabytes */
    } DriveParms;

char    Sector_Buf[ SECTSIZE ];         /* sector I/O buffer                */
Part_Entry  PartTable[ MAXPARTITIONS ]; /* partition table for current drive*/
char    TypeTable[ NUMPARTTYPES ][ 10 ];    /* list of partition types      */

int     CurrentDrive = DEFAULT_DRIVE;   /* current logged drive             */
int     ReadOnly = FALSE;               /* disallows partition table update */
int     Not_Valid = TRUE;               /* current partition table invalid  */


void init_tt()
{
    int     i;

    memset( &PartTable, 0, sizeof( PartTable ) );
    for ( i = 0; i < NUMPARTTYPES; i ++ )
        switch ( i ) {
                case NOPARTITION :  strcpy( &TypeTable[ i ], "  NONE   " );
                                    break;
                case 0x01 : strcpy( &TypeTable[ i ], "DOS-12FAT" );
                            break;
                case 0x02 : strcpy( &TypeTable[ i ], "  XENIX  " );
                            break;
                case 0x03 : strcpy( &TypeTable[ i ], "XENIX-OLD" );
                            break;
                case 0x04 : strcpy( &TypeTable[ i ], "DOS-16FAT" );
                            break;
                case 0x05 : strcpy( &TypeTable[ i ], " DOS-EXT " );
                            break;
                case 0x06 : strcpy( &TypeTable[ i ], " DOS-BIG " );
                            break;
                case 0x08 : strcpy( &TypeTable[ i ], "   AIX   " );
                            break;
                case 0x0A : strcpy( &TypeTable[ i ], "  OPUS   " );
                            break;
                case 0x51 : strcpy( &TypeTable[ i ], " NOVELL? " );
                            break;
                case 0x52 : strcpy( &TypeTable[ i ], "   CPM   " );
                            break;
                case 0x63 : strcpy( &TypeTable[ i ], " 386/IX  " );
                            break;
                case 0x64 : strcpy( &TypeTable[ i ], " NOVELL  " );
                            break;
                case 0x75 : strcpy( &TypeTable[ i ], "  PCIX   " );
                            break;
                case 0x80 : strcpy( &TypeTable[ i ], "MINIX-OLD" );
                            break;
                case 0x81 : strcpy( &TypeTable[ i ], "  MINIX  " );
                            break;
                case 0xDB : strcpy( &TypeTable[ i ], "   CPM   " );
                            break;
                case BADBLOCKPART : strcpy( &TypeTable[ i ], "BADBLOCKS" );
                                    break;
                default   : strcpy( &TypeTable[ i ], " UNKNOWN " );
                            break;
        }
}


void sector_to_hsc( abs_end_sec, partentry )
    unsigned long   abs_end_sec;
    Part_Entry  *partentry;
{
    unsigned int    cylinder;

    cylinder = (unsigned int)( abs_end_sec / ( DriveParms.heads *
                                               DriveParms.sect_trk ) );
    partentry->end_cyl = cylinder & 0xff;
    partentry->end_sec = ( abs_end_sec % DriveParms.sect_trk ) +
                         ( ( cylinder >> 2 ) & 0xc0 ) + 1;
    partentry->end_head= ( abs_end_sec % ( DriveParms.heads *
                                           DriveParms.sect_trk ) ) /
                                         DriveParms.sect_trk;
    partentry->num_sec = abs_end_sec - partentry->abs_sec;
}


check_table()
{
    return( 0 );
}


getdriveparms()
{
    char    errmsg[ 35 ];
    union REGS  regs;

    regs.h.ah = 0x08;                   /* Function: 08h, get drive parms.  */
    regs.h.dl = 0x80 + CurrentDrive - 2;
    int86( 0x13, &regs, &regs );
    DriveParms.numdrives = regs.h.dl;
    DriveParms.heads = regs.h.dh + 1;
    DriveParms.sect_trk = regs.h.cl & 0x3f;
    DriveParms.cylinders = regs.h.ch + ( ( regs.h.cl & 0xc0 ) << 2 ) + 2;
    DriveParms.tracks = (long)DriveParms.heads * (long)DriveParms.cylinders;
    DriveParms.sectors = DriveParms.tracks * (long)DriveParms.sect_trk;
    DriveParms.megs = (int)( (long)DriveParms.tracks *
                             (long)DriveParms.sect_trk *
                             (long)SECTSIZE / (long)1000000 );
    if ( regs.x.cflag ) {
        sprintf( errmsg, "Error reading fixed disk %c: parameters",
                         CurrentDrive + 'A' );
        error( errmsg );
    }
    return( regs.x.cflag );
}


int writeparttab()
{
    if ( ReadOnly ) {
        error( "Write protect option enabled" );
        return( 1 );
    }
    return( 0 );
}


readparttab()
{
    int     i;
    char    errormsg[ 80 ];

    memset( &PartTable, 0, sizeof( PartTable ) );
    if ( ( Not_Valid = readboot( CurrentDrive, &Sector_Buf ) ) != 0 ) {
        sprintf( errormsg, "Error reading fixed disk %c:",
                 CurrentDrive + 'A' );
        error( errormsg );
        return( Not_Valid );
    }
    for( i = 0; i < MAXPARTITIONS; i ++ )
        memcpy( &PartTable[ i ],
                &Sector_Buf[ PARTTABLEOFS + i * sizeof( Part_Entry ) ],
                sizeof( Part_Entry ) );
    Not_Valid = check_table();
    return( Not_Valid );
}


readboot( drivenum, buffer )
    char    drivenum;
    char    *buffer;
{
    union REGS      regs;
    struct SREGS    sregs;

    if ( drivenum < 0 || drivenum > 7 )
        return( 1 );

    segread( &sregs );                  /* get segment registers (DS)       */
    regs.h.ah = 0x02;                   /* Function: 02h, read sectors      */
    regs.h.al = 0x01;                   /* 1 sector                         */
    regs.h.ch = 0x00;                   /* cylinder 0                       */
    regs.h.cl = 0x01;                   /* first sector = 1                 */
    regs.h.dh = 0x00;                   /* head = 0                         */
    regs.h.dl = 0x80 + drivenum - 2;    /* drive number                     */
    sregs.es  = sregs.ds;               /* address of buffer (segment)      */
    regs.x.bx = (int)buffer;            /* address of buffer (offset)       */

    int86x( 0x13, &regs, &regs, &sregs );
    return( (int)regs.x.cflag );        /* carry set=error occurred         */
}

==============================================================================
  Eric Berggren             |         "Life is a Turing Test;
  Computer Science/Eng.     |           We're all automatons!"
  ericb@eecs.cs.pdx.edu     |              - (click, whir, buzz, chirp)