Bootloader INT 0x13 Never Returns

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.
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Bootloader INT 0x13 Never Returns

Post by Geometrian »

Hi,

I am working on my first stage bootloader. I am having a problem loading a sector from the hard drive. Specifically, when I call "int 0x13", the code hangs. Code listing of the important part of the bootloader:

Code: Select all

[BITS 16]
[ORG 0x7C00]

jmp  main

%include "../asm_helpers/print.asm"
%include "print_level1.asm"

; PRINT 3 is a macro that prints given a string, text color, and background color
%macro PRINT1 3  ; print a level 1 text (i.e. something like "Bootloader level 1: <text>")
	call   print_level1
	PRINT  %1, %2, %3
%endmacro
%macro PRINTI 1  ; print an info line (i.e. PRINT1, but without having to specify a dark grey color)
	mov   esi, %1
	call  print_info
%endmacro
%macro FAIL 2  ; a failure condition--print an error and hang
	mov  esi, %2
	%1   fail
%endmacro

main:
	;Code is loaded to 0x0000:0x7C00, so CS=0x7C00/16=0x7C0

	;Setup segment registers
	;	Setup stack
	mov  ax, cs
	mov  ss, ax
	mov  sp, ax  ; Stack grows down from offset 0x7C0 toward 0x0000.
	;	Setup ES to point to text video memory
	mov   ax, 0xB800
	mov   es, ax

	;Print splash info
	PRINT  str_ss_1, 0x0, 0x2

	CURSOR_ADD_Y 2

	;Enable A20 line
	mov   ax, 0x2401     ; 0x2400 to disable
	int   0x15
	FAIL  jc, str_f_a20

	;Load kernel
	PRINTI  str_lk_1
	;	Check support INT13h extensions for LBA addressing
	mov     ah, 0x41
	mov     bx, 0x55AA
	mov     dl, 0x80
	int     0x13
	FAIL    jc, str_f_int13
	PRINTI  str_lk_2
	;	Do the transfer.  See http://en.wikipedia.org/wiki/INT_13H#INT_13h_AH.3D42h:_Extended_Read_Sectors_From_Drive
	mov     si, DAP
	mov     ah, 0x42
	mov     dl, 0x80
	PRINTI  str_lk_3
	int     0x13
	PRINTI  str_lk_4  ;NEVER HAPPENS!!!
	FAIL    jc, str_f_load

	;Kernel returned
	FAIL    jmp, str_f_kr

print_info:
	push  esi
	call  print_level1
	pop   esi
	SET_COLOR_L(0x8,0x0)
	call  print_string
	ret
fail:
	push  esi
	call  print_level1
	pop   esi
	SET_COLOR_L(0x4,0x0)
	call  print_string
	jmp   $

DAP:            ; Disk Address Packet
	;Size of DAP
	db  0x10
	;Unused
	db  0
	;Number of sectors to read
	dw  1       ; int 13 resets this to # of blocks actually read/written
	;Offset and segment of destination
	dw  0x0000  ; offset
	dw  0x0000  ; segment
	;LBA of start
	dd  1       ; lower 4 bytes
	dd  0       ; upper 4 bytes

;ASCII 13 is \r, ASCII 10 is \n
str_ss_1  db "Bootloader 1",13,10,"Ian Mallett",13,10,0

str_lk_1  db "load level 2:",13,10,0
str_lk_2  db "INT13 supported!",13,10,0
str_lk_3  db "load start:",13,10,0
str_lk_4  db "load done!",13,10,0

str_f_kr     db "kernel returned!",0
str_f_a20    db "no A20!",0
str_f_int13  db "no INT13!",0
str_f_load   db "load failed!",0

;Fill the rest of sector with 0
TIMES  510 - ($ - $$) db 0

;Add boot signature at the end of bootloader
DW 0xAA55
I don't know why this is happening. I found one other reference on this forum where the problem was an overwritten stack, but the DAP should only load one sector, so it shouldn't overwrite anything, right? I've also tried 0xFFFF:0xFFFF in the DAP with the same result.

Thanks,
Last edited by Geometrian on Wed Jan 31, 2024 5:33 pm, edited 1 time in total.
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by jnc100 »

Geometrian wrote:

Code: Select all

;Code is loaded to 0x0000:0x7C00, so CS=0x7C00/16=0x7C0
How do you know this is true? I have a feeling your stack may be overwriting your code (rather than your loaded data overwriting your stack).

Regards,
John.
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by Geometrian »

jnc100 wrote:
Geometrian wrote:

Code: Select all

;Code is loaded to 0x0000:0x7C00, so CS=0x7C00/16=0x7C0
How do you know this is true? I have a feeling your stack may be overwriting your code (rather than your loaded data overwriting your stack).
I had read somewhere that the code is loaded to 0x0000:0x7C00, but on rereading, I see that the latter part of the statement about the value of CS is incorrect.

Since CS and IP can vary, how can I set up a stack?

Thanks,
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by jnc100 »

The bootloader code is loaded to physical address 0x7c00. There is no standard as to what values of CS and IP are chosen to make up this value (remember that in real mode physical address = CS * 16 + IP). A safe way to do it is to jump immediately to a known address and then set up the stack at a known safe location, e.g.:

