Tag Archives: boot sector

oemboot

To build on the vdi_info post, I am throwing in my oemboot file. I have used this several times over the years in various forms, in general just to get a loader binary into memory. In its current form it uses LBA after doing an int 13 0x41 check error. Are there bugs? Maybe. It works for my purposes.

Here is a brief description of how it works:

  • 1. Scan directory for a loader file with a name defined by LOADFILE, 8.3 format
  • 2. Loads LOADFILE to memory defined by LOAD_SEG and LOAD_OFF, I use 0x7C000.
  • 3. Far jump to start of loader. The jmp far ptr loader_start is around line 470 and before the jump I load registers with “stuff” I want to pass to the loader.

Some simple BPB Error Codes:
1 – No bios extensions – IBM/MS INT 13 Extensions not supported
2 – Root directory read – could not read root directory
3 – LOADFILE not found
5 – FAT error – bad read or sig error

That is about it. The oemboot.asm file is very commented with good info and random thoughts, so I could remember why I had done certain things. The example files are located:

Code repo: Gitlab or My Gitlab

Okay, let’s put my simple test environment together. I make a raw vdi image then run it attached to a VM so I can format it to FAT16 and make the partition active. Next, I use vdi_info.exe to create bootpbp.inc which will be an include for oemboot.asm. I use uasm to compile oemboot.asm with the following commands:

uasm32 -bin [path to includes] -Fo oemboot.bin oemboot.asm

I write the oemboot.bin back to the vdi using the vdi_info.exe -w option. Next, the loader I am using just gets copied to the FAT16 vdi and boot with bochs.

vdi_info

General

This is a utility of need. I needed to get info from a virtualbox vdi image so that I could modify the boot sector. By modify, I mean to dump the required BPB info to an include file and graft my compiled oemboot boot sector on to the vdi. I did this a year ago and it has been used via a makefile since. What this means is I need to go back and figure out everything it does.

Specifics

Again, this tool is to make my life easier. Currently, I use Open Watcom v2 maintained by Jiří Malák. I compile it on a 32bit system, either an old Win XP image or a ReactOS image. I assume it would compile on AcraOS, I just have not tried to do so. I have only been using FAT16 because it is simple, and I just wanted to get things running.

My build flow is to compile my boot loader on a 32bit system, transfer the boot loader to a vdi image, and boot via bochs from Linux. To get the boot loader in memory, I use a custom boot sector named oemboot (I know, not very original). Without going into details, a vdi has a different structure than a normal hard drive image and it is tedious to extract the vdi information to an include file (BPB include) and, after compiling oemboot, writing the boot sector back to the vdi. The result was this tool that made the task easier when swapped to a new vdi or write a new boot sector to my dev image.

Code repo: Gitlab or My Gitlab

Be careful, while I put the repo together, I already see things I need to fix. Not big things, but mistakes that happen in a hurry. For example, in the v1.0 source if WRITEBPB is true I should skip the input file check until later. Oops.

How it works

The command line options are if you run vdi_info -h :

vdi_info: get dev image vdi information.
Version 1.0, compiled Nov  5 2022 with OpenWatcom 2.0

usage: vdi_info [-f alt filename][-i][-m][-p][-w] vdi_filename

   -f #  alternate input filename for -w option.
   -i    Dump BPB inc to bootpbp.inc.
   -m    Dump MBR to mbr.bin.
   -b    Dump active BPB to bpb.bin.
   -w    Write newmbr.bin to active BPB.
   -h    help message.

Valid option combinations:
  1. -m, -b, or -i single or all at once to dump MBR, BPB and INC.
  2. -w only or with optional -f <filename> for alternate BPB image to add.

The output with vdi name input results in some info about the image:

vdi_info fat16drive.vdi

Info: Input file found (fat16drive.vdi) and opened.

<<< Oracle VM VirtualBox Disk Image >>>
Image Version: 1.1
Image Sig: BEDA107F
Header size: 190
Drive Start: 200000
MBR Sig: AA55 Valid MBR signature!

Part num  1
State: 80
Head:  1
End:   1
Type:  6
Head:  F
End:   CD3F
Off:   3F
Sect:  32AE1

Part num  2
State: 0
Head:  0
End:   0
Type:  0
Head:  0
End:   0
Off:   0
Sect:  0

Part num  3
State: 0
Head:  0
End:   0
Type:  0
Head:  0
End:   0
Off:   0
Sect:  0

Part num  4
State: 0
Head:  0
End:   0
Type:  0
Head:  0
End:   0
Off:   0
Sect:  0

Active partition 1 found at offset 3F
Boot sector VDI offset:  2129408

All done!

-m

Read the vdi and get what is expected to be the MBR and check for a valid signature. If the -m option is passed then write the MBR to a file named mbr.bin.

