Page 1 of 1

Issues loading VBR into memory in MBR using int 13h

Posted: Thu Jan 26, 2017 10:08 pm
by ericscollins
Hello folks,

I'm attempting to implement a basic MBR, and everything seems to be going fine until I attempt to load the VBR from the active partition into memory. The VM hangs when calling int 31h function code 42. I have a feeling its in the construction of my DAP, and is likely due to my shaky understanding of the fine details little-endian formatted values. Thanks for taking a look!

Code: Select all

	BITS 16
	ORG 0x7C00
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Constants:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	%define NEW_OFFSET 0x0600
	%define OLD_OFFSET 0x7C00
	%define MBR_SIZE 512
	%define PARTITION_TABLE_OFFSET 446
	%define PARTITION_ENTRY_SIZE 16
	%define ENTRY_COUNT 4
	%define BOOTABLE_BIT_MASK 0x80
	%define ENTRY_CYLINDER_OFFSET 3
	%define ENTRY_HEAD_OFFSET 1
	%define ENTRY_SECTOR_OFFSET 2
	%define LBA_OFFSET 0x08
	%define DAP_LOC 0x800
	%define DAP_SIZE 0x10
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PreCopy:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DisableInterrupts:
	cli
	
SetupStack:
	mov ax, 0
	mov ss, ax		; Stack segment SS:SP is 0
	mov sp, OLD_OFFSET	; bottom of stack (grows downwards) starts just below code

InitSegRegs:			; Initialize all segment registers to zero
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	
RelocateMBR:	
	xor ax, ax		; Zero ax for segment register zeroing

	mov es, ax		; Set copy location to 0:0x0600
	mov di, NEW_OFFSET

	mov ds, ax		; Set origin location to 0:0x7C00
	mov si, OLD_OFFSET

	mov cx, MBR_SIZE	; Copy all 512 bytes of MBR

	rep movsb		; Copy MBR one byte at a time
	
	jmp 0:PostCopy		; Jump to copied MBR after copy code

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PostCopy:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	
ReenableInterrupts:
	sti

ReadPartitionTable:
	mov [bootDrive], dl				; save boot drive from bios
	mov bx, NEW_OFFSET + PARTITION_TABLE_OFFSET	; mov table addr into bx
	mov cx, ENTRY_COUNT				; 4 partitions to test
	
.bootableScan:
	mov al, [bx]					; get status byte
	test al, BOOTABLE_BIT_MASK			; test if its bootable
	jnz LoadPartition				; bootable -> load vbr
	add bx, PARTITION_ENTRY_SIZE			; go to next partition
	loop .bootableScan
	
NoBootFound:
	int 0x18					; Report not bootable to bios and return control
	
LoadPartition:

;;; Check if bios supports lba reading from disk
.checkExtension:
	push bx						; we need the partition entry address, save it
	mov ah, 0x41
	mov bx, 0x55AA
	mov dl, [bootDrive]
	int 0x13

	pop bx						; restore partition entry address
	jc .chsLoad					; unsupported -> load via chs
	jmp .lbaLoad					; supported -> load via lba

.lbaLoad:
	mov si, DAP_LOC
	mov [si], BYTE DAP_SIZE				; 0-1 size of dap (16)
	inc si

	mov [si], BYTE 0x00				; 1-2 0
	inc si

	mov [si], WORD 0x0001				; 2-4 number of sectors to read (1)
	add si, 2

	mov [si], WORD OLD_OFFSET 			; Offset to read to (0x7c00)
	add si, 2

	mov [si], WORD 0x0000				; Segment to read to (0)
	add si, 2

	
	mov cx, WORD [bx+LBA_OFFSET]
	mov [si], cx
	add si, 2
	mov cx, WORD [bx+LBA_OFFSET+2]
	mov [si], cx
	add si, 2
	mov [si], DWORD 0x00000000

	mov ah, 0x42
	mov dl, [bootDrive]
	mov si, DAP_LOC
	int 0x13

	jnc VBRHandoff

	
.chsLoad:	
	mov ch, [bx + ENTRY_CYLINDER_OFFSET] 		; Cylinder number
	mov cl, [bx + ENTRY_SECTOR_OFFSET]   		; Sector Number
	mov dh, [bx + ENTRY_HEAD_OFFSET]     		; Head Number
	mov dl, [bootDrive]				; drive number

	xor bx, bx
	mov es, bx					; Segment to write to (0)
	mov bx, OLD_OFFSET				; offset to write to (0x7c00)

	mov ah, 0x02					; function code
	mov al, 1					; sectors to read

	int 0x13					; read from disk

	jmp VBRHandoff					; Hand off control to loaded vbr
	
	
