Two-sector FAT32 bootloader (Solved)

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
bigbob
Member
Member
Posts: 122
Joined: Tue Oct 01, 2013 2:50 am
Location: Budapest, Hungary
Contact:

Two-sector FAT32 bootloader (Solved)

Post by bigbob »

Hi,

Currently I am working on a two-sector(VBR1 and VBR2) FAT32 bootloader.
Since LBABegin of the partition is 32, VBR1 is in sector 32 and VBR2 is in sector 34 on the pendrive
(there was some data in sector 33, and writing VBR2 there didn't work). VBR2 is loaded into the memory address right after VBR1 (VBR1+512).

I have some difficulties in calling the functions in VBR2.

Code: Select all

; Simplified code of VBR1:
bits 16

org 0

VBR1_SEG			equ	0x07C0
VBR2_OFFSET			equ	0x200  			; VBR2 will be loader to 0x7E00
VBR2_SECTNUM		equ	32+2			; partition_lba_begin+2, so the second one of the ReservedSectors

; Function-offsets in VBR2
READFILE_OFFS		equ 0
CLUSTER2LBA_OFFS	equ 2

jmp main ;(3 bytes)

BPB data

main:
		; set segment registers to VBR1_SEG
		; ...

		; call a function in VBR2
		xor ecx, ecx
		mov cx, [VBR2_OFFSET+CLUSTER2LBA_OFFS]  ; DS:....
		call ecx
		; ...

Code: Select all

; Simplified code of VBR2
bits 16

org 0x7E00

readfile_offs		dw ReadFile		; address of Readfile used from VBR1
cluster2lba_offs	dw Cluster2LBA	; address of Cluster2LBA used from VBR1

; IN: EBX(clusternum)
; OUT: EAX
Cluster2LBA:
	; code for testing
	mov ax, 0
	mov ds, ax
	mov ax,0xb800
	mov gs, ax
	mov BYTE [gs:80*24*2], byte 'C'
	mov si, txtClus				; 'Clus', 0
	call Print2
	jmp $
		; ...

"call ecx" works in VBR1 ('C' is written in the bottom-left corner of the screen, and 'Clus' also gets printed), but
in the function of VBR2 I have to set the segment registers (e.g. DS) to zero (and restore at the end), otherwise data-referencing won't work in VBR2 (instead of 'Clus', trash will be printed).
It's necessary to get the BPB-data in VBR1 from VBR2, so in that case I have to restore the segment registers.
I have tried to set the origin of VBR2 to 0 and to 0x200 ("org 0" and "org 0x200") and adjust ecx in "call ecx" accordingly, but the call never arrived in the Cluster2LBA in VBR2.
For example with "org 0" I did:

Code: Select all

		xor edx, edx
		mov dx, VBR1_SEG
		shl edx, 4
		xor ecx, ecx
		mov cx, [VBR2_OFFSET+CLUSTER2LBA_OFFS]
		add ecx, edx
		call ecx
In my opinion it should be a "near call" in VBR1, so there should be no segment issues.
Of course "org" just adds its value to the address of the symbols.
In the beginning of VBR2, cluster2lba_offs was 0x00ED with "org 0" but 0x7EF3 with "org 0x7E00.
It works as it is now (org 0x7E00), but I believe it can be done in a more elegant way.

Any help appreciated.
Last edited by bigbob on Tue Apr 21, 2015 9:17 am, edited 1 time in total.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Two-sector FAT32 bootloader

Post by Combuster »

Since LBABegin of the partition is 32, VBR1 is in sector 32 and VBR2 is in sector 34 on the pendrive
(there was some data in sector 33, and writing VBR2 there didn't work). VBR2 is loaded into the memory address right after VBR1 (VBR1+512).
The FAT can follow immediately after the bootsector, and you probably noticed that. Unfortunately it's much longer than one sector, so by writing another part of your boot code into the third sector, you're actually overwriting the second sector of the FAT and permanently corrupting your filesystem.

FAT is very simple. One sector is sufficient to locate a file by its name within the file system and load that instead.

[edit]Various tools will add various amounts of reserved sectors by default, but only for FAT32. This trick might not work for everyone using FAT32, and most likely won't for FAT12/16. A quick search shows that there are many claims regarding the reserved sector count, and they are pretty much all misinformed to some extent or limited to one specific formatting utility.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Octocontrabass
Member
Member
Posts: 5588
Joined: Mon Mar 25, 2013 7:01 pm

Re: Two-sector FAT32 bootloader

Post by Octocontrabass »

Combuster wrote:By default, the FAT follows immediately after the bootsector,
Not FAT32. In FAT32, the sector immediately following the VBR is usually the FSInfo structure, which you shouldn't overwrite. If you used Windows to format the disk, next will be four free sectors, a backup boot sector, a backup fsinfo sector, and then 24 more free sectors before the FAT. Other utilities may lay things out differently, so pay attention to the values in the BPB.

Anyway, your problem is segmentation. With only a few exceptions that don't apply here, every instruction that references memory is using a segment. Your "call ecx" is really "call cs:ecx", and the odd behavior you're seeing is because you don't know what value is in CS. The code you wrote to "adjust" ECX for segmentation is also wrong, for the same reason: the CPU is already doing that.

Once all of the segment registers have the values you expect, things should start to make more sense.
bigbob
Member
Member
Posts: 122
Joined: Tue Oct 01, 2013 2:50 am
Location: Budapest, Hungary
Contact:

Re: Two-sector FAT32 bootloader

Post by bigbob »

Combuster wrote:FAT is very simple. One sector is sufficient to locate a file by its name within the file system and load that instead.
I know, that's the other option. I was told that I could use a few from the ReservedSectors.
Maybe I will write both a one-sector and a two-sector bootloader.
Octocontrabass wrote:Anyway, your problem is segmentation. With only a few exceptions that don't apply here, every instruction that references memory is using a segment. Your "call ecx" is really "call cs:ecx", and the odd behavior you're seeing is because you don't know what value is in CS. The code you wrote to "adjust" ECX for segmentation is also wrong, for the same reason: the CPU is already doing that.

Once all of the segment registers have the values you expect, things should start to make more sense.
Of course, segmentation is the problem and you are right about CS. I will keep playing with the code, but it would be good to know what the origin of VBR2 should be. It's important because it affects the offsets of the symbols.
Octocontrabass
Member
Member
Posts: 5588
Joined: Mon Mar 25, 2013 7:01 pm

Re: Two-sector FAT32 bootloader

Post by Octocontrabass »

bigbob wrote:I will keep playing with the code, but it would be good to know what the origin of VBR2 should be.
The origin should be its offset within the segment.

It will be loaded at linear address 0x7E00, so all you need to do is pick the value you'll have in your segment registers and solve for the offset that results in the correct linear address.

0x7E00 = 0111:6CF0 = 0444:39C0 = 0777:0690

Does that make sense?
bigbob
Member
Member
Posts: 122
Joined: Tue Oct 01, 2013 2:50 am
Location: Budapest, Hungary
Contact:

Re: Two-sector FAT32 bootloader

Post by bigbob »

Octocontrabass wrote:
bigbob wrote:I will keep playing with the code, but it would be good to know what the origin of VBR2 should be.
The origin should be its offset within the segment.

It will be loaded at linear address 0x7E00, so all you need to do is pick the value you'll have in your segment registers and solve for the offset that results in the correct linear address.

0x7E00 = 0111:6CF0 = 0444:39C0 = 0777:0690

Does that make sense?
Since the segment starts at linear 0x7C00, the offset of 0x7E00 is 0x200, if I am not mistaken.
Segment:offset addressing was a long time ago for me :)
bigbob
Member
Member
Posts: 122
Joined: Tue Oct 01, 2013 2:50 am
Location: Budapest, Hungary
Contact:

Re: Two-sector FAT32 bootloader

Post by bigbob »

After setting "org 0x200" in VBR2, and in VBR1 the following code:
xor ecx, ecx
mov cx, [VBR2_OFFSET+CLUSTER2LBA_OFFS]
call ecx

doesn't work. Garbage gets printed and then GRUB loads Linux.

If I add 0x7C00 to ecx then Cluster2LBA gets called with 'C' and 'Clus' printed. Without changing the segment registers in Cluster2LBA.
It is great, but I thought I understood CS:ECX.

EDIT:
Stackoverflow: "Change CS:IP in Assembly Language"
http://stackoverflow.com/questions/1246 ... y-language
Octocontrabass
Member
Member
Posts: 5588
Joined: Mon Mar 25, 2013 7:01 pm

Re: Two-sector FAT32 bootloader (Solved)

Post by Octocontrabass »

Well... I can't say for sure since you haven't posted all of your code, but it still sounds like you're never setting CS (or if you are, you aren't setting it to the correct value).
bigbob
Member
Member
Posts: 122
Joined: Tue Oct 01, 2013 2:50 am
Location: Budapest, Hungary
Contact:

