Сбой загрузчика
В данный момент я занимаюсь разработкой своего загрузчика, но у меня проблема.
Я использую 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
Это крашлог от Бохса:
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
Я не знаю, что не так, я не проверял свой загрузчик на других платформах.
3 ответа
Ну, аварийный дамп однозначно указывает на 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 ] (): 3-е (12) исключение без разрешения, состояние выключения - 00h, сброс
Эта инструкция пытается пересечь нижнюю границу сегмента со смещением 0 при записи данных в стек. И это вызывает неисправимое исключение и следующий сброс.
Наиболее вероятная причина - прыжок в неправильное место и выполнение там случайных вещей, возможно, данных.
Посмотрите на эту инструкцию:
JMP 0x7E00 ;Infinite loop, hang it here.
После того, как процессор выполняет его, 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 тоже неправильно. Но вы перепрыгиваете с неправильными, неожиданными значениями в регистрах.
И поэтому этот код не будет выполняться должным образом. Я могу делать все что угодно, и, держу пари, это делает и приводит к разрушению стека и крушению.
Ключевой урок здесь: код x86 не зависит от позиции.
Вы должны либо вставить соответствующий org
(org 0x7E00
) или настроить регистры соответствующим образом и выполнить дальний переход к этой части кода (используя, например, jmp 0x7e0:0
).
ОБНОВЛЕНИЕ: И сейчас время для урока для меня... Вышеупомянутая проблема действительно существует. Однако, как отмечает @ughoavgfhw, есть еще один, почти в том же месте, но встречающийся чуть раньше. Вторые 512 байтов никогда не загружаются. Ага. Сам BIOS загружает только первый сектор. Этот первый сектор должен загрузить второй сектор, явно вызвав BIOS int 13h.
JMP 0x7E00
Переход к 0x7E00 в этот момент вызовет неожиданное поведение. Похоже, вы хотите перейти к post.bin, который вы ожидаете загрузить в 0x7E00. Это не. В BIOS загружен только 1 сектор, boot.bin, в 0x7C00. Вы должны будете загрузить любые дополнительные сектора вручную.
Когда ваш загрузчик начинает работать, значения большинства регистров могут быть любыми. Это включает в себя указатель стека. Ваш отчет о сбое показывает, что esp
равен 1, и сбой происходит из-за тройной ошибки на pusha
инструкция, которая в основном означает, что вы пытаетесь поместить данные в несуществующий стек. Это потому, что вы никогда не устанавливаете указатель стека, поэтому вы не знаете, где он находится. Вы должны установить указатель стека как одну из первых вещей, которые вы делаете. Я бы предложил установить его на 0x7c00, чтобы он располагался чуть ниже вашего кода. Поместите это в начале вашего загрузчика:
mov sp, 0x7C00