Page 1 of 4

[SOLVED] Bootloader fails on a real pc

Posted: Sun May 08, 2011 4:38 pm
by Bietje
Hello there,

I'm developping a bootloader and everythings goes quit well. I use bochs or kvm to test. Today I tried to boot from a real computer, and that failed. I used an USB harddrive (pendrive) and my first sector gets loaded perfectly the disk reader doesn't show errors either but when i try to far jump it fails.

I can't figure out why it works with bochs or kvm but fails with a real pc..

My first stage:

Code: Select all

[BITS 16]
[ORG 0x7C00]
main: ; entry point
	mov [bootdisk], dl
	mov si, booted
	call println

	call loadimage
	shr ax, 8
	or al, al
	jz .loaded

.bailout:
	mov si, failed
	call println
	xor ax, ax
	int 0x16
	int 0x19
	cli
	jmp $

.loaded:
	mov dl, [bootdisk]
	jmp 0x7C0:0x200
	mov si, failed
	call println
	cli
	jmp $

;
; Image loader
;

%include 'boot/x86/stage1/loadimage.asm'

;
; Print routines
;

%include 'boot/x86/println.asm'

;
; Since flat binary is one big heap of code without sections, is the code below some sort of data section.
;

	bootdisk db 0
	booted db 'GEBL has been loaded by the bios! Executing...', 0x0
	failed db '(0x0) Failed to load the next stage.. ready to reboot. Press any key.', 0x0

times 510 - ($ - $$) db 0
dw 0xAA55
loadimage.asm

Code: Select all

loadimage:
	
.checkextensions:
	mov ah, 0x41	; check ext
	mov dl, [bootdisk]	; HDD0
	mov bx, 0x55AA
	int 0x13
	jc .checkextensions

.extread:
	mov ah,0x42
	mov dl,[bootdisk]
	lea si,[lbar]        
	int 0x13
	jnc .return

.oldway:
	xor ah, ah ; function 0 = reset
	mov dl, [bootdisk]
	int 0x13
	jc .oldway

.oldload:
	mov bx, 0x7C0	; segment
	mov es, bx
	mov bx, 0x200	; offset

	mov ah, 0x2					; function 2
	mov al, 0x1					; read 1 sector
	xor ch, ch					; track
	mov cl, 0x2					; sector to read
	xor dh, dh					; head number
	mov dl, [bootdisk]					; drive number
	int 0x13					; call BIOS - Read the sector

.return:
	ret
;
; LBA register
;
; Loading a sector with this seg:off will place it right after the mbr
;

lbar:
	db 0x10      	; register size
	db 0      	; reserved, must be 0
	dw 0x1      	; sectors to read
	dw 0x200   	; memory offset
	dw 0x7C0   	; memory segment
	dq 0x1		; starting sector (sector to read)
And sector 2 (which will be read)

Code: Select all

main:
	mov [diskid], dl

	call enable_A20
	jnc  .loadstage2
	mov si, a20fail

.bailout:
	call println
	xor ah, ah
	int 0x16
	int 0x19
	cli
	jmp $

.loadstage2:
	mov si, a20ok
	call println
	
	call getmemorymap
	mov si, a20fail
	jc .bailout

	mov ax, word [mmr+4]
	or ax, ax
	jz .bailout		; we are not here to bully our user with al zero-length memory map.

	call dynamicloader
	shr ax, 8
	or al, al
	mov si, nostage2
	jnz .bailout

	jmp 0x7E0:0x400

	jmp .bailout

;
; Dynamic disk reader
;

%include 'boot/x86/stage1/stage1.5/dynamicloader.asm'

;
; Memory map
;

%include 'boot/x86/stage1/stage1.5/getmemorymap.asm'

;
; A20 Gate
;

%include 'boot/x86/stage1/stage1.5/enable_A20.asm'

;
; Print routines
;

%include 'boot/x86/println.asm'
	
	diskid db 0
	a20ok db 'The A20 line has been enabled.', 0x0
	a20fail db '(0x1) The A20 gate couldn`t be opened. Press a key to reboot.', 0x0
	nostage2 db '(0x2) Failed to load the second stage.. Press a key to reboot.', 0x0

times 1024 - ($ - $$) db 0
Does any of you see, why the far jump does what I want in real mode but does not in a real PC. Oh and my linker makes sure the second stage is at 0x7E00

Code: Select all

SECTIONS
{	
	.boot 0x7E00:
	{
		*(.stage1)
		*(.stage2)
		*(.pmode)
	}
	.text :
	{
		*(.text)
	}
	.rodata :
	{
		*(.rodata)
	}
	.data :
	{
		*(.data)
	}
	.bss :
	{
		*(.bss)
	}
	.end ALIGN(2) :
	{
		*(.end)
	}
}
It just hangs there after displaying the first message.. why kvm loads the entire bootloader..

Thanks in advance.

Re: Bootloader fails on a real pc

Posted: Sun May 08, 2011 6:15 pm
by robos
You need to set up a stack and your DS & ES registers. You have no idea now where the stack is, how large it is or where "mov [bootdisk], dl" gets written to

