Category Archives: boot process

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.

The Warp Boot Sequence

Understanding the boot sequence of Warp is important to administrators and support personnel. It also helps to provide additional insight into the architechture of OS/2 Warp. Such an understanding will enable you to determine what can cause problems for your computer hardware as well as for Warp during boot.

Turning on the computer
When the computer is first turned on the power supply prevents the computer from taking any action by maintaining an electrical signal called “Power Good” in the off state until all power supply voltages have settled into the normal operational range. Only when the power supply is working correctly does it turn on the “Power Good” signal.
When the “Power Good” signal is turned on, some electronic circuitry in the computer sets the processor’s instruction pointer to the location in ROM BIOS which contains the POST program code. The computer loads the instruction at that location into the processor and starts the processor execution cycle. At this point the processor is in “Real” mode which means that it is effectively a fast 8086.
Power On Self Test
The Power On Self Test is located on a type of integrated circuit chip called Read Only Memory (ROM), along with some other programs such as BIOS. Power On Self Test (POST) is performed by most IBM PCs and PC compatible computers. POST is a critical part of the boot process because it provides some assurance that the hardware components of the computer are working correctly. Unfortunately, POST (and all DOS based diagnostic programs – there are no OS/2 based 32 bit diagnostic programs) is usually a 16 bit DOS class of program. It is therefore unable to test all aspects of a 32 bit computer. Even if POST or diagnostic programs were true 32 bit programs it would still be impossible to test every electronic circuit and component in a computer. At best a well written diagnostic program can only test about 80% of the components in a computer; POST, which is designed only to provide a quick check of the computer’s critical components does not even check that much. This means that there is still a good chance that a computer which passes both POST and diagnostics can still have a hardware defect which would cause problems with Warp or application programs.
POST first displays a count of installed memory as it performs a quick test of RAM. When that is complete you should see the total amount of RAM installed in your computer on the screen. Warp will recognize and utilize the amount of RAM displayed during the POST memory count.
If the memory count does not appear on the screen, it could be that the computer has been configured not to perform POST, the display is defective, or that the computer has a defective power supply. If the memory count does not show up on your screen, replace the display with one which is known to be good, and ensure that the computer has been configured to perform the POST. If that does not resolve the problem, replace the power supply.
POST then Initializes some interrupt vectors, and loads BIOS into RAM where it will perform better than if it were left in ROM. ROM is not only slower than RAM, but also it can only be read 8 bits at a time. When copied to RAM, BIOS can be read 16 bits at a time. Remember that POST is a 16 bit program, not 32 bit, and the processor is still in Real mode which means that it cannot execute 32 bit instructions or data transfers.
Next. the Power On Self Test scans for I/O adapters and links the adapter BIOS code into the system BIOS from ROM. It is at this time that you will see the BIOS code for your Adaptec SCSI adapter, for example, loaded. Hardware adpater conflicts may cause hangs during this part of POST. If you see the memory count and other messages on your screen during POST but POST never completes, you probably have an adapter conflict. You can tell that POST has not completed because the computer will not beep. If POST completes, whether successfully or with errors, it always produces one or more beeps.
After the BIOS code for the various I/O adapters has been integrated, the computer’s loadable ABIOS is refreshed, if it has one. Some computers have ABIOS, but many do not. Again, ABIOS is an Advanced BIOS which is capable of supporting a true multitasking operating system.
This normally completes POST and, if there were no errors detected, POST causes the computer to produce one short beep. Of course, if you observe any error messages on the screen, or POST beeps any other beep or combination of beeps, the computer has a hardware problem which should immediately be repaired.
Loading the Bootstrap program
The word “boot” is a short form of the term “bootstrap”. It refers to the fact that computers are really incapable of performing an action of any kind without a program of some sort to help them do it. Computers are so dumb that without some help they cannot even start themselves. To overcome this problem, designers have developed small programs called bootstrap loaders which enable it to “pull itself up by the bootstraps” and become smart enough to find and load the operating system. This bootstrap loader is located on the boot sector of the hard drive.
After POST completes with the “OK” beep, it issues an interruupt 19h to BIOS. BIOS interrup 19h searches for a boot sector on the disks specified in the computer’s boot sequence. Interrupt 19 loads into RAM the first boot sector found in the boot sequence into memory location 7C00h and transfers control of the computer to the bootstrap loader.
If no valid, bootable boot sector is found on any of the disks in the boot sequence, the boot sector contains a little bit of code which displays an error message. If the disk (or diskette) was formatted by OS/2, one of the following messages is presented.
OS/2 !! SYS01475
OS/2 !! SYS02027
or
The file OS2LDR cannot be found. Insert a system diskette and restart the system
If the disk (or diskette) was formatted by DOS, the following message is presented.
Non-System disk or disk error
Replace and strike any key when ready
All of these messages mean exactly the same thing: a valid boot record cannot be found. Refer to “OS/2 !! SYS01475 error message” for more details and how to recover.
If a valid boot block is found, it is loaded into RAM and control of the computer is turned over to it.
OS2BOOT Loads the operating system loader
The OS/2 Boot block finds and loads OS2BOOT. The Boot block is very small – only 16 blocks (sectors) in size. Because of this it is not very smart. One of the issues for the operating system at this time is that the HPFS drivers are not yet loaded, but the Boot block needs to be able to locate the OS2BOOT file on the HPFS volume. To enable this to happen, the Boot block uses a micro HPFS which enables it to locate files in the root directory. The boot block loads the OS2BOOT file.
OS2LDR loads the operating system
The OS2BOOT file and the OS2LDR both use a min-HPFS file system that is larger and more complex than the micro-HPFS file system used by the Boot block which can read the root, \OS2 and \OS2\BOOT directories. The OS2BOOT file loads the OS2LDR file and transfers control of the system to it.
OS2LDR in turn loads OS2KRNL which is the kernel of the operating system. Control is transferred to the OS2KRNL and the first OS/2 logo and copyright message is displayed during kernel initialization. The kernel is still using the mini-HPFS file system.
Processing the CONFIG.SYS file
The OS2KRNL reads the CONFIG.SYS file and begins processing of the statements in a combination sorted and native (unsorted) sequence. The CONFIG.SYS statements are processed in order shown below.
BASEDEV statements in the following order:
SYS
BID
VSD
TSD
ADD
I13
FLT
DMD
At this point, the OS/2 Logo changes to “Loading, please wait…”.
IFS statements are processed. The HPFS file system must be first IFS installed because the first one listed in the CONFIG.SYS file in native order replaces the mini-HPFS.
AUTOCHECK runs HPFS if the dirty file system flag is set.
DEVICE= statements processed.
CALL= statements processed.
RUN= statements processed.
Other statements are processed.
SET variable statements are processed.
All of the device drivers loaded at this time. A Trap error in a device driver can cause system hang at this point.
The program specified by PROTSHELL= statement is started. This is the Graphical Programming Interface which enables OS/2 to support a GUI workplace shell.
STARTUP.CMD and the Workplace Shell are started
The STARTUP.CMD is started if one is present.
If SET AUTOSTART=FOLDERS is specified in the CONFIG.SYS file, then theWorkplace Shell is started. All previously open folders will be opened. Desktop objects are displayed.
If SET AUTOSTART=PROGRAMS is specified in the CONFIG.SYS file, then Program objects which were open at shutdown are restarted.
If SET AUTOSTART=CONNECTIONS is specified, then network objects active at shutdown will be restarted. This may cause the logon prompt to be displayed.
The Boot process is complete
The system is now up and running under user control.

