Page 1 of 1
Two-stage bootloader issue
Posted: Mon Oct 13, 2014 6:07 am
by morgoth
Hello,
This is my first post here and I am looking for some help as I am really stuck on something. I have tried many alternatives and spent a few days debugging to no avail.
Long story short, I have initially created a simple boot-loader that shows some graphics and then a very simple shell.
I then realized I am limited by the 512 bytes and cannot add any more functionality to it.
So, my solution was to create a two stage boot-loader. The first one would just read the second sector on the disk and load the second boot-loader, which now can be as long as I need it to be.
When I only have one single bootloader, everything works as expected. When I have two, the first one works, loads the second one and executes just the first few instructions from it, then I get a black screen. But I can see it jumps to a function that reads from keyboard as I can freely type stuff onto that screen.
This is the link for my repository:
https://github.com/blidarescub/os-dev
The single bootloader is test.asm
The other two ones are the remaining files.
I can provide a boot image if necessary for reproducing the issue.
Thank you so much in advance
Re: Two-stage bootloader issue
Posted: Mon Oct 13, 2014 6:31 am
by MrJonParr
Hi,
I'm learning this too, but it might be worth checking that all the stage2 loader is present, from the look at the code, this bit:
Code: Select all
Read_disk:
XOR AH, AH
INT 0x13
MOV BX, 0x8000 ; segment
MOV ES, BX
MOV BX, 0x0000 ; offset
MOV AH, 0x02 ; read function
MOV AL, 0x01 ; sectors - this might be wrong, trying to read from hd
MOV CH, 0x00 ; cylinder
MOV CL, 0x02 ; sector
MOV DH, 0x00 ; head
MOV DL, 0x80 ; drive - trying to read from hd
INT 0x13 ; disk int
JC Read_disk
JMP 0x8000:0x0000 ; buffer
Is it reading the entire stage2? Looks like you pad second_stage.asm to 1024 bytes but only read a single sector. Perhaps it's worth debugging and checking that after the read you see the entire stage2 bootloader in memory (maybe place a signature just like in the bootloader and see if you can see it at the end)?
Cheers,
Jon.
Re: Two-stage bootloader issue
Posted: Mon Oct 13, 2014 6:32 am
by ExeTwezz
Hi,
It seems like you're trying to load the second bootloader from the hard disk
Code: Select all
Read_disk:
XOR AH, AH
INT 0x13
MOV BX, 0x8000 ; segment
MOV ES, BX
MOV BX, 0x0000 ; offset
MOV AH, 0x02 ; read function
MOV AL, 0x01 ; sectors - this might be wrong, trying to read from hd
MOV CH, 0x00 ; cylinder
MOV CL, 0x02 ; sector
MOV DH, 0x00 ; head
MOV DL, 0x80 ; drive - trying to read from hd
INT 0x13 ; disk int
JC Read_disk
JMP 0x8000:0x0000 ; buffer
I don't recommend you to use hard disk right after you write your first bootloader. You should use floppy disk instead. And you're loading the second stage at 0x8000:0x0000 which equals to 0x0000:0x80000. It's better to load it at 0x0000:0x2000, for example.
Also, here is a link to a tutorial on loading sectors:
http://www.osdever.net/tutorials/view/loading-sectors.
Re: Two-stage bootloader issue
Posted: Mon Oct 13, 2014 7:37 am
by morgoth
Thanks for the quick replies.
MrJonParr wrote:Hi,
Is it reading the entire stage2? Looks like you pad second_stage.asm to 1024 bytes but only read a single sector. Perhaps it's worth debugging and checking that after the read you see the entire stage2 bootloader in memory (maybe place a signature just like in the bootloader and see if you can see it at the end)?
Cheers,
Jon.
It does not really matter the extra sector padding, as all the code fits neatly into the second sector. The third sector is just filled with 00's. So, first sector contains first boot-loader and second sector - second boot-loader. But good catch, thanks
ExeTwezz wrote:Hi,
It seems like you're trying to load the second bootloader from the hard disk
I don't recommend you to use hard disk right after you write your first bootloader. You should use floppy disk instead. And you're loading the second stage at 0x8000:0x0000 which equals to 0x0000:0x80000. It's better to load it at 0x0000:0x2000, for example.
Also, here is a link to a tutorial on loading sectors:
http://www.osdever.net/tutorials/view/loading-sectors.
Thanks for this remark, I have altered the code accordingly and I am still facing the exact same issue. I will now upload an .IMG floppy image on my github so that maybe someone can correlate the execution with the code and see where it hangs. I have no idea how to do that. All I can see is that the text cursor is left at the end of the first boot-loader's text, and it allows me to type R or I for reboot or interactive shell, and then executes the code accordingly. I't just the print functions that don't do anything. I initially thought it is about the video modes but I tried so many to no avail...
New code + boot image:
https://github.com/blidarescub/os-dev/tree/devel
Re: Two-stage bootloader issue
Posted: Mon Oct 13, 2014 7:56 am
by Brendan
Hi,
For a start, when you pass control to the second stage all the segment registers (DS, ES, ...) are left unchanged, so in every place where you access data you access the wrong address (e.g. printing the string at "0x0000:INTRO" and not the string at "0x2000:INTRO").
morgoth wrote:Thanks for this remark, I have altered the code accordingly and I am still facing the exact same issue. I will now upload an .IMG floppy image on my github so that maybe someone can correlate the execution with the code and see where it hangs. I have no idea how to do that.
Learning how to use something like (e.g.) the debugger built into Bochs to debug your code is something that every OS developer must learn sooner or later.
Cheers,
Brendan
Re: Two-stage bootloader issue
Posted: Mon Oct 13, 2014 8:11 am
by morgoth
Brendan wrote:
For a start, when you pass control to the second stage all the segment registers (DS, ES, ...) are left unchanged, so in every place where you access data you access the wrong address (e.g. printing the string at "0x0000:INTRO" and not the string at "0x2000:INTRO").
Thanks, I think this is my problem and I will try to fix it.
But I thought that by using [ORG 0x2000:0x0000] the CPU will take care of the offsets...
It still doesn't make any sense to me why the first calls work:
Code: Select all
[BITS 16]
[ORG 0x2000:0x0000]
CALL Clear_screen ; this works, it actually scrolls up the entire screen
CALL Draw_rect ; this works, draws a rectangle pixel by pixel
CALL Delay ; waits a second
CALL Clear_screen ; clears screen again
MOV SI, INTRO ; from here it should load the address into SI and call the print function, which in the first boot-loader works perfectly. here it fails
CALL PrintShell
Re: Two-stage bootloader issue
Posted: Mon Oct 13, 2014 8:40 am
by Brendan
Hi,
morgoth wrote:Brendan wrote:
For a start, when you pass control to the second stage all the segment registers (DS, ES, ...) are left unchanged, so in every place where you access data you access the wrong address (e.g. printing the string at "0x0000:INTRO" and not the string at "0x2000:INTRO").
Thanks, I think this is my problem and I will try to fix it.
But I thought that by using [ORG 0x2000:0x0000] the CPU will take care of the offsets...
I'm surprised that the assembler doesn't give you a "bad origin" error. The "ORG" is used by the assembler to determine the offset of labels, etc. within the segment; and the assembler doesn't know or care how you've chosen to use segmentation (or even if your code is intended for 16-bit protected mode or 16-bit real mode).
morgoth wrote:It still doesn't make any sense to me why the first calls work:
The call instructions depend on CS (which is set by the "jmp 0x2000:0x0000" in the first stage) and not DS, ES, SS, etc.
Also, because these call instructions use a relative displacement they also don't depend on the origin - it's like "displacement = (origin + first_offset) - (origin + second_offset)", where the value of "origin" cancels itself out.
morgoth wrote:Code: Select all
MOV SI, INTRO ; from here it should load the address into SI and call the print function, which in the first boot-loader works perfectly. here it fails
It loads the offset of INTRO into SI; then (later) uses DS:SI as the address (where SI is the correct offset, but DS is not the correct segment). To fix it, just do something like this near your entry point:
Cheers,
Brendan
Re: Two-stage bootloader issue
Posted: Mon Oct 13, 2014 8:58 am
by morgoth
WOW!!
Thanks, Brendan!
It worked perfectly. After spending the whole weekend on this issue, I finally have a two-stage bootloader that works properly.
I deleted the ORG instruction in the second stage and used your advice on correctly initializing the segment registers.
Code: Select all
[BITS 16]
MOV AX,CS ; initialize
MOV DS,AX ; segments
MOV ES,AX ; correctly
CALL Clear_screen
CALL Draw_rect
CALL Delay
CALL Clear_screen
Now all is OK, I guess this question can be closed.
Thanks again for all your advices.