Code: Select all

[BITS 16]
[ORG 0x0]
jmp 0x0:next
next:
mov ax, 0x0
mov ss, ax
mov sp, 0x7c00
Which ensures you are running with CS = 0 and IP = 0x7c00 with the stack below 0x7c00. Typically you'd relocate your MBR to another safe address and then load your second stage at 0x7c00.

Regards,
John.
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by Geometrian »

jnc100 wrote:The bootloader code is loaded to physical address 0x7c00. There is no standard as to what values of CS and IP are chosen to make up this value (remember that in real mode physical address = CS * 16 + IP). A safe way to do it is to jump immediately to a known address and then set up the stack at a known safe location, e.g.:

Code: Select all

[BITS 16]
[ORG 0x0]
jmp 0x0:next
next:
mov ax, 0x0
mov ss, ax
mov sp, 0x7c00
Which ensures you are running with CS = 0 and IP = 0x7c00 with the stack below 0x7c00.
Changing the code around, and the BIOS doesn't print anything. Primarily, when changing the ORG from 0x7C00. I suspect this has to do with the video memory now being changed so the print is not longer working.

Questions:
-The ORG nasm command is an offset to all addresses. This is all accesses to physical memory?
-The jmp instruction sets CS=0 and IP=next, where next is the address of the first instruction in next. But, isn't the jmp's instruction equal to 0x7C00, and we jumped after that? Doesn't IP have to be somewhat different?
-Under ORG 0x7C00, video memory was loaded into ES as 0xB800. Under ORG 0x0000, what should it be?

Thanks!
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by jnc100 »

Apologies. I meant [ORG 0x7c00].

Regards,
John.
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by Geometrian »

Okay. Still though:

-The ORG nasm command is an offset to all addresses to physical memory? So, since ORG=0x7C00, all references are shifted by 0x7C00 so as to match where the BIOS loaded the bootloader?

-The jmp instruction sets CS=0 and IP=next, where "next" is the address of the first instruction in next, right? But, the jmp instruction is actually the first instruction, so shouldn't its address be 0x7C00? How is 0x0000:main equal to 0x0000:0x7C00 so that CS=0x0000, IP=0x7C00? Wouldn't IP be slightly different (larger?)?

My code now looks like:

Code: Select all

[BITS 16]
[ORG 0x7C00]

;BIOS loads code to 0x0000:0x7C00, but CS is NOT necessarily 0x7C00/16 = 0x7C0
;So, jump to a known address: CS=0x0000 and IP=0x7C00 and set up a new stack immediately
jmp  0x0000:main

;===INCLUDES AND MACROS SNIPPED===

main:
	;Setup segment registers
	;	Setup stack
	mov  ax, 0x0000
	mov  ss, ax
	mov  sp, 0x7C00 ; Stack grows down from offset 0x7C00 toward 0x0000.
	;	Setup ES to point to text video memory
	mov  ax, 0xB800
	mov  es, ax
Thanks!
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by jnc100 »

The org directive determines the segment offset of the next instruction, and is required for the assembler to know what values to use for absolute jumps and also what the addresses of any data objects that follow that directive are. So in this case org 0x7c00 simply tells the assembler to generate code that expects to be loaded at segment offset 0x7c00. Because the first instruction ensures that we are operating with CS = 0, this means that segment offsets are equal to physical addresses.

The first instruction is jmp 0x0:next. This will assemble to a five byte opcode (EA followed by ptr16:16 which specifes CS:IP), so the actual code generated by jmp 0x0:next would be jmp 0x0:0x7c05 (the assembler works out the 7c05 from the combination of the org directive and the length of the opcode).

If you are loading any data from this segment note that the default segment used for loading data is DS, so you will probably also want to set ds to 0.

Regards,
John.
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by Geometrian »

jnc100 wrote:The org directive determines the segment offset of the next instruction, and is required for the assembler to know what values to use for absolute jumps and also what the addresses of any data objects that follow that directive are.
Okay, jumps and data. Thanks.
jnc100 wrote:The first instruction is jmp 0x0:next. This will assemble to a five byte opcode (EA followed by ptr16:16 which specifes CS:IP), so the actual code generated by jmp 0x0:next would be jmp 0x0:0x7c05 (the assembler works out the 7c05 from the combination of the org directive and the length of the opcode).
That's why I was confused. You wrote that the code "ensures you are running with CS = 0 and IP = 0x7c00 with the stack below 0x7c00", so IP is actually 0x7C05? Since the stack grows toward lower addresses, this means that pushing things onto the stack can overwrite the jmp (which is fine, because we don't need it anymore), right?
jnc100 wrote:If you are loading any data from this segment note that the default segment used for loading data is DS, so you will probably also want to set ds to 0.
My virtual machine's BIOS seems to set it to 0x0000 by default, but I added code to initialize it to 0x0000 just in case.

The INT 13 still hangs . . .

Thanks!
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by jnc100 »