Re: Two-sector FAT32 bootloader (Solved)

Post by bigbob »

Octocontrabass wrote:Well... I can't say for sure since you haven't posted all of your code, but it still sounds like you're never setting CS (or if you are, you aren't setting it to the correct value).
Ok, see the code below (the attached boot.asm and boot2.asm). CS can't be set directly. The functions in VBR2 always get called (with adding 0x7C00 to ecx), but in VBR2 there are still problems (now 'Clus' doesn't get printed).
In the meantime, I am working on the code of one-VBR, based on that of MenuetOS :oops:
Attachments
boot.asm
(8.06 KiB) Downloaded 92 times
boot2.asm
(6.22 KiB) Downloaded 108 times
Octocontrabass
Member
Member
Posts: 5588
Joined: Mon Mar 25, 2013 7:01 pm

Re: Two-sector FAT32 bootloader (Solved)

Post by Octocontrabass »

Code: Select all

mov sp, 0xFFFF
Why do so many people make this mistake? You want SP to be zero.

Anyway, you still aren't setting CS. That's why it's not working. The wiki lists a few ways you can do it. Any of those can get the job done, but a far jump is the easiest:

Code: Select all

    jmp VBR_SEG:label
label:
Once you've fixed that, things should start working correctly.
bigbob
Member
Member
Posts: 122
Joined: Tue Oct 01, 2013 2:50 am
Location: Budapest, Hungary
Contact:

