1

我有以下引导加载程序代码,它似乎在硬盘上运行得很好:

[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

但它仍然是三重故障。为什么?

4

2 回答 2

1

我是 xorriso 的开发者。如果 image.flp 是一个带有 MBR 的软盘映像,可能是一个分区表和一个文件系统,那么 Michael 的提示就指向了正确的方向。El Torito 指定了让引导映像文件在 BIOS 中显示为软盘或硬盘的仿真。

选项 -no-emul-boot -boot-load-size 4 使 BIOS 加载文件 image.flp 的前 2048 个字节,并将它们作为 x86 程序执行。显然,软盘映像不适合作为普通程序。

根据 mkisofs 的传统,软盘仿真是带有选项 -b 的默认设置。因此,您只需从 xorriso 命令行中删除选项 -no-emul-boot 即可将 El Torito 引导映像作为软盘。(然后,-boot-load-size 4 也已过时。)软盘映像必须具有 2400、2880 或 5760 个 512 字节的扇区,否则它将被 xorriso 拒绝。

其他大小的图像可以模拟为硬盘,其中 MBR 分区表中的第一个(也是唯一一个)分区条目告诉磁盘的大小。xorriso -as mkisofs option -hard-disk-boot 选择此仿真。

于 2017-05-29T15:48:40.460 回答
0

您的问题中所有三重错误的主要原因实际上归结为您的内核没有正确加载到 0x0000:0x2000 的内存中。当您使用JMP将控制权转移到该位置时,您最终会运行内存区域中的内容,并且 CPU 会一直执行,直到遇到导致错误的指令。


可引导 CD 是一种奇怪的野兽,具有多种不同的模式,并且有许多 BIOS 可以引导此类 CD,但它们也可能有自己的怪癖。当您使用-no-emul-bootXORRISO,您要求磁盘既不被视为软盘也不被视为硬盘。您可以删除-no-emul-boot -boot-load-size 4它应该生成一个被视为软盘的 ISO。问题在于许多真实的 BIOS、仿真器(BOCH 和 QEMU)和虚拟机在使用软盘仿真启动 CD 时不支持Int 13h/AH=42h扩展磁盘读取。您可能被迫通过Int 13h/AH=02h使用常规磁盘读取。

如果您使用,您应该能够通过 Int 13h/AH=42h 使用扩展磁盘读取,-no-emul-boot -boot-load-size 4但这需要对您的引导加载程序进行一些更改。使用-no-emul-boot -boot-load-size 4CDROM 时,扇区大小为 2048 字节,而不是 512。这需要对引导加载程序和内核进行一些修改。向-boot-load-size 4ISO 写入信息,通知 BIOS4从 ISO 内的磁盘映像的开头读取 512 字节的块。0xaa55不再需要引导签名。

如果您使用-no-emul-boot,则需要处理另一个障碍。CD-ROM 上的 LBA 0 不是磁盘映像放置在最终 ISO 中的位置。问题是,如何获得磁盘映像在 ISO 中的 LBA?您可以让XORRISO将此信息写入您创建的引导加载程序的特殊部分,然后使用-boot-info-table.

在引导加载程序的开头创建特殊部分相对容易。在El Torito 规范补充中,他们提到了这一点:

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 个字节。如果我们将引导加载程序代码的顶部修改为如下所示,我们将有效地创建一个空白引导信息表:

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该表将在生成 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 是从磁盘映像(我们的引导加载程序所在的扇区)的开头开始的。我们需要将该 LBA 增加 1 并将其放入 DAP,以便我们使用内核启动的 LBA。使用 32 位指令,我们可以从引导信息表中读取 32 位值,加 1 并将其保存到 DAP。如果使用严格的 16 位指令,将 1 加到 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
于 2017-05-29T19:06:02.140 回答