Geometrian wrote:You wrote that the code "ensures you are running with CS = 0 and IP = 0x7c00 with the stack below 0x7c00", so IP is actually 0x7C05?
Sorry for the confusion, I merely meant that the contents of the file (i.e. starting at the jmp) would be at 0x7c00, the next instruction would be at 0x7c05 and the ones after obviously at higher addresses.
Geometrian wrote:Since the stack grows toward lower addresses, this means that pushing things onto the stack can overwrite the jmp (which is fine, because we don't need it anymore), right?
The push instruction decrements the stack pointer first then stores the data, so actually the first push would write to 0x7bfe (and therefore not overwrite anything in the bootsector).
Geometrian wrote:The INT 13 still hangs . . .
The only other issue I can see (at a quick skim read) is that you set the destination buffer in the disk address packet to 0, and therefore overwrite both the real mode ivt and the bios data area, which is probably a bad idea.

Regards,
John.
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by Geometrian »

jnc100 wrote:
Geometrian wrote:You wrote that the code "ensures you are running with CS = 0 and IP = 0x7c00 with the stack below 0x7c00", so IP is actually 0x7C05?
Sorry for the confusion, I merely meant that the contents of the file (i.e. starting at the jmp) would be at 0x7c00, the next instruction would be at 0x7c05 and the ones after obviously at higher addresses.
Yeah; I'm just new at this, so I'm trying to understand completely. Thanks for your help, by the way; I appreciate it highly.
jnc100 wrote:
Geometrian wrote:Since the stack grows toward lower addresses, this means that pushing things onto the stack can overwrite the jmp (which is fine, because we don't need it anymore), right?
The push instruction decrements the stack pointer first then stores the data, so actually the first push would write to 0x7bfe (and therefore not overwrite anything in the bootsector).
Ack, I could have gotten that. The code starts at 0x7C00 going toward higher addresses, and the stack starts at the same but grows toward lower addresses.
jnc100 wrote:The only other issue I can see (at a quick skim read) is that you set the destination buffer in the disk address packet to 0, and therefore overwrite both the real mode ivt and the bios data area, which is probably a bad idea.
I tried other addresses, like 0xFFFF:0xFFFF and 0x8000:0x8000, but the same problem.

Thanks,
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by jnc100 »

Is it the first or second call to int 13h that causes the problem? And note that passing 0xffff:0xffff as the address has special meaning for 13h/ah=42h. Something like dw 0x8000, dw 0x0000 as the destination field would load your second stage to physical address 0x8000 (which wouldn't conflict with the bootloader or stack). Does your emulator provide any clues in its output (e.g. does it demonstrate that any disk i/o occurs in its logs?). My only other suggestion is to make sure you're trying to load from the correct drive. Ideally you should use the value of dl on entry to your bootsector in the call to int 13h to ensure you're loading from the same drive as you booted from.

Regards,
John.
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by Geometrian »

jnc100 wrote:Is it the first or second call to int 13h that causes the problem?
As in the comments, the second int 13h causes the problem. It is issued but never completes.
jnc100 wrote:And note that passing 0xffff:0xffff as the address has special meaning for 13h/ah=42h.
Really? what does it do? I couldn't find anything about that online.
jnc100 wrote:Something like dw 0x8000, dw 0x0000 as the destination field would load your second stage to physical address 0x8000 (which wouldn't conflict with the bootloader or stack).
Tried this. I was using addresses of the form with the segment being the same as the offset in case I got which was which backwards.
jnc100 wrote:Does your emulator provide any clues in its output (e.g. does it demonstrate that any disk i/o occurs in its logs?).
Using VirtualBox. It reports PIO Transfers to be 2 and Data Read to be 512 B. This corresponds to the BIOS loading the boot sector; removing the second int 13 interrupt results in the same numbers.
jnc100 wrote:My only other suggestion is to make sure you're trying to load from the correct drive. Ideally you should use the value of dl on entry to your bootsector in the call to int 13h to ensure you're loading from the same drive as you booted from.
I had actually seen this somewhere, and left it commented in (but removed when I posted here). I readded in the code, but no dice.

Here's a full source listing of the entire bootloader:
bootloader1.asm: http://pastebin.com/NccAAbg8
print.asm: http://pastebin.com/HGkzm3kP
print_level1.asm: http://pastebin.com/1kxfuncq

Thanks!
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: Bootloader INT 0x13 Never Returns

Post by DavidCooper »

Geometrian wrote: mov si, DAP
mov ah, 0x42
mov dl, 0x80
PRINTI str_lk_3
int 0x13
PRINTI str_lk_4 ;NEVER HAPPENS!!!
Have you checked carefully to make sure the bit in bold isn't messing up any registers you've just set?
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: Bootloader INT 0x13 Never Returns

Post by jnc100 »

I've just noticed the macro PRINTI immediately before int 13h will overwrite the contents of si such that it will no longer point to 'DAP'. I suggest you use an emulator like bochs as it has a built in debugger that would help you single-step through the code and potentially pick up problems like this (you could, for instance, examine all the registers immediately prior to the int call). I appreciate you can do this with the virtualbox debugger too but imho the bochs one is much easier to use.

Regards,
John.

edit: snap
Post Reply