-b

The MBR is scanned for the first active partition and extracted. If the -b option is passed, then write the BPB of the first active partition to a file named bpb.bin.

-i

So, here is the real use for this tool. When vdi_info -i <vdi file> is executed it outputs bootpbp.inc. Why did I name the output bootpbp.inc ? Who knows, I was probably tired or dyslexic. Anyway, I should really change it to bootbpb.inc. Passing -i goes through all the step above, read MBR, scan for first active partition, load the BPB of that partition, and dump it to an include file. The following is the output for my current development image.

; *** BPB output of vdi_info *** 

BS_OEMName      db  'MSDOS5.0'

BPB_BytsPerSec 	dw  0X0200
BPB_SecPerClus 	db  0X04
BPB_RsvdSecCnt 	dw  0X0001
BPB_NumFATs    	db  0X02
BPB_RootEntCnt 	dw  0X0200
BPB_TotSec16    dw  000000
BPB_Media       db  0XF8
BPB_FATSz16 	dw  0X00CB
BPB_SecPerTrk   dw  0X003F
BPB_NumHeads    dw  0X0010
BPB_HiddSec     dd  0X0000003F
BPB_TotSec32 	dd  0X00032AE1

BS_DrvNum       db  0X80
BS_Reserved1    db  0000
BS_BootSig      db  0X29
BS_VolID        dd  0X2A6316E8
BS_VolLab       db  'TESTDRV    '
BS_FilSysType   db  'FAT16   '

; *** end of BPB output ***

-w

This finds the active partition and writes a new boot sector from the file named newbpb.bin or the file name passed with the -f option.

Partition BPB Disassembled

BIOS parameter block (BPB) is a variant record embedded within the boot block (block zero) of a disc volume. In the example below the BPB is located from 0x7C0B to 0x7C36. The references I used, bios-parameter-block and DFSee.

Entry is made at 0x7C00 followed by a jump to _entry. After the stack is set up INT 12 is called to find the top of low memory (640K). Next, 54h (84 dec) is subtracted, and AND with 0xFFF0 then shifted left (6) to get the segment for the next module load (micro-FSD). For the Bochs example image the result is 0x8800. A load of BPB+31 sectors is loaded at the calculated segment and offset 0x0000 and the temp storage area is moved to this area (7C3E – 7C45). This load contains the instructions to continue the system load and the micro-FSD.

During the drive read and error will result in DAP information being displayed and a SYS02027 message, the system will then hang. The error sequence is from the readdrive procedure which is also used executing the micro-FSD loading code.

A check of the calculated segment and offset 0x0200 is made for 0x1961 signature. If the signature is found and return from function is executed which will result in execution continuing at the calculated load segment and offset 0x199C (Bochs image 8800:199C or 0x8999C). If the signature is now found, a “Invalid code for JFS” error and some address information will be displayed and the system will hang.

 

NOTE: Getting a “Invalid code for JFS” error could be cleared up with a sysinstx.com on the boot drive or by using DFSee.

On entry a register dump and these might not be set values (based on Bochs image):

eax: 0x00000000 ecx: 0x00007fbe — MBR info location edx: 0x00000080 — Drive number
ebx: 0x00007fbe — MBR info location esi: 0xffff7fbe — MBR info location
edi: 0x00080005 eip: 0x00007c00

eflags 0x00000246
cs:s=0x0000 ds:s=0x0000 ss:s=0x0030 es:s=0x0000
fs:s=0x3000 — location of INT 13 Ext result

The Extended Boot structure is as follows:

 

struct Extended_Boot {
	unsigned char Boot_jmp[3];
	unsigned char Boot_OEM[8];
	struct Extended_BPB Boot_BPB;
	unsigned char Boot_DriveNumber;
	unsigned char Boot_CurrentHead;
	unsigned char Boot_Sig = 41; /* Indicate Extended Boot */
	unsigned char Boot_Serial[4];
	unsigned char Boot_Vol_Label[11];
	unsigned char Boot_System_ID[8];
};

 

Where 

Boot_Serial

is the 32-bit binary volume serial number for the media. Boot_System_ID
is an 8-byte name written when the media is formatted. It is used by FSDs to identify their media but need not be the same as the name the FSD exports via FS_NAME and is NOT the name users employ to refer to the FSD. (They may, however, be the same names). Boot_Vol_Label
is the 11-byte ASCII label of the disk/diskette volume. FAT file systems must ALWAYS use the volume label in the root directory for compatibility reasons. An FSD may use the one in the boot sector.

 