Re: Two-sector FAT32 bootloader (Solved)

Post by bigbob »

Octocontrabass wrote:Anyway, you still aren't setting CS. That's why it's not working. The wiki lists a few ways you can do it. Any of those can get the job done, but a far jump is the easiest:

Code: Select all

    jmp VBR_SEG:label
label:
Once you've fixed that, things should start working correctly.
Yes, with adding the jump, "call ecx" works without adding the segment-value.
That's good, but still only 'C' gets printed in the bottom-left corner, and 'Clus' doesn't. Then it freezes.
So Cluster2LBA in VBR2 gets called but maybe the offset of txtClus is wrong.
Even without the jump and with adding the segment-value to "call ecx" 'Clus' is not shown anymore.
I have never seen this jump before in tutorials (or MenuettOS' boot-code), probably because they didn't need it.

EDIT: It seems that there was an older version of VBR2 on the pen-drive. Now 'Clus' gets printed, sorry :oops:
Octocontrabass wrote:

Code: Select all

mov sp, 0xFFFF
Why do so many people make this mistake? You want SP to be zero.
I must disagree. It's from a tutorial, and MenuetOS also sets the stack-pointer to the bottom (the high-end) of the stack:
http://www.menuetos.org/appl/bootmf32.asm
From MenuetOS:
; 0x2000:0xfff0 - stack set at start
mov sp,0xfff0
The stack-pointer moves from higher memory-addresses to lower ones. If I am not mistaken. It's a stack.
Octocontrabass
Member
Member
Posts: 5588
Joined: Mon Mar 25, 2013 7:01 pm

Re: Two-sector FAT32 bootloader (Solved)

Post by Octocontrabass »

bigbob wrote:I have never seen this jump before in tutorials (or MenuettOS' boot-code), probably because they didn't need it.
Most bootloaders don't use any absolute jumps. Relative jumps will (usually) work correctly even if CS is not the expected value.
bigbob wrote:I must disagree. It's from a tutorial, and MenuetOS also sets the stack-pointer to the bottom (the high-end) of the stack:
Most tutorials are fine examples of cargo cult programming: no one knows how it works, but it must be right because that's how everyone else does it. MenuetOS at least aligns the stack, but still wastes 16 bytes of potential stack space for no reason.
bigbob wrote:The stack-pointer moves from higher memory-addresses to lower ones. If I am not mistaken. It's a stack.
There are two ways to push a value to a stack:
  1. Store the value at the address in the stack pointer, then decrement the stack pointer.
  2. Decrement the stack pointer, then store the value at the address in the stack pointer.
The x86 architecture follows option B. When you push a word-sized value to the stack when SP is 0, SP is decremented (SP becomes 0xFFFE) and then the value is stored to SS:SP (SS:0xFFFE).
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Two-sector FAT32 bootloader (Solved)

Post by Combuster »

I must disagree. It's from a tutorial
"Don't trust tutorials" is covered in chapter one of the Beginner Mistakes. This particular item is explicitly mentioned in the FAQ item on My Bootloader Does Not Work
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply