2

我目前正在开发我的引导加载程序,但我有问题。

我使用 Bochs 测试引导加载程序,编译引导加载程序并制作磁盘映像:

rm disk.bin
rm boot.bin
rm post.bin
nasm bootloader.asm -o boot.bin
nasm postmbr.asm -o post.bin
cat boot.bin post.bin > disk.bin

这是 bootloader.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code
[ORG 0x7C00]    ;Origin, tell the assembler that where the code will

MOV SI, HelloString ;Store string pointer to SI
MOV AH, 02h          ; read sector command
MOV AL, 01h
MOV CX, 0001h
MOV DH, 00h
MOV DL, 80h ;disk
MOV AX, 7E00h
MOV ES, AX ;buffer
MOV BX, 00h ;offset
CALL PrintString
JMP 0x7E00              ;Infinite loop, hang it here.


PrintCharacter: ;Procedure to print character on screen
        ;Assume that ASCII value is in register AL
MOV AH, 0Eh     ;Tell BIOS that we need to print one charater on screen.
MOV BH, 00h     ;Page no.
MOV BL, 07h     ;Text attribute 0x07 is lightgrey font on black background

INT 10h ;Call video interrupt
RET             ;Return to calling procedure



PrintString:    ;Procedure to print string on screen
        ;Assume that string starting pointer is in register SI

next_character: ;Lable to fetch next character from string
MOV AL, [SI]    ;Get a byte from string and store in AL register
INC SI          ;Increment SI pointer
OR AL, AL       ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character      ;Fetch next character from string
exit_function:  ;End label
RET             ;Return from procedure


;Data
HelloString db 'Loading OS demo...', 0  ;HelloWorld string ending with 0

TIMES 510 - ($ - $$) db 0       ;Fill the rest of sector with 0
DW 0xAA55                       ;Add boot signature at the end of bootloader

和 postmbr.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code

MOV SI, HelloString ;Store string pointer to SI
CALL PrintString        ;Call print string procedure
JMP $           ;Infinite loop, hang it here.


PrintCharacter: ;Procedure to print character on screen
        ;Assume that ASCII value is in register AL
MOV AH, 0x0E    ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00    ;Page no.
MOV BL, 0x07    ;Text attribute 0x07 is lightgrey font on black background

INT 0x10        ;Call video interrupt
RET             ;Return to calling procedure



PrintString:    ;Procedure to print string on screen
        ;Assume that string starting pointer is in register SI

next_character: ;Lable to fetch next character from string
MOV AL, [SI]    ;Get a byte from string and store in AL register
INC SI          ;Increment SI pointer
OR AL, AL       ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character      ;Fetch next character from string
exit_function:  ;End label
RET             ;Return from procedure


;Data
HelloString db 'Hello World', 0 ;HelloWorld string ending with 0

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

这是来自 Bochs 的崩溃日志:

00000004661i[BIOS ] $Revision: 11318 $ $Date: 2012-08-06 19:59:54 +0200 (Mo, 06. Aug 2012) $
00000319074i[KBD  ] reset-disable command received
00000321808i[BIOS ] Starting rombios32
00000322242i[BIOS ] Shutdown flag 0
00000322837i[BIOS ] ram_size=0x02000000
00000323258i[BIOS ] ram_end=32MB
00000363787i[BIOS ] Found 1 cpu(s)
00000377969i[BIOS ] bios_table_addr: 0x000fa438 end=0x000fcc00
00000396429i[BIOS ] bios_table_cur_addr: 0x000fa438
00000524046i[VBIOS] VGABios $Id: vgabios.c,v 1.75 2011/10/15 14:07:21 vruppert Exp $
00000524117i[BXVGA] VBE known Display Interface b0c0
00000524149i[BXVGA] VBE known Display Interface b0c5
00000527074i[VBIOS] VBE Bios $Id: vbe.c,v 1.64 2011/07/19 18:25:05 vruppert Exp $
00000800003i[XGUI ] charmap update. Font Height is 16
00000866078i[BIOS ] ata0-0: PCHS=1/1/2 translation=none LCHS=1/1/2
00004743252i[BIOS ] IDE time out
00016726470i[BIOS ] Booting from 0000:7c00
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553e[CPU0 ] write_virtual_word_32(): segment limit violation
00016755553i[CPU0 ] CPU is in real mode (active)
00016755553i[CPU0 ] CS.mode = 16 bit
00016755553i[CPU0 ] SS.mode = 16 bit
00016755553i[CPU0 ] EFER   = 0x00000000
00016755553i[CPU0 ] | EAX=000000aa  EBX=00000007  ECX=00090001  EDX=00000080
00016755553i[CPU0 ] | ESP=00000001  EBP=00000000  ESI=000e7c45  EDI=0000ffac
00016755553i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of DF if tf SF zf af PF cf
00016755553i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00016755553i[CPU0 ] |  CS:0000( 0004| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  DS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  ES:7e00( 0005| 0|  0) 0007e000 0000ffff 0 0
00016755553i[CPU0 ] |  FS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] |  GS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016755553i[CPU0 ] | EIP=0000fd34 (0000fd34)
00016755553i[CPU0 ] | CR0=0x60000010 CR2=0x00000000
00016755553i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00016755553i[CPU0 ] 0x0000fd34>> pusha  : 60
00016755553e[CPU0 ] exception(): 3rd (12) exception with no resolution, shutdown status is 00h, resetting
00016755553i[SYS  ] bx_pc_system_c::Reset(HARDWARE) called
00016755553i[CPU0 ] cpu hardware reset
00016755553i[APIC0] allocate APIC id=0 (MMIO enabled) to 0x00000000fee00000
00016755553i[CPU0 ] CPUID[0x00000000]: 00000002 68747541 444d4163 69746e65
00016755553i[CPU0 ] CPUID[0x00000001]: 00000633 00010800 00000008 17cbfbff
00016755553i[CPU0 ] CPUID[0x00000002]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000000]: 80000008 68747541 444d4163 69746e65
00016755553i[CPU0 ] CPUID[0x80000001]: 00000633 00000000 00000000 c1c3f3ff
00016755553i[CPU0 ] CPUID[0x80000002]: 20444d41 6c687441 74286e6f 7020296d
00016755553i[CPU0 ] CPUID[0x80000003]: 65636f72 726f7373 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000004]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000005]: 01ff01ff 01ff01ff 40020140 40020140
00016755553i[CPU0 ] CPUID[0x80000006]: 00000000 42004200 02008140 00000000
00016755553i[CPU0 ] CPUID[0x80000007]: 00000000 00000000 00000000 00000000
00016755553i[CPU0 ] CPUID[0x80000008]: 00002028 00000000 00000000 00000000
00016755553i[     ] reset of 'cmos' plugin device by virtual method
00016755553i[     ] reset of 'dma' plugin device by virtual method
00016755553i[     ] reset of 'pic' plugin device by virtual method
00016755553i[     ] reset of 'pit' plugin device by virtual method
00016755553i[     ] reset of 'floppy' plugin device by virtual method
00016755553i[     ] reset of 'vga' plugin device by virtual method
00016755553i[     ] reset of 'ioapic' plugin device by virtual method
00016755553i[     ] reset of 'keyboard' plugin device by virtual method
00016755553i[     ] reset of 'harddrv' plugin device by virtual method
00016755553i[     ] reset of 'unmapped' plugin device by virtual method
00016755553i[     ] reset of 'biosdev' plugin device by virtual method
00016755553i[     ] reset of 'speaker' plugin device by virtual method
00016755553i[     ] reset of 'extfpuirq' plugin device by virtual method
00016755553i[     ] reset of 'parallel' plugin device by virtual method
00016755553i[     ] reset of 'serial' plugin device by virtual method

这是 Bochs 的配置文件:

# configuration file generated by Bochs
plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1
config_interface: textconfig
display_library: x
memory: host=32, guest=32
romimage: file="/usr/share/bochs/BIOS-bochs-latest"
vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest"
boot: disk
floppy_bootsig_check: disabled=0
# no floppya
# no floppyb
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, mode=flat, translation=auto, path="disk.bin", cylinders=1, heads=1, spt=2, biosdetect=auto, model="Generic 1234"
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata2: enabled=0
ata3: enabled=0
pci: enabled=0
vga: extension=vbe, update_freq=5
cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
cpuid: family=6, model=0x03, stepping=3, mmx=1, apic=xapic, sse=sse2, sse4a=0, sep=1, aes=0, xsave=0, xsaveopt=0, movbe=0, adx=0, smep=0, mwait=1
cpuid: vendor_string="AuthenticAMD"
cpuid: brand_string="AMD Athlon(tm) processor"

