问题
I am trying to load up a little data using my bootloader on a USB, but apparently int 13h won't work!
Bootloader:
[bits 16]
[ORG 0x7c00]
jmp 0x0000:start
start:
cli
xor ax, ax
mov ss, ax
mov sp, 0x7c00
mov ax, cs
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
sti
mov [driveno], dl
reset:
;reset drive
xor ax, ax
mov dl, [driveno]
int 0x13
or ah, ah
jnz reset
mov ah, 0x02
mov al, 0x01
mov bx, 0x0000
mov es, bx
mov bx, 0x7e00
mov dl, [driveno]
xor dh, dh
mov cx, 0x0002
int 0x13
or ah, ah
jnz reset
mov dx, [0x7e00] ;So i can check and see if it has been loaded
call printhex
cli
hlt
jmp $
print:
loop:
lodsb
or al, al
jz done
mov ah, 0x0e
mov bx, 0x0003 ;page 0 and default color
int 0x10
jmp loop
done:
ret
printhex:
push bx
push si
mov si, hex_template
mov bx, dx
shr bx, 12
mov bx, [bx+hexabet]
mov [hex_template+2], bl
mov bx, dx
shr bx, 8
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+3], bl
mov bx, dx
shr bx, 4
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+4], bl
mov bx, dx
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+5], bl
call print
pop si
pop bx
ret
hex_template db '0x????',0
hexabet db '0123456789abcdef'
driveno db 0 ;Storing dl
times 510-($-$$) db 0
dw 0aa55h
dw 0x1234 ;This wont load!!!!
times 510 db 0
I want my hex dump method to print out 0x1234 to the screen, but it prints 0x0000 instead! I know my hex dump method works, the problem is that the 0x1234 never gets loaded in the first place. Any ideas?
I'm running on Windows. I compile and generate an image with:
nasm -f bin -o boot.bin boot.asm
dd if=boot.bin of=\\.\e: bs=512 count=2
I am using dd
from chrysocome. Can anyone explain this behavior?
回答1:
Although this question is old, it does bring to light a problem that I know has been experienced by others that does deserve a proper answer.
As Frank Kotler points out your code looks fine. A couple minor nitpicks I have:
- Before using lodsb you don't specify a direction flag with instruction CLD or STD. In your case it should be CLD since you expect LODSB to advance in a positive direction. You can see a more thorough explanation in another StackOverflow answer I wrote.
- Although you are targeting 16-bit code you are setting up fs and gs segment registers which aren't available one 8086/8088/80286.
The Problem and Solution
The issue is your usage of the DD program. You do:
dd if=boot.bin of=\.\e: bs=512 count=2
Typically you'd use of=\\.\e:
with double backslash, however this isn't the cause of the problem. \.\e:
doesn't actually point at the beginning of the disk (or USB drive). It points to the beginning of the partition that E: points to. Because of this you wrote your bootloader at the beginning of partition data, and not the Master Boot Record(MBR) . Newer versions of DD support an option to specify the the beginning of the entire device (not just a partition):
Changes in version 0.6beta1
new feature id=/od= for input disk and output disk. if is the only partition on a disk, then the entire disk is selected. Eg: if you insert a USB disk and it is mounted as f: then 'id=f:' will select the USB disk (not just the partition like if=\.\f: would do)
To write to the beginning of the USB drive that partition E: is a part of, you can do this (I'm assuming you are running the command prompt as a user with Admin privileges):
dd if=boot.bin od=e: bs=512 count=2
Notice how I have now specified (od=e:
). od=
(output device) just means that we want to use the entire physical device partition E: belongs to.
Explanation of Observed Behavior
What may be of interest is to why your bootloader seemed to work but printed out 0x0000
. I'll offer up my best guess based on knowledge of Windows.
Because of the issue with DD writing to the beginning of the partition (and not the beginning of the drive) sector #2 (1-based) doesn't actually contain our small kernel. Sector #1 doesn't even contain the bootloader we wrote!
Now if our code was written in the partition, why did our bootloader even run and why did it print the wrong value (0x0000)?
This would occur if you formatted the USB drive with Windows. What you may not realize is that the USB drive by default is formatted like a hard drive with a single partition, and that one partition is marked as bootable. Microsoft installs a bootloader in the MBR (first sector of the disk). This Microsoft bootloader is a chainloader, and it typically functions like this (some of the details may vary):
- Set up segment and stack appropriately
- Save DL [drive we booted on]
- Move ourselves from physical address 0x00007C00 somewhere else.
- Jump to the offset in the new memory location where we should continue
- Look at the partition table for the partition marked bootable
- Read first sector (512 bytes) of the bootable partition (not disk) into memory at 0x00007C00
- Restore DL with the value saved in first step
- FAR JMP to 0x0000:0x7C00
- Execution of the partition bootloader begins as if the BIOS had loaded and jumped to it directly.
What we have now is the Microsoft MBR bootloader reading the first sector of the bootable partition on the USB drive and executing it. The first sector of the partition happens to be our bootloader because we used dd if=boot.bin of=\.\e: bs=512 count=2
. We actually wrote two sectors. The second sector of the partition (not disk) contains our kernel. So effectively our bootloader runs!
So now we know why our bootloader ran, why did it print out the wrong value? It might be clearer now that the second sector of the disk doesn't have our kernel, the partition contains it. The disk read (int 13h) code does this:
mov ah, 0x02 ; Disk Read
mov al, 0x01 ; Number of Sectors to read
mov bx, 0x0000
mov es, bx
mov bx, 0x7e00 ; ES:BX location to read to 0x0000:0x7E00
mov dl, [driveno]
xor dh, dh
mov cx, 0x0002 ; Read sector #2 (1-based, not 0-based)
int 0x13
We just read the second sector of the disk, not the partition. Most likely the second sector is zeroed out and thus the reason why our bootloader read a value of 0x0000 .
Conclusion
If we had properly written over the first 2 sectors (1024 bytes) of the disk (with DD) then our bootloader and kernel would have worked properly..
Thanks to Microsoft and its chainloader - our bootloader ran but the kernel was in the wrong place on the disk and we printed out a sector that is likely filled with zeroes. This chain of events happened to make our bootloader run and made it appear that int 13h failed. It likely didn't fail at all, it just read a sector that didn't contain our kernel.
Note: I use the word kernel
, but in the context of this question it refers to the data (0x1234
) being stored in the second sector.
来源:https://stackoverflow.com/questions/29001104/int-13h-doesnt-appear-to-read-sectors-containing-my-kernel