The extended BPB structure is a super-set of the conventional BPB structure, as follows:

 

	  struct Extended_BPB {    unsigned short BytePerSector;
		  unsigned char SectorPerCluster;
		  unsigned short ReservedSectors;
		  unsigned char NumberOfFats;
		  unsigned short RootEntries;
		  unsigned short TotalSectors;
		  unsigned char MediaDescriptor;
		  unsigned short SectorsPerFat;
		  unsigned short SectorsPerTrack;
		  unsigned short Heads;
		  unsigned long HiddenSectors;
		  unsigned long Ext_TotalSectors;
	  };

 

 

7C00	jmp	short near ptr _entry  ;  Entry point from MBR code
7C02	nop
;  BIOS parameter block (BPB)
7C03	db    'IBM 4.50'    ; Partition creator
7C0B	db    0, 2    ; 0x0200 size of sector in bytes
7C0D	db    0
7C0E	db    0
7C0F	db    0
7C10	db    0
7C11	db    0
7C12	db    0
7C13	db    0
7C14	db    0
7C15	db   F8    ; media type - hard disk
7C16	db    0
7C17	db    0
7C18	db   3F, 0		; BPB formatted geo: Sectors - 63
7C1A	db    20, 0		; BPB formatted geo: Heads - 32
7C1C	db    3F, 0, 0 ,0	; 0x0000003F hidden sectors
7C20	db    41, 12, 13, 0    ; 0x00131241 Big number of sectors
7C24	db    80    		;  physical drive number
7C25	db    80    		;  Boot drive letter
7C26	db    29    		;  Ext-BPB signature
7C27	db    BD , 55,  9C, 69    ;  Partition serial number 0x699c55bd
7C2B	db    bochs, 0, 0, 0, 0, 0, 0    ; Partition label (11)
7C36	db    "JFS     "    ; Filesystem type (8)
; Used as temp storage
7C3E	db    0, 0, 0, 0    ; absolute number of the start of the sectors
7C42	db    0, 0, 0, 0
; DAP : Disk Address Packet (16 bytes)
7C46	db    10		; size of DAP = 16 = 10h
7C47	db    0			; unused, should be zero
7C48	db    20		; number of sectors to be read
7C49	db    0			; unused, should be zero
7C4A	db    0, 0, 0, 0	;segment:offset pointer to the memory buffer
7C4E	db    0, 0, 0, 0, 0, 0, 0, 0

 

 

_entry    proc far
	cli			; Clear Interrupt Flag
	xor	ax, ax		; zero ax
	mov	ss, ax		; set SS to 0000
	mov	sp, 7C00h    	; set stack pointer
	sti			; Set Interrupt Flag
	mov	bx, 7C0h
	mov	es, bx		; 7C0 to es
	sti			; Set Interrupt Flag
	; ax == 0  ss == 0  sp == 7C0  es == 7C0
	; Find the top of continuous low memory (640K)
	; subtract 54h (84 dec) or 84K
	; clear lower 4 bits
	; shift left 6 bits and move to ds
	; this will be the segment address for os2boot loading
	; Returns 27Fh - 639 is returned from Bochs and final ds == 0x8800
	int	12h		; MEMORY SIZE - LOW MEM Return: AX = number of contiguous 1K blocks of memory
	sub	ax, 54h		; subtract 54h -- 84 dec
	and	ax, 0FFF0h	; 1111111111110000b - results in 220h
	shl	ax, 6		; Shift	Logical	Left
	mov	ds, ax
	xor	edi, edi		; zero edi
	xor	ebx, ebx		; zero edx
	mov	eax, es:1Ch	; load eax 0x1C  (Bochs drive 0x0000003F) hidden sectors
	; add start of partition to readdrive storage
	add	es:3Eh,	eax
	adc	es:42h,	ebx
	push	ds		; buffer segment
	mov	ax, 20h		; number sectors to read
	xor	si, si		; buffer offset
	; Bochs drive:
	; eax: 0x00000020   ecx: 0x00007fbe  edx: 0x00000080  ebx: 0x00000000   esp: 0x00007bfe
	; esi: 0xffff0000   edi: 0x00000000
	; cs:s=0x0000  ds:s=0x8800  ss:s=0x0000  es:s=0x07c0  fs:s=0x3000
	call	readdrive
	pop	ds
	; Bochs drive:
	; if reados2boot returns from goodread, registers:
	; eax: 0x00000000  ecx: 0x00007fbe  edx: 0x00000080  ebx: 0x00000000  esp: 0x00007c00
	; ebp: 0x00000000  esi: 0xffff0046
	; cs:s=0x0000  ds:s=0x8800  ss:s=0x0000  es:s=0x07c0  fs:s=0x3000
	; stack is empty
	; move from part. boot data to Phase 3 data area
	mov	eax, es:3Eh
	mov	ds:3Eh,	eax	; Bochs image move 0x7C3E to 0x8803E
	mov	eax, es:42h
	mov	ds:42h,	eax	; Bochs image move 0x7C42 to 0x88042
	mov	al, es:24h
	mov	ds:24h,	al	; Bochs image move 0x7C24 to 0x88024
	; check for 1961h at 8800:0200 (0x88200)
	cmp	word ptr ds:200h, 1961h
	jz	short  _goodload
	mov	ax, 7C0h
	mov	ds, ax
	mov	ax, ds:200h	; move 7E00 to ax  == 0xb8fa
	mov	si, 0C8h		; *** display - "Invalid code for JFS" ***
	call	displayerr