Re: Bootloader fails on a real pc

Posted: Mon May 09, 2011 12:43 am
by Bietje
I also thought of that.. So should I set my data and es segment registers to 0? And do I have to that in stage 1 ánd stage 1.5 or just in stage 1?

So:

Code: Select all

xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7BFE
mov bp, sp
still doesn't work at all..

Re: Bootloader fails on a real pc

Posted: Mon May 09, 2011 10:47 am
by Bietje
So.. I have found the error. I thought my bios recognizes my usb as harddisk so I used 0x80 in dl in the disk reader. Now I have another problem.. what number should I use in dl? 0x0 for a floppy also fails :|

Re: Bootloader fails on a real pc

Posted: Mon May 09, 2011 1:11 pm
by Firestryke31
Use [bootdisk]. That's the whole reason it exists, isn't it?

Re: Bootloader fails on a real pc

Posted: Mon May 09, 2011 2:29 pm
by Bietje
If I use mov [bootdisk], dl it triple faults (I tested on my main pc and it spams the first bootmessage of stage 1 10000 milion times)..
And if I do not use the disk number supplied by the bios in dl but use a static value like 0x0 for floppy or 0x80 it doesn't load the correct sectors..

Re: Bootloader fails on a real pc

Posted: Mon May 09, 2011 5:35 pm
by DavidCooper
Bietje wrote:If I use mov [bootdisk], dl it triple faults (I tested on my main pc and it spams the first bootmessage of stage 1 10000 milion times)..
And if I do not use the disk number supplied by the bios in dl but use a static value like 0x0 for floppy or 0x80 it doesn't load the correct sectors..
Where is that going to in memory? 0? It looks as if it's probably messing up the first of the BIOS's interrupt vectors, and as that one gets triggered 18 times a second you're going to get a crash straight away. You need to set up the segment registers right at the start and pick an address to store the drive number where it isn't going to cause any harm.

Bochs probably doesn't bother to simulate real mode interrupts - it certainly doesn't appear to update the system timer variable in the BDA, so you're probably using memory you shouldn't and getting away with in Bochs by luck.

Re: Bootloader fails on a real pc

Posted: Mon May 09, 2011 10:37 pm
by CelestialMechanic
Bietje wrote:{Snip!}

Code: Select all

xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7BFE
mov bp, sp
still doesn't work at all..
Where did you place this code? It should be after your main: label and before mov [bootdisk],dl.

It might also be a good idea to place a far jump like this first thing after main and before the snippet quoted above just in case your BIOS/emulator jumps to 07C0:0000 instead of 0000:7C00 after loading the boot sector.

Code: Select all

main:
jmp 0000:main1
main1:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7BFE
mov bp, sp
mov [bootdisk],dl

Re: Bootloader fails on a real pc

Posted: Tue May 10, 2011 5:48 am
by Bietje
DavidCooper wrote:
Bietje wrote:If I use mov [bootdisk], dl it triple faults (I tested on my main pc and it spams the first bootmessage of stage 1 10000 milion times)..
And if I do not use the disk number supplied by the bios in dl but use a static value like 0x0 for floppy or 0x80 it doesn't load the correct sectors..
Where is that going to in memory? 0? It looks as if it's probably messing up the first of the BIOS's interrupt vectors, and as that one gets triggered 18 times a second you're going to get a crash straight away. You need to set up the segment registers right at the start and pick an address to store the drive number where it isn't going to cause any harm.

Bochs probably doesn't bother to simulate real mode interrupts - it certainly doesn't appear to update the system timer variable in the BDA, so you're probably using memory you shouldn't and getting away with in Bochs by luck.
So what you are suggesting is that I should not reset my data segments to 0?

Re: Bootloader fails on a real pc

Posted: Tue May 10, 2011 10:05 am
by Rusky
You do need to set your data segment to something known. Because of the org 0x7c00, you should probably just set it to 0. It should look something like this:

Code: Select all

cli

; initialize segment registers
xor ax, ax
mov ds, ax
mov es, ax

mov ss, ax
mov sp, 0x7c00

sti

mov [bootdisk], dl
; do stuff
; load from drive [bootdisk]

Re: Bootloader fails on a real pc

Posted: Tue May 10, 2011 10:20 am
by DavidCooper
Bietje wrote:So what you are suggesting is that I should not reset my data segments to 0?
Actually I probably jumped the gun - I'll have a proper look at your code and not just assume it's as bad as it looked at first sight...

Setting the segments to 0 at the start is fine, by the way.
booted db 'GEBL has been loaded by the bios! Executing...', 0x0
Is this the message that appears 10000 million times? And where's the code that prints it out? All I can see is the call:-

Code: Select all

call println
and this:-

Code: Select all

;
; Print routines
;

%include 'boot/x86/println.asm'
No one's going to want to analyse the rest of your code carefully until they're sure there's no bug in your print routine.

Re: Bootloader fails on a real pc

Posted: Tue May 10, 2011 11:59 am
by Bietje
Hi,