print_timestamps: enabled=0
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0
# no cmosimage
# no loader
log: -
logprefix: %t%e%d
panic: action=ask
error: action=report
info: action=report
debug: action=ignore
keyboard: type=mf, serial_delay=250, paste_delay=100000, keymap=
user_shortcut: keys=none
mouse: enabled=0, type=ps2, toggle=ctrl+mbutton
parport1: enabled=1, file=""
parport2: enabled=0
com1: enabled=1, mode=null, dev=""
com2: enabled=0
com3: enabled=0
com4: enabled=0

我不知道出了什么问题,我还没有在其他平台上测试过我的引导加载程序。

4

3 回答 3

4

好吧,故障转储明确指出了pusha试图将寄存器压入已破坏堆栈的指令。

相关位:

00016755553e[CPU0] write_virtual_word_32():违反段限制
00016755553e[CPU0] write_virtual_word_32():违反段限制
00016755553e[CPU0] write_virtual_word_32():违反段限制
00016755553i[CPU0] | EAX=000000aa EBX=00000007 ECX=00090001 EDX=00000080
00016755553i[CPU0] | ESP=00000001 EBP=00000000 ESI=000e7c45 EDI=0000ffac
00016755553i[CPU0] | SEG sltr(index|ti|rpl) 基本限制 GD
00016755553i[CPU0] | CS:0000(0004|0|0)00000000 0000ffff 0 0
00016755553i[CPU0]| SS:0000(0005|0|0)00000000 0000ffff 0 0
00016755553i[CPU0]| EIP=0000fd34 (0000fd34)
00016755553i[CPU0] 0x0000fd34>> pusha : 60
00016755553e[CPU0] exception():第 3 (12) 个异常,无解决,关机状态为 00h,正在重置

该指令在将数据写入堆栈时试图在偏移量 0 处越过下段边界。这会导致不可恢复的异常和以下重置。

最可能的原因是跳转到错误的位置并在那里执行随机的东西,可能是数据。

看看这个指令:

JMP 0x7E00              ;Infinite loop, hang it here.

CPU 执行后,CS:IP将是 0:0x7E00。

现在看看你的 postmbr.asm:

[BITS 16]       ;Tells the assembler that its a 16 bit code

MOV SI, HelloString ;Store string pointer to SI
...

那里没有org,在这种情况下org 0是隐含的。

因此,这部分代码的编译假设IP在开始时 =0 并且所有偏移量都从这部分代码的开头开始计算,并且从逻辑上讲,这意味着CS= DS=0 也是错误的。但是您在寄存器中使用错误的、意外的值跳转到它。

所以这段代码不会按预期执行。IOW,它几乎可以做任何事情,我敢打赌,它确实会导致堆栈破坏和崩溃。

这里的关键教训:x86 代码不是与位置无关的。

您应该插入适当的org( org 0x7E00) 或相应地设置寄存器并跳到这部分代码(使用 eg jmp 0x7e0:0)。

更新:现在是我上一课的时候了……上述问题确实存在。然而,正如@ughoavgfhw所指出的,还有一个几乎在同一个地方,但发生的时间稍早一些。永远不会加载第二个 512 字节。是的。BIOS 本身只加载第一个扇区。第一个扇区必须通过显式调用 BIOS int 13h 来加载第二个扇区。

于 2012-10-22T17:40:20.260 回答
1
JMP 0x7E00

此时跳转到 0x7E00 将导致意外行为。看起来您想跳转到 post.bin,您希望在 0x7E00 加载该文件。它不是。BIOS 仅在 0x7C00 处加载了 1 个扇区 boot.bin。您必须手动加载任何其他扇区。

于 2012-10-22T17:43:46.153 回答
-1

当您的引导加载程序开始运行时,大多数寄存器的值可以是任何值。这包括堆栈指针。您的崩溃报告显示esp为 1,并且由于pusha指令上的三重错误而发生崩溃,这基本上意味着您正在尝试将数据推送到不存在的堆栈上。这是因为你从来没有设置堆栈指针,所以你不知道它在哪里。您需要将堆栈指针设置为您首先要做的事情之一。我建议将其设置为 0x7c00,以便将其放置在您的代码下方。将其放在引导加载程序的开头附近:

mov  sp, 0x7C00
于 2012-10-22T17:37:47.077 回答