_goodload:
	push	ds		; segment entry -- calc from previous
	mov	ax, 199Ch	; Offset
	push	ax		; 8800:199C (0x8999C) for Bochs drive
	retf			; Return Far from Procedure
_entry    endp

 

 

(0x7CC8)    InvalidCode	db 'Invalid code for JFS ',0
(0x7CDE)    Sys02027	db '- SYS02027 - ',0

 

 

;On entry the Bochs registers are:
; eax: 0x00000020   ecx: 0x00007fbe  edx: 0x00000080  ebx: 0x00000000   esp: 0x00007bfe
; esi: 0xffff0000   edi: 0x00000000
; cs:s=0x0000  ds:s=0x8800  ss:s=0x0000  es:s=0x07c0  fs:s=0x3000
; readdrive
;
;  entry:
;	ax contains number of sectors to read
;	es segment for DAP structure
;	ds segment for transfer buffer
;	si offset for transfer buffer
;	es:003E + 4 and es:0042 + 4 absolute number start sectors to read
;	es:0024 drive index
;	di (L) and bx (H) contain offset to absolute start for begin read
readdrive  proc near
	push	ds		; save ds and dx
	push	dx
	mov	dx, ds
	push	es
	pop	ds		; set ds to entry es value
	; DAP : Disk Address Packet (16 bytes)
	; offset range 	size 	description
	; 00h 		1 byte 	size of DAP = 16 = 10h
	; 01h 		1 byte 	unused, should be zero
	; 02h 		1 byte 	number of sectors to be read, 0..127 (= 7Fh)
	; 03h 		1 byte 	unused, should be zero
	; 04h..07h    4 bytes 	segment:offset pointer to the memory buffer
	;			to which sectors will be transferred
	; 08h..0Fh    8 bytes 	absolute number of the start of the sectors to be read
	; This routine DAP structure:
	; ds:0046		size of DAP - 16 bytes always
	; ds:0047		always zero
	; ds:0048		number of sectors to read
	; ds:0049		always zero
	; ds:004A to 004D       segment:offset pointer transfer buffer
	; ds:004E to 0055	absolute number of the start of the sectors to be
	;                       read (1st sector of drive has number 0)
	; Load DAP
	mov	ds:48h,	ax	; number of sectors to read, ax contains on entry
	mov	ds:4Ch,	dx	; Buffer segment
	mov	ds:4Ah,	si	; Buffer offset
	mov	si, 46h         	; DAP offset
	mov	eax, ds:3Eh	; move sector read start from storage area
	mov	ds:4Eh,	eax	; ds:003E to ds:0055 to DAP
	mov	eax, ds:42h
	mov	ds:52h,	eax
	add	ds:4Eh,	edi
	adc	ds:52h,	ebx
	; DAP located at ds:0046
	mov	ah, 42h         	; 42h = function number for extended read
	mov	dl, ds:24h      	; drive index
	mov	al, 0
	int	13h		; cf  Set On Error, Clear If No Error
				; ah  Return Code
	jnb	short goodread
	or	ah, ah
	jnz	short readerror
goodread:
	pop	dx		; restore entry dx and ds before returning
	pop	ds
	retn
readerror:			; display some DAP info
	push	ax
	mov	eax, ds:52h
	shr	eax, 10h
	call	dispaddress
	mov	eax, ds:52h
	call	dispaddress
	mov	eax, ds:4Eh
	shr	eax, 10h
	call	dispaddress
	mov	eax, ds:4Eh
	call	dispaddress
	mov	ax, ds:48h
	shl	eax, 10h
	pop	ax
	mov	al, dl
	mov	si, 0DEh		; SYS02027  message
	call	$+3		; really a jump to displayerr - never returns
readdrive  endp
; displayerr
;  Display error message pointed to by ds:(e)si and address
;  then hang the system
displayerr	proc near
	cld
	push	eax
_dispnextchar:
	lodsb			; Load byte at address DS:(E)SI into AL
	test	al, 0FFh
	jz	short   _endmessage
	mov	ah, 0Eh		; int10 teletype output
	mov	bx, 7		; page 0 - color 7
	int	10h
	jmp	short _dispnextchar