Boot Sector Example – by Jeff Weeks

Note: From OSRC, pulled from Wayback Machine
WRITING A BOOTSECTOR
(c)1997 Jeff Weeks and Code X software
Writting your own boot sector is probably actually easier then you think. All you really need to know is how the Intel processor boots up. A valid boot sector has the code 0xAA55 at an offset of 510, and is located in the very first sector of the disk. Therefore, the BIOS simply checks drive 0 (A:) for this code. If not found, it then checks drive 128 (C:). If a valid boot sector is found, it is loaded into memory at location 0:07C00h.
So, all you have to do is write a boot sector, assemble it into a plain binary file (their is no format or header to a boot sector), and write it to the first sector of your disk. The best way to do that would be to either use nasm (The netwide assembler can produce plain binary files) or assemble into a DOS .EXE and remove the first 512 bytes. You can also write your own program to write the bootsector to sector 1 of the disk using BIOS INT 13h AH=02h.
Pretty simple eh? Well, in case you’re still a little confused, here’s a little bootsector from PolyOS that simply switches to protected mode, after checking that you have a 386+ computer. Actually, it even loads in the PolyFS superblock and checks if it’s valid, but that’s about it. Soon it’ll load in the kernel and jump to it. The bootesctor was written with Nasm.
 

; ------------------------------------------------------------------------
; PolyOS boot loader code            (c)1997 Jeff Weeks of Code X Software
; ------------------------------------------------------------------------
; This little bit of assembly is the boot loader for my operating system.
; ------------------------------------------------------------------------
[BITS 16]       ; the bios starts out in 16-bit real mode
[ORG 0]
; ------------------------------------------------------------------------
; SECTOR ONE: THE BOOT LOADER
; ------------------------------------------------------------------------
; This sector detects your processor.  If a 386 is found, it loads the
; kernel from the disk and executes it (atleast it will in the future :).
; ------------------------------------------------------------------------
jmp start       ; skip over our data and functions
; -------------------------------------
; Data used in the boot-loading process
; ------------------------------------------------------------------------
        bootdrv         db 0
        bootmsg         db 'Booting PolyOS (c)1997 Cipher of Code X',13,10,0
        loadmsg         db 'Loading kernel',13,10,0
        jumpmsg         db 'Jumping to kernel',13,10,0
        rebootmsg       db 'Press any key to reboot',13,10,0
        ; these are used in the processor identification
        processormsg    db 'Checking for 386+ processor: ',0
        need386         db 'Sorry... 386+ required!',13,10,0
        found386        db 'Excellent!',13,10,0
        ; these are used when entering protected mode
        a20msg          db 'Setting A20 address line',13,10,0
        pmodemsg        db 'Setting CR0 -> Entering PMode',13,10,0
        ; Here's the locations of my IDT and GDT.  Remember, Intel's are
        ; little endian processors, therefore, these are in reversed order.
        ; Also note that lidt and lgdt accept a 32-bit address and 16-bit
        ; limit, therefore, these are 48-bit variables.
        pIDT            dw 7FFh         ; limit of 256 IDT slots
                        dd 0000h        ; starting at 0000
        pGDT            dw 17FFh        ; limit of 768 GDT slots
                        dd 0800h        ; starting at 0800h (after IDT)