VBRHandoff:
	mov dl, [bootDrive]				; Restore boot drive for vbr 
	jmp OLD_OFFSET					; Jump to vbr code
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	
;;; String address in si
PrintString:
	push ax
	push si
.printloop:
	mov al, [si]
	inc si
	test al, al
	jz .done
	call PrintChar
	jmp .printloop
.done:
	pop si
	pop ax
	ret

	
;; Char in al
PrintChar:
	push ax
	push bx
	
	mov ah, 0x0E
	mov bh, 0
	int 0x10

	pop bx
	pop ax
	
	ret

;;; Assume byte in ah
PrintHex:
	push ax
	push bx

	xor bx, bx
	
	mov bl, ah
	and bl, 0xF0
	shr bl, 4
	mov al, [hexstring + bx]
	call PrintChar

	mov bl, ah
	and bl, 0x0F
	mov al, [hexstring + bx]
	call PrintChar

	pop bx
	pop ax
	
	ret

PrintNL:
	push si

	mov si, NL
	call PrintString
	
	pop si
	ret

	
;;; Reg is ax
PrintReg:
	push ax


	call PrintHex
	mov ah, al
	call PrintHex

	
	pop ax
	ret
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Data:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


	bootDrive db 0
	hexstring db "0123456789ABCDEF"
	NL db 0x0A,0x0D,0
	
InLBA:		db "In Lba",0x0A,0x0D,0
HERE:		db "HERE",0x0A,0x0D,0
	
;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	

 times 446 - ($ - $$) db 0

Re: Issues loading VBR into memory in MBR using int 13h

Posted: Fri Jan 27, 2017 2:55 am
by Brendan
Hi,

Code: Select all

	ORG 0x7C00

Code: Select all

	jmp 0:PostCopy		; Jump to copied MBR after copy code
The assembler thinks that "PostCopy" is at 0x7C?? and not 0x06??, so the jump does nothing (except reload CS) and when you successfully load the sector you overwrite the code you're currently running and crash when the BIOS function returns to "corrupted caller".

Note: I'd strongly recommend using "org 0x0600".


Cheers,

Brendan

Re: Issues loading VBR into memory in MBR using int 13h

Posted: Fri Jan 27, 2017 4:25 am
by Antti
@ericscollins: I would say your code is a very good start. I did not read it through but I listed some notes:
  • "mov ax, 0x0000" can usually be replaced with "xor ax, ax" unless FLAGS needs to be preserved.
  • Avoid using redundant instructions , e.g. "xor ax, ax" if register ax was zero already.
  • Make it explicit that your code requires 80386 or newer (see "mov fs, ax" instruction).
  • Clear direction flag before "rep movsb" (see "cld" instruction).
  • Mandatory: handle "read from disk" errors (see "jc my_error_handler").
Brendan wrote:when you successfully load the sector you overwrite the code you're currently running and crash when the BIOS function returns to "corrupted caller"
For practitioners of code acrobatics, this can also result in relatively defined behavior. :D

Re: Issues loading VBR into memory in MBR using int 13h

Posted: Fri Jan 27, 2017 5:43 am
by Antti
This is totally off-topic but I need to share this:

Code: Select all

0x0000:0x????       mov word [0x7BFE], 0x13CD
...
0x0000:0x????       <set int 0x13 parameters>
0x0000:0x????       jmp 0x7BFE
...
...
0x0000:0x7BFE       int 0x13        ; assume "atomic" success or fail
0x0000:0x7C00       ?? ??           ; old code (endless loop) or new code (continue)
...
Interesting. Would be possible to get rid of relocation and save some bytes?

Re: Issues loading VBR into memory in MBR using int 13h

Posted: Fri Jan 27, 2017 6:52 am
by ericscollins
@Brendan,
Brendan wrote:Note: I'd strongly recommend using "org 0x0600"
Changing the ORG to 0x0600 did the trick! I can now successfully jump to the selected partition's VBR. Thank you!

I had very little understanding of what ORG did, so just reading the brief description of its function in NASM's documentation, it made sense to put 0x7C00 there. Now, based on hexdumps of my binaries, I'm assuming the assembler uses the ORG value to resolve label addresses at assemble-time? Which would certainly explain my issue before.

@Antti

Thank you for your advice! I will certainly take those pointers into account, especially the final three. The two regarding zeroing out a register were artifacts of debugging code that used ax spliced into existing code.