_endmessage:
	sti
	pop	eax
	push	eax
	and	eax, 0FFFF0000h
	shr	eax, 10h
	call	dispaddress
	mov	al, 3Ah
	mov	ah, 0Eh
	mov	bx, 7
	int	10h
	pop	eax
	call	dispaddress
_hangsystem:
	jmp	short   _hangsystem
displayerr	endp
; dispaddress
;  Entry - address (16 bit) in ax
;  Display address in hex on page 0
 dispaddress	proc near
	push	ax
	mov	al, ah
	and	al, 0F0h
	mov	cl, 4
	shr	al, cl
	call	dispchar
	pop	ax
	push	ax
	mov	al, ah
	and	al, 0Fh
	call	dispchar
	pop	ax
	push	ax
	and	al, 0F0h
	mov	cl, 4
	shr	al, cl
	call	dispchar
	pop	ax
	push	ax
	and	al, 0Fh
	call	dispchar
	pop	ax
	retn
dispaddress  endp
; dispchar
;  Output char from dispaddress
dispchar	proc near
	add	al, 30h
	cmp	al, 39h
	jle	short   _dispchar1
	add	al, 7
_dispchar1:
	mov	ah, 0Eh	; int10 teletype output
	mov	bx, 7	; page 0 - color 7
	int	10h
	retn
dispchar	endp

 

 

Os2ldr	db 'OS2LDR',0
Os2boot	db 'OS2BOOT',0
shtemenko   db '(c) P.Shtemenko 2002,2004',0
	db    0
	db    0
	db    0
	db    0
	db    0
	db    0
	dw 0AA55h

 

LVM MBR Disassembled

Loaded at 7C00h, setup stack and copy block from 7C00 to 7E00h. Push 7E20h on the stack and return near which will begin execution at 7E20h. This is the initial code loaded at 07C0:0000 and the disassembled code relocated at 07E0:0000 continues below.

After relocation the entry is at 7E20, I left out the 7E00 to 7E1F code which is not used. I am really only interested in the basic follow and the loaded and follow-on module, so some of the commenting could be better. In general, a Boot Manager partition is looked for and a simple consistency check is done on the MBR.

If you see below a check is made to verify INT 13 Extensions API support (see CheckINT13Ext). The result is stored at 3000:0000, if supported 58333149h is stored if not 0 is stored. This value is used later.

ReadDrive procedure is used to load the second drive without using the INT 13 Extensions API, but is used later to load the partition boot information using INT 13 Extensions API.

Things for my own note, there are 3 possible error messages while processing the LVM MBR: SYS01462, SYS01463, and SYS01464. Also, the drives look like they must support INT 13 Extensions API which should not be a problem now days.

The partition boot information is loaded at 7C00 and execution is continued.

 

(7C00h)
_entry		proc near
	; disable interupts
	cli
	; setup stack
	mov	ax, 30h
	mov	ss, ax
	mov	sp, 100h  ; decimal 256
	; enable interupts
	sti
	; move 7C00 to 7E00 +512
	cld
	xor	ax, ax     ; Zero out the Accumulator
	mov	ds, ax     ; Zero-out Data Segment
	mov	es, ax     ; Zero-out Extra Segment
	mov	si, 7C00h  ; Copy from here...
	mov	di, 7E00h  ; copy to here: 0000:7E00
	mov	cx, 200h   ; 200h (512 words) count
	rep movsw
	; push return addr 7E20 and execute return
	push	7E20h
	retn
_entry		endp

 

After the code is moved from 0000:7C00 to 0000:7E00 the entry point is 0000:7E20.

 

(7E20)
_relocentry     proc near
	mov     si, 7EFAh       ; si point to SYS01462 message start
	mov     bx, 7FBEh       ; bx set to first partition info location
