运行 os on iso 时的一般保护错误
general protection fault when running os on iso
我有以下 bootloader 代码,在硬盘上似乎 运行 完美无缺:
[bits 16]
[org 0x7c00]
bootld_start:
KERNEL_OFFSET equ 0x2000
xor ax, ax ; Explicitly set ES = DS = 0
mov ds, ax
mov es, ax
mov bx, 0x8C00 ; Set SS:SP to 0x8C00:0x0000 . The stack will exist
; between 0x8C00:0x0000 and 0x8C00:0xFFFF
mov ss, bx
mov sp, ax
mov [BOOT_DRIVE], dl
mov bx, boot_msg
call print_string
mov dl, [BOOT_DRIVE]
call disk_load
jmp pm_setup
jmp $
BOOT_DRIVE:
db 0
disk_load:
mov si, dap
mov ah, 0x42
int 0x13
;cmp al, 4
;jne disk_error_132
ret
dap:
db 0x10 ; Size of DAP
db 0
; You can only read 46 sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/512 = 46
dw 46 ; Number of sectors to read
dw KERNEL_OFFSET ; Offset
dw 0 ; Segment
dd 1
dd 0
disk_error_132:
mov bx, disk_error_132_msg
call print_string
jmp $
disk_error_132_msg:
db 'Error! Error! Something is VERY wrong! (0x132)', 0
gdt_start:
gdt_null:
dd 0x0
dd 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_descriptor:
dw gdt_end - gdt_start
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
boot_msg:
db 'OS is booting files... ', 0
done_msg:
db 'Done! ', 0
%include "boot/print_string.asm"
pm_setup:
mov bx, done_msg
call print_string
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Place stack below EBDA in lower memory
mov ebp, 0x9c000
mov esp, ebp
mov ebx, pmode_msg
call print32
call KERNEL_OFFSET
jmp $
pmode_msg:
db 'Protected mode enabled!', 0
kernel:
mov ebx, pmode_msg
call print32
jmp $
pmode_tst:
db 'Testing...'
times 510-($-$$) db 0
db 0x55
db 0xAA
问题是当我使用这些命令将其转换为 ISO 时:
mkdir iso
mkdir iso/boot
cp image.flp iso/boot/boot
xorriso -as mkisofs -R -J -c boot/bootcat \
-b boot/boot -no-emul-boot -boot-load-size 4 \
-o image.iso iso
...它因三重故障而失败。当我 运行 它与 qemu-system-i386 -boot d -cdrom os-image.iso -m 512 -d int -no-reboot -no-shutdown
时,它输出(不包括无用的 SMM 异常):
check_exception old: 0xffffffff new 0xd
0: v=0d e=0000 i=0 cpl=0 IP=0008:0000000000006616
pc=0000000000006616
SP=0010:000000000009bff8 env->regs[R_EAX]=0000000000000000
EAX=00000000 EBX=00007d72 ECX=00000000 EDX=000000e0
ESI=00007cb0 EDI=00000010 EBP=0009c000 ESP=0009bff8
EIP=00006616 EFL=00000083 [--S---C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007c73 00000018
IDT= 00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000e0 CCD=000001b3 CCO=ADDB
EFER=0000000000000000
check_exception old: 0xd new 0xd
1: v=08 e=0000 i=0 cpl=0 IP=0008:0000000000006616 pc=0000000000006616 SP=0010:000000000009bff8 env- >regs[R_EAX]=0000000000000000
EAX=00000000 EBX=00007d72 ECX=00000000 EDX=000000e0
ESI=00007cb0 EDI=00000010 EBP=0009c000 ESP=0009bff8
EIP=00006616 EFL=00000083 [--S---C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007c73 00000018
IDT= 00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000e0 CCD=000001b3 CCO=ADDB
EFER=0000000000000000
check_exception old: 0x8 new 0xd
这意味着我得到了 0x0d(一般保护故障),然后是 0x08(双重故障),然后是三重故障。为什么会这样?
编辑:我已将命令更改为:
xorriso -as mkisofs -R -J -c boot/bootcat -b boot/boot.flp -o nmos.iso nmos.flp
但我现在收到以下错误:
xorriso : FAILURE : Cannot find in ISO image: -boot_image ... bin_path='/boot/boot.flp'
xorriso : NOTE : -return_with SORRY 32 triggered by problem severity FAILURE
有人知道这是什么意思吗?
编辑 2:
我已将代码更改为使用 ah=0x02 读取,如下所示:
mov bx, KERNEL_OFFSET
mov ah, 0x02
mov al, 46
mov ch, 0x00
mov dh, 0x00
mov cl, 0x02
mov dl, [BOOT_DRIVE]
int 0x13
但它仍然是三重故障。为什么?
我是 xorriso 的开发者。如果image.flp是软盘镜像
使用 MBR,可能是一个分区 table,和一个文件系统,然后是提示
迈克尔走向正确的方向。 El Torito 指定仿真
这让引导映像文件在 BIOS 中显示为软盘或硬盘。
选项-no-emul-boot -boot-load-size 4导致BIOS加载
文件 image.flp 的前 2048 字节并将它们作为 x86 程序执行。
显然,软盘映像不是 suitable 作为普通程序。
根据 mkisofs 的传统,软盘仿真是默认的
选项-b。所以你只需要删除选项 -no-emul-boot
从您的 xorriso 命令行获取 El Torito 引导映像
作为软盘。 (-boot-load-size 4 也已过时。)
软盘映像必须有 2400、2880 或 5760 个 512 扇区
字节,否则会被 xorriso 拒绝。
其他大小的图像可能会被模拟为硬盘,其中第一个
MBR 分区 table 中的(且仅)分区条目告诉大小
磁盘。 xorriso -as mkisofs option -hard-disk-boot 选择此仿真。
你问题中所有三重错误的主要原因实际上归结为你的内核没有正确加载到 0x0000:0x2000 的内存中。当您使用 JMP 将控制权转移到此位置时,您最终会 运行 发生在内存区域中的内容并且 CPU 执行直到它遇到一条指令导致故障。
Bootable CD 是奇怪的野兽,有许多不同的模式,并且有许多 BIOS 可以引导此类 CD,但它们也可能有自己的怪癖。当您将 -no-emul-boot
与 XORRISO 一起使用时,您要求磁盘既不被视为软盘也不被视为硬盘。您可以删除 -no-emul-boot -boot-load-size 4
,它应该生成一个被视为软盘的 ISO。问题是许多真正的 BIOS、仿真器(BOCH 和 QEMU)和虚拟机不支持 Int 13h/AH=42h extended disk reads when the CD is booted using floppy emulation. You may be forced to use regular disk read via Int 13h/AH=02h。
如果您使用 -no-emul-boot -boot-load-size 4
,您应该能够通过 Int 13h/AH=42h 使用扩展磁盘读取,但它需要对您的引导加载程序进行一些更改。当使用 -no-emul-boot -boot-load-size 4
CDROM 时,扇区大小是 2048 字节,而不是 512。这需要对您的引导加载程序和内核进行一些修改。 -boot-load-size 4
将信息写入 ISO,通知 BIOS 从 ISO 内磁盘映像的开头读取 4
512 字节的块。不再需要 0xaa55
引导签名。
如果您使用 -no-emul-boot
,则还有一个问题需要处理。在 CD-ROM 上,LBA 0 不是磁盘映像放置在最终 ISO 中的位置。问题是,如何获取磁盘镜像在ISO中的LBA呢?您可以让 XORRISO 将此信息写入您创建的引导加载程序的特殊部分,然后使用 -boot-info-table
.
启用此功能
在引导加载程序的开头创建特殊部分相对容易。在 El Torito Specification Supplement 他们提到这个:
EL TORITO BOOT INFORMATION TABLE
...
The format of this table is as follows; all integers are in sec-
tion 7.3.1 ("little endian") format.
Offset Name Size Meaning
8 bi_pvd 4 bytes LBA of primary volume descriptor
12 bi_file 4 bytes LBA of boot file
16 bi_length 4 bytes Boot file length in bytes
20 bi_csum 4 bytes 32-bit checksum
24 bi_reserved 40 bytes Reserved
The 32-bit checksum is the sum of all the 32-bit words in the
boot file starting at byte offset 64. All linear block addresses
(LBAs) are given in CD sectors (normally 2048 bytes).
这是在讨论我们创建的包含引导加载程序的虚拟磁盘的偏移量 8 处的 56 个字节。如果我们将引导加载程序代码的顶部修改为如下所示,我们实际上会创建一个空白引导信息 table:
start:
jmp bootld_start
times 8-($-$$) db 0 ; Pad out first 8 bytes
; Boot info table
bi_pvd dd 0
bi_file dd 0
bi_kength dd 0
bi_csum dd 0
bi_reserved times 40 db 0 ; 40 bytes reserved
当使用 XORRISO 和 -boot-info-table
时,此 table 将在生成 ISO 后填写。 bi_file
是我们需要的重要信息,因为它是我们的磁盘映像放置在 ISO 中的 LBA。我们可以用它来填充扩展磁盘读取使用的磁盘访问数据包,以从 ISO 的正确位置读取。
为了使 DAP 更具可读性并考虑 2048 字节扇区,我将其修改为如下所示:
dap:
dap_size: db 0x10 ; Size of DAP
dap_zero db 0
; You can only read 11 2048 byte sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/2048 = 11 (rounded down)
dap_numsec: dw 11 ; Number of sectors to read
dap_offset: dw KERNEL_OFFSET ; Offset
dap_segment: dw 0 ; Segment
dap_lba_low: dd 0
dap_lba_high:dd 0
一个问题是放置在启动信息中的 LBA table 是从磁盘映像的开始(带有我们的引导加载程序的扇区)开始的。我们需要将该 LBA 递增 1 并将其放入 DAP,以便我们在内核启动处使用 LBA。使用 32 位指令,我们可以直接从 Boot Information Table 中读取 32 位值,加 1 并将其保存到 DAP。如果严格使用 16 位指令,将 32 位值加一会更复杂。由于我们要进入 386 保护模式,我们可以假设在实模式下支持带有 32 位操作数的指令。使用内核的 LBA 更新 DAP 的代码可能如下所示:
mov ebx, [bi_file] ; Get LBA of our disk image in ISO
inc ebx ; Add sector to get LBA for start of kernel
mov [dap_lba_low], ebx ; Update DAP with LBA of kernel in the ISO
唯一的另一个问题是引导加载程序扇区需要填充到 2048(CD-ROM 扇区的大小)而不是 512,我们可以删除引导签名。变化:
times 510-($-$$) db 0
db 0x55
db 0xAA
收件人:
times 2048-($-$$) db 0
修改后的引导加载程序代码可能如下所示:
[bits 16]
[org 0x7c00]
KERNEL_OFFSET equ 0x2000
start:
jmp bootld_start
times 8-($-$$) db 0 ; Pad out first 8 bytes
; Boot info table
bi_pvd dd 0
bi_file dd 0
bi_kength dd 0
bi_csum dd 0
bi_reserved times 40 db 0 ; 40 bytes reserved
bootld_start:
xor ax, ax ; Explicitly set ES = DS = 0
mov ds, ax
mov es, ax
mov bx, 0x8C00 ; Set SS:SP to 0x8C00:0x0000 . The stack will exist
; between 0x8C00:0x0000 and 0x8C00:0xFFFF
mov ss, bx
mov sp, ax
mov ebx, [bi_file] ; Get LBA of our disk image in ISO
inc ebx ; Add sector to get LBA for start of kernel
mov [dap_lba_low], ebx ; Update DAP with LBA of kernel in the ISO
mov [BOOT_DRIVE], dl
mov bx, boot_msg
call print_string
mov dl, [BOOT_DRIVE]
call disk_load
jmp pm_setup
jmp $
BOOT_DRIVE:
db 0
disk_load:
mov si, dap
mov ah, 0x42
int 0x13
;cmp al, 4
;jne disk_error_132
ret
dap:
dap_size: db 0x10 ; Size of DAP
dap_zero db 0
; You can only read 11 2048 byte sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/2048 = 11 (rounded down)
dap_numsec: dw 11 ; Number of sectors to read
dap_offset: dw KERNEL_OFFSET ; Offset
dap_segment: dw 0 ; Segment
dap_lba_low: dd 0
dap_lba_high:dd 0
disk_error_132:
mov bx, disk_error_132_msg
call print_string
jmp $
disk_error_132_msg:
db 'Error! Error! Something is VERY wrong! (0x132)', 0
gdt_start:
gdt_null:
dd 0x0
dd 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_descriptor:
dw gdt_end - gdt_start
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
boot_msg:
db 'OS is booting files... ', 0
done_msg:
db 'Done! ', 0
%include "boot/print_string.asm"
pm_setup:
mov bx, done_msg
call print_string
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Place stack below EBDA in lower memory
mov ebp, 0x9c000
mov esp, ebp
mov ebx, pmode_msg
call print32
call KERNEL_OFFSET
jmp $
pmode_msg:
db 'Protected mode enabled!', 0
kernel:
mov ebx, pmode_msg
call print32
jmp $
pmode_tst:
db 'Testing...'
times 2048-($-$$) db 0
然后您可以将原来的 XORRISO 命令修改为:
xorriso -as mkisofs -R -J -c boot/bootcat \
-b boot/boot -no-emul-boot -boot-load-size 4 \
-boot-info-table -o image.iso iso
我有以下 bootloader 代码,在硬盘上似乎 运行 完美无缺:
[bits 16]
[org 0x7c00]
bootld_start:
KERNEL_OFFSET equ 0x2000
xor ax, ax ; Explicitly set ES = DS = 0
mov ds, ax
mov es, ax
mov bx, 0x8C00 ; Set SS:SP to 0x8C00:0x0000 . The stack will exist
; between 0x8C00:0x0000 and 0x8C00:0xFFFF
mov ss, bx
mov sp, ax
mov [BOOT_DRIVE], dl
mov bx, boot_msg
call print_string
mov dl, [BOOT_DRIVE]
call disk_load
jmp pm_setup
jmp $
BOOT_DRIVE:
db 0
disk_load:
mov si, dap
mov ah, 0x42
int 0x13
;cmp al, 4
;jne disk_error_132
ret
dap:
db 0x10 ; Size of DAP
db 0
; You can only read 46 sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/512 = 46
dw 46 ; Number of sectors to read
dw KERNEL_OFFSET ; Offset
dw 0 ; Segment
dd 1
dd 0
disk_error_132:
mov bx, disk_error_132_msg
call print_string
jmp $
disk_error_132_msg:
db 'Error! Error! Something is VERY wrong! (0x132)', 0
gdt_start:
gdt_null:
dd 0x0
dd 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_descriptor:
dw gdt_end - gdt_start
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
boot_msg:
db 'OS is booting files... ', 0
done_msg:
db 'Done! ', 0
%include "boot/print_string.asm"
pm_setup:
mov bx, done_msg
call print_string
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Place stack below EBDA in lower memory
mov ebp, 0x9c000
mov esp, ebp
mov ebx, pmode_msg
call print32
call KERNEL_OFFSET
jmp $
pmode_msg:
db 'Protected mode enabled!', 0
kernel:
mov ebx, pmode_msg
call print32
jmp $
pmode_tst:
db 'Testing...'
times 510-($-$$) db 0
db 0x55
db 0xAA
问题是当我使用这些命令将其转换为 ISO 时:
mkdir iso
mkdir iso/boot
cp image.flp iso/boot/boot
xorriso -as mkisofs -R -J -c boot/bootcat \
-b boot/boot -no-emul-boot -boot-load-size 4 \
-o image.iso iso
...它因三重故障而失败。当我 运行 它与 qemu-system-i386 -boot d -cdrom os-image.iso -m 512 -d int -no-reboot -no-shutdown
时,它输出(不包括无用的 SMM 异常):
check_exception old: 0xffffffff new 0xd
0: v=0d e=0000 i=0 cpl=0 IP=0008:0000000000006616
pc=0000000000006616
SP=0010:000000000009bff8 env->regs[R_EAX]=0000000000000000
EAX=00000000 EBX=00007d72 ECX=00000000 EDX=000000e0
ESI=00007cb0 EDI=00000010 EBP=0009c000 ESP=0009bff8
EIP=00006616 EFL=00000083 [--S---C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007c73 00000018
IDT= 00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000e0 CCD=000001b3 CCO=ADDB
EFER=0000000000000000
check_exception old: 0xd new 0xd
1: v=08 e=0000 i=0 cpl=0 IP=0008:0000000000006616 pc=0000000000006616 SP=0010:000000000009bff8 env- >regs[R_EAX]=0000000000000000
EAX=00000000 EBX=00007d72 ECX=00000000 EDX=000000e0
ESI=00007cb0 EDI=00000010 EBP=0009c000 ESP=0009bff8
EIP=00006616 EFL=00000083 [--S---C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007c73 00000018
IDT= 00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000e0 CCD=000001b3 CCO=ADDB
EFER=0000000000000000
check_exception old: 0x8 new 0xd
这意味着我得到了 0x0d(一般保护故障),然后是 0x08(双重故障),然后是三重故障。为什么会这样?
编辑:我已将命令更改为:
xorriso -as mkisofs -R -J -c boot/bootcat -b boot/boot.flp -o nmos.iso nmos.flp
但我现在收到以下错误:
xorriso : FAILURE : Cannot find in ISO image: -boot_image ... bin_path='/boot/boot.flp'
xorriso : NOTE : -return_with SORRY 32 triggered by problem severity FAILURE
有人知道这是什么意思吗?
编辑 2:
我已将代码更改为使用 ah=0x02 读取,如下所示:
mov bx, KERNEL_OFFSET
mov ah, 0x02
mov al, 46
mov ch, 0x00
mov dh, 0x00
mov cl, 0x02
mov dl, [BOOT_DRIVE]
int 0x13
但它仍然是三重故障。为什么?
我是 xorriso 的开发者。如果image.flp是软盘镜像 使用 MBR,可能是一个分区 table,和一个文件系统,然后是提示 迈克尔走向正确的方向。 El Torito 指定仿真 这让引导映像文件在 BIOS 中显示为软盘或硬盘。
选项-no-emul-boot -boot-load-size 4导致BIOS加载 文件 image.flp 的前 2048 字节并将它们作为 x86 程序执行。 显然,软盘映像不是 suitable 作为普通程序。
根据 mkisofs 的传统,软盘仿真是默认的 选项-b。所以你只需要删除选项 -no-emul-boot 从您的 xorriso 命令行获取 El Torito 引导映像 作为软盘。 (-boot-load-size 4 也已过时。) 软盘映像必须有 2400、2880 或 5760 个 512 扇区 字节,否则会被 xorriso 拒绝。
其他大小的图像可能会被模拟为硬盘,其中第一个 MBR 分区 table 中的(且仅)分区条目告诉大小 磁盘。 xorriso -as mkisofs option -hard-disk-boot 选择此仿真。
你问题中所有三重错误的主要原因实际上归结为你的内核没有正确加载到 0x0000:0x2000 的内存中。当您使用 JMP 将控制权转移到此位置时,您最终会 运行 发生在内存区域中的内容并且 CPU 执行直到它遇到一条指令导致故障。
Bootable CD 是奇怪的野兽,有许多不同的模式,并且有许多 BIOS 可以引导此类 CD,但它们也可能有自己的怪癖。当您将 -no-emul-boot
与 XORRISO 一起使用时,您要求磁盘既不被视为软盘也不被视为硬盘。您可以删除 -no-emul-boot -boot-load-size 4
,它应该生成一个被视为软盘的 ISO。问题是许多真正的 BIOS、仿真器(BOCH 和 QEMU)和虚拟机不支持 Int 13h/AH=42h extended disk reads when the CD is booted using floppy emulation. You may be forced to use regular disk read via Int 13h/AH=02h。
如果您使用 -no-emul-boot -boot-load-size 4
,您应该能够通过 Int 13h/AH=42h 使用扩展磁盘读取,但它需要对您的引导加载程序进行一些更改。当使用 -no-emul-boot -boot-load-size 4
CDROM 时,扇区大小是 2048 字节,而不是 512。这需要对您的引导加载程序和内核进行一些修改。 -boot-load-size 4
将信息写入 ISO,通知 BIOS 从 ISO 内磁盘映像的开头读取 4
512 字节的块。不再需要 0xaa55
引导签名。
如果您使用 -no-emul-boot
,则还有一个问题需要处理。在 CD-ROM 上,LBA 0 不是磁盘映像放置在最终 ISO 中的位置。问题是,如何获取磁盘镜像在ISO中的LBA呢?您可以让 XORRISO 将此信息写入您创建的引导加载程序的特殊部分,然后使用 -boot-info-table
.
在引导加载程序的开头创建特殊部分相对容易。在 El Torito Specification Supplement 他们提到这个:
EL TORITO BOOT INFORMATION TABLE ... The format of this table is as follows; all integers are in sec- tion 7.3.1 ("little endian") format. Offset Name Size Meaning 8 bi_pvd 4 bytes LBA of primary volume descriptor 12 bi_file 4 bytes LBA of boot file 16 bi_length 4 bytes Boot file length in bytes 20 bi_csum 4 bytes 32-bit checksum 24 bi_reserved 40 bytes Reserved The 32-bit checksum is the sum of all the 32-bit words in the boot file starting at byte offset 64. All linear block addresses (LBAs) are given in CD sectors (normally 2048 bytes).
这是在讨论我们创建的包含引导加载程序的虚拟磁盘的偏移量 8 处的 56 个字节。如果我们将引导加载程序代码的顶部修改为如下所示,我们实际上会创建一个空白引导信息 table:
start:
jmp bootld_start
times 8-($-$$) db 0 ; Pad out first 8 bytes
; Boot info table
bi_pvd dd 0
bi_file dd 0
bi_kength dd 0
bi_csum dd 0
bi_reserved times 40 db 0 ; 40 bytes reserved
当使用 XORRISO 和 -boot-info-table
时,此 table 将在生成 ISO 后填写。 bi_file
是我们需要的重要信息,因为它是我们的磁盘映像放置在 ISO 中的 LBA。我们可以用它来填充扩展磁盘读取使用的磁盘访问数据包,以从 ISO 的正确位置读取。
为了使 DAP 更具可读性并考虑 2048 字节扇区,我将其修改为如下所示:
dap:
dap_size: db 0x10 ; Size of DAP
dap_zero db 0
; You can only read 11 2048 byte sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/2048 = 11 (rounded down)
dap_numsec: dw 11 ; Number of sectors to read
dap_offset: dw KERNEL_OFFSET ; Offset
dap_segment: dw 0 ; Segment
dap_lba_low: dd 0
dap_lba_high:dd 0
一个问题是放置在启动信息中的 LBA table 是从磁盘映像的开始(带有我们的引导加载程序的扇区)开始的。我们需要将该 LBA 递增 1 并将其放入 DAP,以便我们在内核启动处使用 LBA。使用 32 位指令,我们可以直接从 Boot Information Table 中读取 32 位值,加 1 并将其保存到 DAP。如果严格使用 16 位指令,将 32 位值加一会更复杂。由于我们要进入 386 保护模式,我们可以假设在实模式下支持带有 32 位操作数的指令。使用内核的 LBA 更新 DAP 的代码可能如下所示:
mov ebx, [bi_file] ; Get LBA of our disk image in ISO
inc ebx ; Add sector to get LBA for start of kernel
mov [dap_lba_low], ebx ; Update DAP with LBA of kernel in the ISO
唯一的另一个问题是引导加载程序扇区需要填充到 2048(CD-ROM 扇区的大小)而不是 512,我们可以删除引导签名。变化:
times 510-($-$$) db 0
db 0x55
db 0xAA
收件人:
times 2048-($-$$) db 0
修改后的引导加载程序代码可能如下所示:
[bits 16]
[org 0x7c00]
KERNEL_OFFSET equ 0x2000
start:
jmp bootld_start
times 8-($-$$) db 0 ; Pad out first 8 bytes
; Boot info table
bi_pvd dd 0
bi_file dd 0
bi_kength dd 0
bi_csum dd 0
bi_reserved times 40 db 0 ; 40 bytes reserved
bootld_start:
xor ax, ax ; Explicitly set ES = DS = 0
mov ds, ax
mov es, ax
mov bx, 0x8C00 ; Set SS:SP to 0x8C00:0x0000 . The stack will exist
; between 0x8C00:0x0000 and 0x8C00:0xFFFF
mov ss, bx
mov sp, ax
mov ebx, [bi_file] ; Get LBA of our disk image in ISO
inc ebx ; Add sector to get LBA for start of kernel
mov [dap_lba_low], ebx ; Update DAP with LBA of kernel in the ISO
mov [BOOT_DRIVE], dl
mov bx, boot_msg
call print_string
mov dl, [BOOT_DRIVE]
call disk_load
jmp pm_setup
jmp $
BOOT_DRIVE:
db 0
disk_load:
mov si, dap
mov ah, 0x42
int 0x13
;cmp al, 4
;jne disk_error_132
ret
dap:
dap_size: db 0x10 ; Size of DAP
dap_zero db 0
; You can only read 11 2048 byte sectors into memory between 0x2000 and
; 0x7C00. Don't read anymore or we overwrite the bootloader we are
; executing from. (0x7c00-0x2000)/2048 = 11 (rounded down)
dap_numsec: dw 11 ; Number of sectors to read
dap_offset: dw KERNEL_OFFSET ; Offset
dap_segment: dw 0 ; Segment
dap_lba_low: dd 0
dap_lba_high:dd 0
disk_error_132:
mov bx, disk_error_132_msg
call print_string
jmp $
disk_error_132_msg:
db 'Error! Error! Something is VERY wrong! (0x132)', 0
gdt_start:
gdt_null:
dd 0x0
dd 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_descriptor:
dw gdt_end - gdt_start
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
boot_msg:
db 'OS is booting files... ', 0
done_msg:
db 'Done! ', 0
%include "boot/print_string.asm"
pm_setup:
mov bx, done_msg
call print_string
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Place stack below EBDA in lower memory
mov ebp, 0x9c000
mov esp, ebp
mov ebx, pmode_msg
call print32
call KERNEL_OFFSET
jmp $
pmode_msg:
db 'Protected mode enabled!', 0
kernel:
mov ebx, pmode_msg
call print32
jmp $
pmode_tst:
db 'Testing...'
times 2048-($-$$) db 0
然后您可以将原来的 XORRISO 命令修改为:
xorriso -as mkisofs -R -J -c boot/bootcat \
-b boot/boot -no-emul-boot -boot-load-size 4 \
-boot-info-table -o image.iso iso