英文:
Bootloader receives a disk read error when booting on real hardware but not on vmware, qemu, and bochs
问题
我已经编写了一个x86引导加载程序一段时间,已经验证了我的引导加载程序在bochs、qemu和vmware上都能正常工作。然而,在真实硬件(Thinkpad T400)上,我遇到了一个磁盘读取错误,错误代码为0x0880,但在qemu上没有这个问题。
最初我认为这个错误是由于BIOS不支持int 0x13 ah=42h
扩展引起的,因为我听说有些BIOS不支持磁盘扩展,而必须使用CHS方法(我不想使用CHS)。但是,当添加了检查ah=42h
读取支持的函数后,没有任何变化,这表明BIOS支持这个功能。
我还搜索了一些可能导致我的代码在真实硬件上不工作的原因,并尝试了所有这些方法(例如,确保DS寄存器为零,清除方向标志等)。
我想请教一些建议,以解决这个问题。
注意:我只提供代码部分的翻译,不提供回答问题的翻译。以下是代码部分的翻译:
[bits 16]
[extern kernel]
[global isr_stub_table]
[global IDT_load]
[extern FortressLoader_IdtInit]
[extern FortressLoader_ChecmMemMap]
[extern X86TTY_Init]
[extern X86TTY_Clear]
[extern printf]
[global FortressLoader_HeapStart]
[global FortressLoader_HeapEnd]
[global startKernel]
section .boot
global boot
KERNEL_SIZE_SECTORS equ 128
jmp boot
TIMES 3-($-$$) DB 0x90
; ...(以下是你的汇编代码的一部分,包括引导加载程序和其他函数)...
如果你需要更多关于如何解决这个问题的帮助,请随时提问。
英文:
I have been writing a x86 bootloader for some while, and have verified that my bootloader works on both bochs and qemu, along with vmware. However, I get a disk read error with an error code of 0x0880 on real hardware (Thinkpad T400), but not on qemu.
[bits 16]
[extern kernel]
[global isr_stub_table]
[global IDT_load]
[extern FortressLoader_IdtInit]
[extern FortressLoader_ChecmMemMap]
[extern X86TTY_Init]
[extern X86TTY_Clear]
[extern printf]
[global FortressLoader_HeapStart]
[global FortressLoader_HeapEnd]
[global startKernel]
section .boot
global boot
KERNEL_SIZE_SECTORS equ 128
jmp boot
TIMES 3-($-$$) DB 0x90
OEMname: db "mkfs.fat"
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: dw 18
numHeads: dw 2
numHiddenSectors: dd 0
numSectorsHuge: dd 0
driveNum: db 0
reserved: db 0
signature: db 0x65
volumeID: dd 0x2d7e5a1a
volumeLabel: db "NO NAME "
fileSysType: db "FAT12 "
boot:
cld ;Clear direction flag
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax
mov sp, 0x7c00
mov di, 0x7c00
cli
mov ah, 0x41 ;Check to make sure 13h extensions
mov bx, 55AAh ;CONT: are enabled by the BIOS
int 0x13 ;Call the disky wisky interrupt 🥺
jc dskDrvNope ;Print no disk support msg
cmp bx, 0xAA55 ;Check if 13h supported by BIOS
jnz dskDrvNoExtAtALl
mov bx, diskNoError ;
call print
mov [disk], dl
mov ax, 0x2401
int 0x15
mov ax, 0x3 ;Set the VGA mode, unknown at boot
int 0x10
mov ah, 42h
mov dl, [disk]
mov si, dap
int 0x13
jc disk_err ;Jump if disk read error
jmp disk_target ;
dskDrvNope:
mov bx, disk_no_extensions
call print
hlt
dskDrvNoExtAtALl:
mov bx, disk_no_extatall
call print
hlt
print:
pusha
start:
mov al, [bx]
cmp al, 0
je done
mov ah, 0x0e
int 0x10
add bx, 1
jmp start
done:
popa
ret
print_nl:
pusha
mov ah, 0x0e
mov al, 0x0a
int 0x10
mov al, 0x0d
int 0x10
popa
ret
print_hex:
pusha
mov cx, 0
hex_loop:
cmp cx, 4
je end
mov ax, dx
and ax, 0x000f
add al, 0x30
cmp al, 0x39
jle step2
add al, 7
step2:
mov bx, HEX_OUT + 5
sub bx, cx
mov [bx], al
ror dx, 4
add cx, 1
jmp hex_loop
end:
mov bx, HEX_OUT
call print
popa
ret
disk_err: ;Label for any disk errors
mov bx, disk_read_error ;Print the disk error
call print
call print_nl
mov dh, ah ;Load error code in ah register
call print_hex
jmp $
disk:
db 0x0
dap:
db 0x10
db 0
dw KERNEL_SIZE_SECTORS
dw 0 ;
dw 0x07e0 ;value recommended by a friend
dq 1 ;start second sector read
HEX_OUT:
db '0x0000',0
disk_read_error: db "Disk read error!", 0
disk_no_extensions: db "no disk ah=42h", 0
disk_no_extatall: db "no bios ah=42h", 0
diskNoError: db "Using bios extensions", 0
times 510 - ($-$$) db 0
dw 0xAA55
%include "./fortressloader/memory.asm"
%include "./fortressloader/memmap.asm"
%include "./fortressloader/multiboot.asm"
GDT_START:
dq 0x0
GDT_CODE:
dw 0xffff
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
GDT_DATA:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
GDT_END:
GDT_POINTER
dw GDT_END - GDT_START - 1
dd GDT_START
CODE_SEG equ GDT_CODE - GDT_START
DATA_SEG equ GDT_DATA - GDT_START
disk_target
call BiosGetMemorySize64MB
;Load the memory size
mov [mbInfo+mbInfo.memoryHi], bx
mov [mbInfo+mbInfo.memoryLo], ax
mov word [mbInfo+mbInfo.bootDevice], 0x0
mov edi, 0x2000
call BiosGetMemoryMap
mov word [mbInfo+mbInfo.mmap_length], ax
mov word [mbInfo+mbInfo.mmap_addr], 0x2000
cli ;Clear the interrupts
lgdt [GDT_POINTER] ;Load the GDT using a gdt pointer
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:boot2
[bits 32]
startKernel:
boot2:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esi, loaded_msg
mov ebx, 0xb8000
.loop: ;Print a message using a loop
lodsb
or al, al
jz load_kernel ;Load kernel when msg finished
or eax,0x0F00
mov word [ebx], ax
add ebx,2
jmp .loop
load_kernel:
mov esp, kernel_stack_top
;Load the stack to call C functions
call X86TTY_Init ;Initialize the TTY
call X86TTY_Clear ;Clear everything on the screen
call FortressLoader_IdtInit
;Initialize the IDT
mov eax, 0x1BADB002 ;Multiboot magic number
mov ebx, mbInfo
mov ebx, 0
push eax
push ebx
call kernel ;Call the actual kernel
halt:
cli
hlt
loaded_msg:db "Operating system loaded",0
;Message here to verify disk read
section .bss
align 4
kernel_stack_bottom: equ $ ;equ the current address for the stack
resb 16384 ;Use 16kb for stack size
kernel_stack_top: ;Top of the stack
section .text
My original belief for the error was that the int 0x13 ah=42h
extensions were not supported by the BIOS, as I had heard that some BIOSes do not support the disk extensions and instead I would have to use the CHS method (I would prefer not to use CHS). However, when adding the functions to check for the ah=42h
read support, nothing changes, indicating that the BIOS does support it.
I have also searched for reasons on why my code does not work on real hardware and followed all of them (ex: making sure DS is zero, clearing direction flag, etc)
I would like a few pointers on how to fix this issue.
答案1
得分: 5
一些 BIOS 不允许一次使用 int 13h/ah=42h
读取超过 127 个扇区的数据。我建议将 KERNEL_SIZE_SECTORS
设置为 127,而不是 128。
有人在其他地方提到过(但我没有亲身经历过)一些 BIOS 需要你在进行磁盘读取之前将 ES 寄存器设置为与 DAP(Disk Address Packet)中的段字段指定的值相同。在你的情况下,建议将 ES 设置为 0x07c0,以匹配 DAP 中的段值,然后再进行读取。
英文:
Some BIOSes don't allow the reading of more than 127 sectors at a time using int 13h/ah=42h
. I'd recommend setting KERNEL_SIZE_SECTORS
to 127 instead of 128.
It has been suggested elsewhere (but not something I have experienced) that there are BIOSes that need you to set ES to the same value as what is specified in the DAP's segment field before doing the disk read. In your case it might be advisable to set ES to 0x07c0 to match the segment value in the Disk Address Packet (DAP) before you initiate the read.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论