; *** Check for Boot Manager
; read each MBR record and check if it is type Boot Manager (0x0A).
bootman00:
	cmp     byte ptr [bx+4], 0Ah ; cmp part type 11 (0Ah) BootManager
	jz      short checktype      ; jump if Boot Manager partition found
	add     bx, 10h              ; setup to read next partition entry
	cmp     bx, 7FFEh            ; check for sig - end of MBR if not get next
	jl      short bootman00
	xor     ax, ax               ; zero ax
	int     13h                  ; DISK - RESET DISK SYSTEM
	; DISK - GET DRIVE PARAMETERS (PC,XT286,CONV,PS,ESDI,SCSI)
	; AH = 08h
	; DL = drive (bit 7 set for hard disk)
	; Return:CF set on error
	; AH = status (07h) (see #0211)
	; CF clear if successful
	; AH = 00h
	; AL = 00h on at least some BIOSes
	; BL = drive type (AT/PS2 floppies only) (see #0219)
	; CH = low eight bits of maximum cylinder number
	; CL = maximum sector number (bits 5-0)
	; high two bits of maximum cylinder number (bits 7-6)
	; DH = maximum head number
	; DL = number of drives
	; ES:DI -> drive parameter table (floppies only)
	; check for drive #2
	mov     ah, 8
	mov     dl, 81h ; drive 2
	int     13h
	jb      short checktype ;  jump no 2nd drive
	; read 2nd drive MBR
	mov     cx, 7FB4h
	mov     dl, 81h
	call    ReadDrive
	or      ah, ah
	jnz     short checktype   ; jump if read error
	cmp     word ptr ds:7DFEh, 0AA55h  ; check signature
	jnz     short checktype    ; jump not valid MBR signature
	mov     bx, 7DBEh ; set start 2nd drive MBR info
	; Check for Boot Manager on 2nd drive
bootman01:
	cmp     byte ptr [bx+4], 0Ah
	jnz     short bootman02
	; the following get executed if a 0Ah found on 2nd drive
	mov     dl, 81h    ; second drive 81h in dl
	mov     cx, bx      ; bx - location of 0Ah partition info
	jmp     short extcheck2
bootman02:
	add     bx, 10h
	cmp     bx, 7DFEh
	jl      short bootman01
; *** Check for bootable partition
; get here if boot manager part found on 1st drive,  no second drive, and falls through
; no boot manager found on second drive
checktype:
	mov     bx, 7FBEh  ; load _bootind partition 1
	xor     cx, cx  ; zero cx
; seems to just run through the MBR and ensure it is somewhat correct
checktype1:
	cmp     byte ptr [bx], 80h ; is it bootable?
	jnz     short checktype2   ; jump if not bootable
	or      cx, cx
	jnz     short DispMsgEntry ; Not zero display SYS01462 error and hang
	mov     cx, bx
	jmp     short checktype3
checktype2:
	cmp     byte ptr [bx], 0 ; is it 0 (not-bootable) - so if not 80h or 0 then unknown _bootind
	jnz     short DispMsgEntry ; Not zero display SYS01462 error and hang
checktype3:
	add     bx, 10h ; increment to next partition record
	cmp     bx, 7FFEh ; at the end of MBR - check signature
	jl      short checktype1
	or      cx, cx
	jnz     short extcheck1
	int     18h        ; None were bootable, so start ROM-BASIC many
			; BIOS simply display "PRESS A KEY TO REBOOT"
			; when an Interrupt 18h is executed.
extcheck1:
	mov     dl, 80h    ; first drive
; at this point dl contains drive number 80h or 81h
extcheck2:
	pusha                 ; PUSH AX, CX, DX, BX, SP, BP, SI and DI
	call    CheckINT13Ext
	popa                  ; POP  AX, CX, DX, BX, SP, BP, SI and DI
	push    dx
	push    cx
	call    ReadDrive
	jz      short vbr00        ; jump no error
	mov     si, 7F0Fh          ; SYS01463 Message
	jmp     short DispMsgEntry ; Not zero display error and hang
vbr00:
	mov     si, 7F24h                 ; SYS01464 Message
	cmp     ds:SigEnd, 0AA55h    ; compare to end block signature
	jnz     short DispMsgEntry     ; Not zero display error and hang
	pop     si	; seems to hold MBR pointer
	pop     dx	; boot drive number
	jmp     far ptr 0000:7C00h    ; ** jump 0000:7C00 **
_relocentry     endp
; IBM/MS INT 13 Extensions - INSTALLATION CHECK
; AH = 41h
; BX = 55AAh
; DL = drive (80h-FFh)
; Return:CF set on error (extensions not supported)
; AH = 01h (invalid function)
; CF clear if successful
; BX = AA55h if installed
; AH = major version of extensions
;   01h = 1.x
;   20h = 2.0 / EDD-1.0
;   21h = 2.1 / EDD-1.1
;   30h = EDD-3.0
; AL = internal use
; CX = API subset support bitmap (see #0248)
; DH = extension version (v2.0+ ??? -- not present in 1.x)
; Note: The Phoenix Enhanced Disk Drive Specification v1.0 uses version 2.0 of the INT 13 Extensions API
;
; See Also: AH=42h"INT 13 Ext" - AH=48h"INT 13 Ext"
;
; Bitfields for IBM/MS INT 13 Extensions API support bitmap:
;
; Bit(s)  Description     (Table 0248)
;   0      extended disk access functions (AH=42h-44h,47h,48h) supported
;   1      removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h)
;           supported
;   2      enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported.
;  Extended drive parameter table is valid (see #0250,#0255)
;   3-15   reserved (0)
;
; NOTE: From : http://lrs.uni-passau.de/support/doc/interrupt-57/RB-0668.HTM
; checks for extended int 13 capability -
; Exit if supported  3000:0000   move  58333149h
; not supported mov 0
; * dl contains drive number on entry
(7EBA)
CheckINT13Ext   proc near
	mov     ah, 41h
	mov     bx, 55AAh
	int     13h
	jb      short NoINT13Ext        ; jump to NoINT13Ext is not supported
	cmp     bx, 0AA55h              ; AA55h if installed INT 13 Extensions API
	jnz     short NoINT13Ext
	cmp     ah, 21h                 ; major version of extensions 21h = 2.1 / EDD-1.1
	jb      short NoINT13Ext
	test    cl, 1                   ; Test if extended Disk Access functions supported
	jz      short NoINT13Ext
	mov     eax, 58333149h
	jmp     short INT13Continue
NoINT13Ext:
	xor     ax, ax    ; zero ax if ext 13 not supported
INT13Continue:
	push    3000h    ; store eax at 3000:0000
	pop     fs
	mov     fs:0, eax
	retn
CheckINT13Ext   endp

 

There are 3 possible error messages while processing the LVM MBR: SYS01462, SYS01463, and SYS01464:

 

***** DISPLAY MESSAGE LOOP *****
(7EE8)
DispMsgEntry:
	xor     bx, bx	; zero bx
	jmp     short DispMsg
DispNext:
	int     10h
DispMsg:
	mov     ah, 0Eh
	lodsb		; get char of message
	or      al, al	; check if end
	jnz     short DispNext
	sti
HangLoop:
	jmp     short HangLoop
; The following boot message information is from Bob Eager,
; Tavi Systems page http://www.tavi.co.uk/os2pages/boot.html:
; SYS01462
; The partition table on the startup drive is incorrect. Generally, this
; means either that more than one partition is marked active, or one of
; the partitions has a status byte with a value other than 00H or 80H,
; which are the only legal values.
7EF8                 db  12h
7EF9                 db    0
7EFA _SYS01462       db 'OS/2 !! SYS01462',0Dh,0Ah,0
; SYS01463
; The operating system cannot be loaded from the startup drive. This is
; caused by a disk read error, while reading the boot sector of the
; active partition
7F0D                 db  12h
7F0E                 db    0
7F0F _SYS01463       db 'OS/2 !! SYS01463',0Dh,0Ah,0
; SYS01464
; The operating system is missing from the startup drive. A valid boot
; sector for a partition should contain the values 055H and 0AAH in its
; last two bytes, in that order. This is a simple validation check,
; intended to prevent attempts to boot from a corrupt or unformatted
; partition. This message is generated if the validation check for
; these two bytes fails.
7F22                 db  12h
7F23                 db    0
7F24 _SYS01464       db 'OS/2 !! SYS01464',0Dh,0Ah,0

 

ReadDrive procedure loads MBR of the second drive and the boot record of the botable partition:

 

; On entry:
;    DL == drive
;    CX == location of Track + Sector to read
;    location 3000:0000 == 49h ext read supported else old 0 - 1023 read
ReadDrive       proc near
	mov     bx, cx
	mov     di, 5
	 ; see INT13Ext storage 3000:0000
	push    3000h
	pop     fs
	cmp     byte ptr fs:0, 49h
	jz      short ExtRead    ; equals 49h if ext supported
	; CX contains both the cylinder number (10 bits, possible
	; values are 0 to 1023) and the sector number (6 bits, possible values are 1 to 63):
	mov     cx, [bx+2]
	; Head
	mov     dh, [bx+1]
	; (ES):BX = Memory Buffer
	mov     bx, 7C00h
ReadOld01:
	xor     ax, ax
	int     13h           ; Reset DISK
	mov     ax, 201h      ; Function 2 AH == 00000010 / Sectors To Read Count AL == 00000001
	int     13h           ; INT 13, -- Read Sectors From Drive
	jnb     short ReadOld02  ; error reading
	dec     di               ; number of retries - default 5
	jg      short ReadOld01  ; retry read
ReadOld02:
	retn
	; The following are the "INT 13 Extensions Installation Check" and
	; the Extended READ sectors from Hard Drive (Function 42h) routines.
	; **** Normal entry if Ext int13 supported after CheckINT13Ext call
ExtRead:
	; dl == drive number
	push    ds
	mov     eax, [bx+8]  ; load number of sectors before partition from MBR
	; set fs and ds 0x3000
	push    fs
	pop     ds
	; DS:SI 	segment:offset pointer to the DAP, see below
	; DAP == 3000:0008
	mov     si, 8
	; DAP : Disk Address Packet (16 bytes)
	; offset range 	size 	description
	; 00h 		1 byte 	size of DAP = 16 = 10h
	; 01h 		1 byte 	unused, should be zero
	; 02h 		1 byte 	number of sectors to be read, 0..127 (= 7Fh)
	; 03h 		1 byte 	unused, should be zero
	; 04h..07h    4 bytes 	segment:offset pointer to the memory buffer to which sectors will be transferred
	; 08h..0Fh    8 bytes 	absolute number of the start of the sectors to be read (1st sector of drive has number 0)
	mov     ds:4, eax
	mov     [si+8], eax
	xor     eax, eax      ; zero eax
	mov     word ptr [si], 10h     ; 00h    BYTE    10h (size of packet)
	mov     word ptr [si+2], 1     ; number of blocks to transfer
	mov     word ptr [si+4], 7C00h ; -> transfer buffer
	mov     [si+6], ax
	mov     [si+0Ch], eax
	; DAP"
	; 01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F  10
	; 10  00  01  00  [00 7C  00 00] [00 3F   00 00   00 00   00 00]
ReadJmp04:
	sub     ax, ax
	int     13h     ; Reset drive
	mov     ah, 42h  ; function number for extended read
	int     13h
	jnb     short ReadJmp05  ; Error
	dec     di
	ja      short ReadJmp04  ; Retry read
ReadJmp05:
	pop     ds
	retn
ReadDrive       endp

 

 

; The following is an example using my current Virtual PC drive
; src/jfs/utils/libfs/mbr.h -- from openJFS source located on Netlabs.org
;
; struct part {
;     UCHAR       bootind;        /* 0x80 means partition is bootable */
;     UCHAR       starthead;      /* head number of partition start */
;     UCHAR       startsect;      /* sector number */
;     UCHAR       startcyl;       /* cylinder number */
;     UCHAR       systind;        /* partition ID */
;     UCHAR       endhead;        /* head number of partition end */
;     UCHAR       endsect;        /* sector number */
;     UCHAR       endcyl;         /* cylinder number */
;     ULONG       lsn;            /* number of sectors before partition */
;     ULONG       nsects;         /* number of sectors in partition */
; };
;
; struct mbr {
;     UCHAR       code[0x1be];    /* boot record code and data */
;     struct part ptbl[4];        /* the partition table */
;     USHORT      sig;            /* special signature */
; };
7FB8 OptiDiskSig     dd 0
7FBC                 dw 0CC33h
7FBE ; ***** Partition 1 *****
7FBE _bootind        db 80h   ; bootable
7FBF _starthead      db 1
7FC0 _startsect      db 1
7FC1 _startcyl       db 0
7FC2 _systind        db 7
7FC3 _endhead        db 3Fh
7FC4 _endsect        db 0FFh
7FC5 _endcyl         db 0F6h
7FC6 _lsn            dd 3Fh
7FCA _nsects         dd 3E7201h
7FCE ; ***** Partition 2 *****
7FCE Part2           db 10h dup(0)
7FDE ; ***** Partition 3 *****
7FDE Part3           db 10h dup(0)
7FEE ; ***** Partition 4 *****
7FEE Part4           db 10h dup(0)
7FFE ; ***** Signature *****
7FFE SigEnd          dw 0AA55h

 

Hardware and BIOS Initialization

On system reset, the BIOS is given control by the CPU. After performing tests and system initialisation, the BIOS starts the system boot operation; this all takes place in real mode, generally using 16 bit code, although some 32 bit instructions may be used. The actual sequence of operation depends on the BIOS options selected, but in most cases the BIOS will attempt to boot from the first diskette drive; if this fails, it will attempt to boot from the first hard drive. In both cases, the basic operation is the same, although the following is primarily about booting from a hard drive.

The boot operation consists of reading the first sector (cylinder 0, head 0, sector 1) from the boot device, and placing it into memory starting at address 0000:7C00H (segment 0, offset 7C00). Control is then transferred to address 0000:7C00H, i.e. with CS (the code segment register) set to 0000H, and IP (the instruction pointer) set to 7C00H. Note that this is functionally different to having CS set to 07C0H, and IP set to 0000H!

  • Move itself in memory, to vacate the area it occupies in order to make way for the boot sector for the partition being booted. Conventionally, it is moved to 0000:7E00H.
  • Check for Boot Manager partition on first and second drive (if installed)
  • Validate the partition table (checking for exactly one active partition, and for format errors).
  • Read the boot sector (first sector) from the active partition, into memory at 0000:7C00H; this mimics the effect if the disk were not partitioned at all (as would be the case for a diskette).
  • Transfer control to 0000:7C00H.
The previous from Bob Eager, Tavi Systems page The OS/2 boot sequence.