; ------------------------------------------
; Functions used in the boot-loading process
; ------------------------------------------------------------------------
        detect_cpu:
                mov si, processormsg    ; tell the user what we're doing
                call message
                ; test if 8088/8086 is present (flag bits 12-15 will be set)
                pushf                   ; save the flags original value
                xor ah,ah               ; ah = 0
                push ax                 ; copy ax into the flags
                popf                    ; with bits 12-15 clear
                pushf                   ; Read flags back into ax
                pop ax
                and ah,0f0h             ; check if bits 12-15 are set
                cmp ah,0f0h
                je no386                ; no 386 detected (8088/8086 present)
                ; check for a 286 (bits 12-15 are clear)
                mov ah,0f0h             ; set bits 12-15
                push ax                 ; copy ax onto the flags
                popf
                pushf                   ; copy the flags into ax
                pop ax
                and ah,0f0h             ; check if bits 12-15 are clear
                jz no386                ; no 386 detected (80286 present)
                popf                    ; pop the original flags back
                mov si, found386
                call message
                ret                     ; no 8088/8086 or 286, so ateast 386
         no386:
                mov si,need386          ; tell the user the problem
                call message
                jmp reboot              ; and reboot when key pressed
;       ------------------------------------------------------------------
        message:                        ; Dump ds:si to screen.
                lodsb                   ; load byte at ds:si into al
                or al,al                ; test if character is 0 (end)
                jz done
                mov ah,0eh              ; put character
                mov bx,0007             ; attribute
                int 0x10                ; call BIOS
                jmp message
        done:
                ret
;       ------------------------------------------------------------------
        getkey:
                mov ah, 0               ; wait for key
                int 016h
                ret
;       ------------------------------------------------------------------
        reboot:
                mov si, rebootmsg       ; be polite, and say we're rebooting
                call message
                call getkey             ; and even wait for a key :)
                db 0EAh                 ; machine language to jump to FFFF:0000 (reboot)
                dw 0000h
                dw 0FFFFh
                ; no ret required; we're rebooting! (Hey, I just saved a byte :)
