我正在尝试编写自己的bootloader。虽然它在QEMU,Bochs和VirtualBox中运行良好,但我似乎无法在笔记本电脑上运行。
在我的笔记本电脑上,引导加载程序与所有模拟器的行为完全不同,挂起看似随机的地方,拒绝打印,甚至跳过一些jmp $
指令。
虽然我在"真实硬件"上遇到了很多麻烦,但我认为他们都有一个原因。
以下代码是一个简短的引导加载程序,应该打印" TEST"消息3次,然后通过跳转到同一位置挂起:
[BITS 16]
[ORG 0x7C00]
jmp 0x0000:start_16 ; In case bootloader is at 0x07C0:0x0000
start_16:
xor ax, ax
mov ds, ax
mov es, ax
cli ; Disable interrupts
mov ss, ax
mov sp, 0x7C00
sti ; Enable interrupts
cld ; Clear Direction Flag
; Store the drive number
mov [drive_number], dl
; Print message(s)
mov si, msg
call print_string
mov si, msg
call print_string
mov si, msg
call print_string
jmp $ ; HALT
; print_string
; si = string
print_string:
pusha
mov ah, 0x0E
.repeat:
lodsb
cmp al, 0x00
je .done
int 0x10
jmp short .repeat
.done:
popa
ret
; Variables
drive_number db 0x00
msg db 'TEST', 0x0D, 0x0A, 0x00
times 510-($-$$) db 0x00
db 0x55
db 0xAA
使用以下代码编译和模拟代码:
$ nasm -f bin bootloader.asm
$ qemu-system-x86_64 bootloader
在模拟器上,它打印" TEST"三次并挂起,在我的笔记本电脑上打印" TEST"接着是3个奇怪的字符:
来自http://wiki.osdev.org的大多数引导加载程序代码也不起作用。例如,http://wiki.osdev.org/Babystep2中的所有代码段都不能在我的笔记本电脑上运行。
我的代码出了什么问题?我该如何解决?
如果我删除了2个不必要的mov si, msg
,那么" TEST"消息打印两次。
笔记本:
汇编和写作:
$ nasm -f bin bootloader.asm
$ qemu-system-x86_64 bootloader # TEST 1
$ sudo dd if=/dev/zero of=/dev/sdd bs=1M count=1 # clean the USB
$ sudo dd if=bootloader of=/dev/sdd conv=fsync # write to USB
$ qemu-system-x86_64 /dev/sdd # TEST 2
Ω♣|
是引导加载程序的前3个字节。
更新了打印功能和字符串:
print_string:
pusha
.repeat:
mov ah, 0x0E
xor bx, bx
cld ; Clear Direction Flag
lodsb
cmp al, 0x00
je .done
int 0x10
jmp short .repeat
.done:
popa
ret
msg db 'TEST', 0x00
输出
一个TEST
。另外两个缺失了。
正如Ross Ridge所建议的那样,为了更好的调试,添加了dumpregs
。 int 0x10
执行不修改任何寄存器。
经过一些测试后,我已将dumpregs
函数移至drive_number
分配,并在jmp $
后移动$ ndisasm -b16 bootload2 -o 0x7c00
。代码应打印1行寄存器转储并暂停。相反,它继续:
完整代码: https://gist.github.com/anonymous/0ddc146f73ff3a13dd35
使用以下命令反汇编当前的引导程序:
[
{
"Stage":"birth",
"Avg":19071
},
{
//
}
]
答案 0 :(得分:4)
现在看起来BIOS可能正在修改它错误地假设的BIOS parameter block是加载到内存中的引导程序的一部分。它可能感觉需要,因为它在引导时为USB设备提供的几何结构可能与引导程序及其假定的BPB写入设备时使用的几何结构不同。由于在bootsector开始时存在跳转指令是应用程序测试BPB存在的方法之一,因此您可以尝试在bootsector的开头插入一些其他指令(但不是NOP)。例如:
[BITS 16]
[ORG 0x7C00]
xor ax,ax
jmp 0x0000:start_16 ; In case bootloader is at 0x07C0:0x0000
start_16:
请注意,远距离跳跃似乎不是normal indicators of the presence of a BPB之一。实际上,它是非常确凿的证据,表明BPB不存在,因为BPB从偏移量4开始,而远程跳转指令长度为5个字节。
如果这不起作用,您可以尝试为BPB保留空间,如下所示:
[BITS 16]
[ORG 0x7C00]
jmp start
nop
resb 8 + 25
start:
jmp 0x0000:start_16 ; In case bootloader is at 0x07C0:0x0000
start_16:
这里有一些代码可用于转储寄存器以帮助调试问题:
dumpregs:
push es
pusha
push 0xb800
pop es
mov di, [vidmem_ptr]
mov bp, sp
mov cx, 8
dump_loop:
dec bp
dec bp
mov ax, [bp + 16]
call printhex2
inc di
inc di
loop dump_loop
mov [vidmem_ptr], di
popa
pop es
ret
printhex2:
push ax
mov al, ah
call convhex1
pop ax
convhex1:
aam 16
; DB 0D4h, 16
xchg al, ah
call convhex
mov al, ah
convhex:
cmp al, 10
jb lessthan_10
add al, 'A' - '0' - 10
lessthan_10:
add al, '0'
stosb
mov al, 7
stosb
ret
vidmem_ptr dw 5 * 80 * 2 ; start at row 5
它使用直接视频写入来转储所有通用寄存器。它使用PUSHA / POPA寄存器命令:AX CX DX BX SP BP SI DI
如果通过调用此函数将原始代码中的INT 0x10指令括起来,则应得到如下输出:
特别要确保左侧的8个数字与右侧的8个数字匹配,用于每行调试输出。您正在使用的BIOS调用不应该更改任何寄存器。
答案 1 :(得分:2)
我以前见过类似的症状。场景可能类似于:
a)您正在从USB闪存启动笔记本电脑
b)BIOS查看设备的第一个扇区,并尝试确定它是应该像软盘一样对待它还是像硬盘一样。它无法找到有效的分区表,因此它决定将其视为软盘。c)BIOS试图"有帮助"并修补BPB中的一些字段。
d)你没有BPB,所以你的代码和/或数据被破坏,导致奇怪的事情发生(代码中的微小变化导致不同的奇怪事情发生)。
可悲的是,我自己没有硬件可以做到这一点。我不知道为什么BIOS假设有BPB,或多种不同的BPB格式中的哪一种"它假设存在,或者它认为它可能正在修补或为什么它认为修补任何东西是必要的。我只知道有些BIOS会将偏移量为0x1C到0x1F的字节和偏移量为0x24的字节丢弃。