Page 1 of 1

Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 7:35 pm
by alethiophile
I have written a function to load FAT meta-data to memory from the hard disk, for a bootloader. In this function, I have a div instruction to get the size in sectors of the rootdir, knowing its size in bytes. This div instruction freezes the bootloader. The function follows:

Code: Select all

;; Method: load_FAT_data
;;--------------------------------------
;; di => where to load
;;--------------------------------------
;; Loads the boot sector, FATs, and root directory
;; of the FAT filesystem on disk 0x80 to di. di must
;; point to enough space. Returns the number of sectors
;; read.
load_FAT_data:
	push	ebp
	mov	ebp,	esp
	sub	esp,	4	;; space for return value
	pusha
	push	fs

lfd_read_bpb:
	xor	ecx,	ecx
	mov	bx,	di	;; offset to read to
	xor	eax,	eax	;; reading the first sector
	mov	fs,	ax	;; no segment variable
	mov	cx,	1	;; only reading bpb sector
	call	read_sectors
	add	word [ebp - 4],	cx	;; for return value

lfd_read_FATs:
	mov	ax,	word [di + 22]	;; sectors per FAT
	mul	byte [di + 16]	   	;; number of FATs
	mov	cx,  	ax 		;; that's how many sectors we need load
	
	mov	ax,	word [di + 14]	;; FATs start right after the reserved sectors
	
	add	bx,	0x200		;; read to sector after bpb
	call	read_sectors		
	add	word [ebp - 4],	cx	;; for return value

lfd_read_rootdir:
	add	ax,	cx		;; rootdir is right after FATs, so add number just loaded
	push	ax			;; save ax so we can use it for calculations
	
	mov	cx,	ax		;; num sectors loaded last
	mov	dx,	0x200		;; size of a sector
	mul	dx			;; number of bytes loaded last
	add	bx,	ax		;; add to memory index
	
	mov	ax,	word [di + 17]	;; # of rootdir entries
	mov	dx,	0x20 	   	;; 32-byte entry
	mul	dx
	mov	edx,	0x200		;; # of bytes per sector
	div	edx ;; <- this is the instruction that freezes it
	mov	cx,	ax

	pop	ax
	call	read_sectors
	add	word [ebp - 4],	cx	;; return value
	
	pop	fs
	popa
	mov	ax,	word [ebp - 4]	;; return value
	mov	esp,	ebp
	pop	ebp
	ret
I have stepped through the function in Bochs debugger and verified that this instruction is the problem. The bochs register dump immediately before the instruction is:

Code: Select all

eax: 0x00004000 16384
ecx: 0x00000080 128
edx: 0x00000200 512
ebx: 0x00009e00 40448
esp: 0x00009be1 39905
ebp: 0x00009bf9 39929
esi: 0x00008ea5 36517
edi: 0x00009c00 39936
eip: 0x00008d95
eflags 0x00000206
id vip vif ac vm rf nt IOPL=0 of df IF tf sf zf af PF cf
This seems like it contains the correct values. I also have some similar code in my first-stage bootloader, for the same purpose, and it works well. Does anyone have an idea as to the problem?

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 7:49 pm
by alethiophile
Update: I now have replaced the divide-by-512 with a right-shift by 9, and it works. Hence, this is less urgent. I'm still curious, however, about why the div would cause the loader to freeze.

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 9:05 pm
by mintie
Could your read_sectors function depend upon the value in edx being 0x200?

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 9:10 pm
by alethiophile
No; read_sectors only uses edx internally, and preserves its value. No assumption is made about its contents.

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 9:43 pm
by mintie
If I remember this right, when you do a 32 bit divide with edx, it divides edx:eax by edx, so you're actually dividing 0x02004000 by 0x200 instead of doing 0x4000/0x200.
Try replacing it with

Code: Select all

mov edx, 0
mov ecx, 0x200
div ecx
Or something similar depending on what registers you use.

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 9:56 pm
by alethiophile
OK, thanks. I'm now using a right shift instead of a div, but it's good to know why that failed.

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 9:59 pm
by mintie
You're welcome, and besides it's better to use that right shift, it's way quicker than a div :P

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 10:58 pm
by Masterkiller
Dividing on a 32-bit value (register or memory) divides value of EDX:EAX (forming 64-bit number) to that 32-bit value.
For example if edx=0x200; eax=0x200; your division will do: 0x0000020000000200/0x00000200 = 0x100000001 | 0; Quitient as you see is 33-bit value and it is stored in 32-bit register EAX, the remainder is stored in EDX. Since you cannot store 33-bit value into 32-bit register, instruction will fire an exception. Exception in real-mode are handled, but causes the program to freeze. (For example Bochs execute IRET an restart the DIV instruction, then again CPU fires an exeption and instruction is restarted again, again and again).
You should always keep the divident in EDX:EAX, or if it is 32-bit, zero the edx before division.

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 11:03 pm
by mintie
Whoops, thanks haha, I just realized I mean to put 0x0000020000004000 instead of 0x02004000. That was from me making sure the same thing happened with 16-bit registers, and i forgot it was with 32-bit registers in that code.

Re: Bootloader freezes on div instruction

Posted: Wed Jun 03, 2009 11:31 pm
by kop99
Instead of div, you can use shr command.
It's better good than div's performance.

Code: Select all

shr eax, 0x9

Re: Bootloader freezes on div instruction

Posted: Thu Jun 04, 2009 8:52 am
by quok
kop99 wrote:Instead of div, you can use shr command.
It's better good than div's performance.

Code: Select all

shr eax, 0x9
The problem here though is that using shr is completely and totally wrong. The OP wants to get the size of the FAT root directory in sectors. The BPB contains a field that holds the size of a sector in bytes. That is what you want to use. It is almost always 512, but it may not be, so coding in a 9 bit right shift is definitely not the right way to go.

shr may be faster than div (although div is heavily optimized for using powers of 2), but converting a sector size that is already in bytes to the proper number of bits to shift by is non-trivial at best. Especially in a bootsector where you're already quite constrained on available space. Besides, it's a bootsector and you're only doing this calculation once. This is not the right place for speed optimizations. :)

Re: Bootloader freezes on div instruction

Posted: Thu Jun 04, 2009 6:52 pm
by kop99
it may not be, so coding in a 9 bit right shift is definitely not the right way to go.
Maybe your advice is right.
But I naver see that sector size isn't 512byte...

Re: Bootloader freezes on div instruction

Posted: Thu Jun 04, 2009 7:54 pm
by alethiophile
For one thing, this is a second-stage bootloader, so I'm not as constrained on space. But this will only fail on filesystems that are formatted really weirdly, and since I'm using this to parse a filesystem that I'm formatting, I don't see a problem with using a hard-coded value for div (or shr). Non-512-byte sectors might be a concern in actual OS drivers, but not for the bootloader.