I have fixed the spamming on of the message.
booted db 'GEBL has been loaded by the bios! Executing...', 0x0
Don't ask me how, it just was, after I did a few tests.

I use the following println routine:

Code: Select all

println:
	lodsb
	or al, al
	jz .return	; null byte found
	mov ah, 0x0E
	xor bh, bh	; page 0
	int 0x10
	jmp println

.return:
	mov al, 0x0A 	; new line
	mov ah, 0x0E
	xor bh, bh
	int 0x10

	mov al, 0x0D	; carrage return
	mov ah, 0x0E
	xor bh, bh
	int 0x10
	ret
You can find my entire project here.

I really apriciate your help!

I found an old hard disk and I'm going to try that now, maby it is just that it won't boot with and USB.

Greetings,
Bietje

edit:
When I try to read and load sector one, every thing is ok.. it just spams the welcome message. That was probaly the bug why it spammed earlier. When I try to read sector 2 it juust hangs..

Re: Bootloader fails on a real pc

Posted: Tue May 10, 2011 1:49 pm
by DavidCooper
Try this. If I'm understanding you correctly, everything's fine up to:-

Code: Select all

   jmp 0x7C0:0x200
I'd like you to add a bit of code just before that just to make sure, and also to add a bit of code to the start of the second sector to do the same thing. I want you to set ES to B800, DI to 0, AL to 65 and AH to 10, then send the contents of AX (a green capital A) to the top left corner of the screen with a stosw instruction. Do that just before the far jump. At the start of the second sector I want you to set AL set to 66 and to do another stosw - this should print a capital B next to the A if the second sector has loaded.

Also, is there a disk image available for me to look at - I prefer to read the machine code directly.

Re: Bootloader fails on a real pc

Posted: Tue May 10, 2011 2:40 pm
by Bietje
Did you realized you may not use video memory in real mode?

But I moved the location of my welcome message to just before the jump, and that one does print correctly. But the print at the start of stage 2 does not print.. Even if I do not work with calls (and thus.. not with the stack), but move the code into my main routine..

I made a test project, which should just load a sector and execute it (just to keep overview, my main project does to many other things).

Sources of test project (were the jump fails as hard as with the bigger project) below. This one also has great success with emulators but great failure with real pc's and bioses.. Also there is a zip package added with source + flat bin executable so you can make your own hexdumps and stuff.

entry point

Code: Select all

[ORG 0x7C00]
[BITS 16]

_start: ; entry point
	cli
	jmp 0x0:.flush
.flush:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, 0x7BFE
	mov [bootdisk], dl
	sti


.reset:
	xor ax, ax
	mov dl, [bootdisk]
	int 0x13
	jc .reset

.read:
	mov ah, 0x2
	mov al, 1 ; read 1 sector
	xor cx, cx ; track 0
	mov cl, 2 ; read sector 2
	xor dx, dx ; head 0
	mov dl, [bootdisk]
	
	; setup buffer
	xor bx, bx
	mov es, bx
	mov bx, 8000h ; offset 8000 hex
	int 0x13
	jc .read
	or ah, ah
	jnz .reset

	; print a character
	mov al, 65
	mov ah, 0x0E
	xor bh, bh	; page 0
	int 0x10
	jmp 0x0:8000h

	bootdisk db 0

times 510 - ($ - $$) db 0
dw 0xAA55
stage2

Code: Select all

[ORG 0x8000]
[BITS 16]

	mov al, 66
	mov ah, 0x0E
	xor bh, bh	; page 0
	int 0x10

	jmp $

times 512 - ($ - $$) db 0

Re: Bootloader fails on a real pc

Posted: Tue May 10, 2011 3:41 pm
by DavidCooper
Bietje wrote:Did you realized you may not use video memory in real mode?
Do you realise that you can use video memory in real mode? I suggested that you do it that way to avoid extra possible register corruptions being introduced by the BIOS.
But I moved the location of my welcome message to just before the jump, and that one does print correctly. But the print at the start of stage 2 does not print.. Even if I do not work with calls (and thus.. not with the stack), but move the code into my main routine..
So it looks as if it isn't loading. Another way to make absolutely sure though would be to put a jump in at the start of the second sector and follow it with some ASCII. You can then add code before the far jump in the boot sector to read from the addresses where the ASCII should be and just copy them directly to the screen to see if they are there.
I can't attatch the binary image because it says the extension is not allowed, whatever extension I use..
In that case I can't check your code properly because I don't know assembler well enough to know exactly how it gets converted into the machine code that I understand.

Perhaps you could add some more code befor the far jump to collect the drive number from the place you've stored it in (I'm talking about the number passed to your code in DL by the BIOS) - you can then post it to the screen by putting it in AL with AH=10 (that's a decimal ten, by the way, as were the 65 and 66 I mentioned before for the capital A and B - you may want to convert them to hex) and then send AX directly to the screen as described earlier. You might then see a strange symbol on the screen, but just do a screen grab of it and I'll tell you what number value it must be. In case it's blank, inc AX and post it to the screen again two bytes further on in screen memory so that it turns into something visible.