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