Article View: comp.os.msdos.programmer
Article #6749Re: How does BIOS find Boot Sectors?
From: Eric Berggren
Date: Wed, 22 May 1991 20:31
Date: Wed, 22 May 1991 20:31
266 lines
12399 bytes
12399 bytes
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, ®s, ®s );
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, ®s, ®s, &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)
Technical Details
Message-ID:
<2710@pdxgate.UUCP>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!ogicse!pdxgate!eecs!ericbReferences:
<13073@monu1.cc.monash.oz>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!ogicse!pdxgate!eecs!ericb From: ericb@eecs.cs.pdx.edu (Eric Berggren) Newsgroups: comp.os.msdos.programmer Subject: Re: How does BIOS find Boot Sectors? Message-ID: <2710@pdxgate.UUCP> Date: 22 May 91 20:31:05 GMT References: <13073@monu1.cc.monash.oz> Sender: news@pdxgate.UUCP Lines: 266