; -------------------------------------------
; The actual code of our boot loading process
; ------------------------------------------------------------------------
start:
        mov ax,0x7c0    ; BIOS puts us at 0:07C00h, so set DS accordinly
        mov ds,ax       ; Therefore, we don't have to add 07C00h to all our data
        mov [bootdrv], dl ; quickly save what drive we booted from
        cli             ; clear interrupts while we setup a stack
        mov ax,0x9000   ; this seems to be the typical place for a stack
        mov ss,ax
        mov sp,0xffff   ; let's use the whole segment.  Why not?  We can :)
        sti             ; put our interrupts back on
        ; Interestingly enough, apparently the processor will disable
        ; interupts itself when you directly access the stack segment!
        ; Atleast it does in protected mode, I'm not sure about real mode.
        mov si,bootmsg  ; display our startup message
        call message
        call detect_cpu ; check if we've got a 386
.386    ; use 386 instructions from now on (I don't want to manually include
        ; operand-size(66h) or address-size(67h) prefixes... it's annoying :)
        mov si,loadmsg  ; tell the user we're loading the kernel
        call message
        call getkey
read_me:
        ; first, reset the disk controller
        xor ax, ax
        int 0x13
        jc reboot       ; reboot on error
        ; then load in the PolyFS superblock
        mov ax,0x09000          ; superblock goes to 9000:0000 (above stack)
        mov es,ax
        xor bx,bx
        ; I could condense a few of these high/low 8-bit movs into one 16-bit
        ; mov, but, for simplicity, I'll leave it as is, unless necessary.
        mov ax,0x0202           ; load one block (two sectors)
        mov ch,0                ; cylinder = 0
        mov cl,3                ; sector = 2 (starts at sector 1 not 0)
        mov dh,0                ; head = 0 = side one
        mov dl,[bootdrv]        ; disk = what we booted from
        int 0x13                ; read it
        jc read_me              ; if there's an error then we'll try again.
                                ; Often there is not error but requires a few
                                ; tries.  Ofcourse, this may end up as an
                                ; infinite loop... but only on a bad disk...
        ; Check if we have a valid super block (BTW: ES still equals 0x9000)
        mov di, 0               ; offset of PolyFS magic signature
        mov si, polymagic       ; offset of PolyFS magic to check for (in ds)
        cmpsw                   ; compare ES:[DI] with DS:[SI]
        jnz reboot              ; reboot on error (otherwise, we've got a PolyFS)
	; Ideally, we'd load the kernel right here
        mov si, a20msg          ; tell the user we're setting the A20 line
        call message
        ; set A20 line
        cli                     ; no more interuptions! :)
        xor cx, cx
clear_buf:
        in al, 64h              ; get input from keyboard status port
        test al, 02h            ; test the buffer full flag
        loopnz clear_buf        ; loop until buffer is empty
        mov al, 0D1h            ; keyboard: write to output port
        out 64h, al             ; output command to keyboard
clear_buf2:
        in al, 64h              ; wait 'till buffer is empty again
        test al, 02h
        loopnz clear_buf2
        mov al, 0dfh            ; keyboard: set A20
        out 60h, al             ; send it to the keyboard controller
        mov cx, 14h
wait_kbc:                       ; this is approx. a 25uS delay to wait
        out 0edh, ax            ; for the kb controler to execute our
        loop wait_kbc           ; command.
        ; the A20 line is on now.  Let's load in our ITD and GDT tables...
        ; Ideally, there will actually be data in their locations (by loading
        ; the kernel)
        lidt [pIDT]
        lgdt [pGDT]
        ; now let's enter pmode...
        mov si, pmodemsg
        call message
        call getkey
        mov eax, cr0            ; load the control register in
        or  al, 1               ; set bit 1: pmode bit
        mov cr0, eax            ; copy it back to the control register
        jmp $+2                 ; and clear the prefetch queue
        nop
        nop
        ; jump to the kernel that we've loaded in...
        ; For now, we'll actually just reboot (this really doesn't
        ; work in protected mode, but it does reboot :)
        db 0xEA
        dw 0x0000
        dw 0xFFFF
        ; The boot sector is supposed to have to have 0xAA55 at the end of
        ; the sector (the word at 510 bytes) to be loaded by the BIOS...
        times 510-($-$) db 0
        dw 0xAA55