在iso上运行os时的一般保护错误

时间:2017-05-29 11:58:48

标签: nasm qemu iso osdev general-protection-fault

我有以下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

但它仍然是三重断层。为什么呢?

2 个答案:

答案 0 :(得分:1)

我是xorriso的开发者。如果image.flp是软盘映像 使用MBR,可能是分区表和文件系统,然后是提示 迈克尔走向正确的方向。 El Torito指定仿真 这使启动映像文件在BIOS中显示为软盘或硬盘。

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

根据mkisofs的传统,软盘仿真是默认的 选项-b。所以你只需要删除选项-no-emul-boot 从您的xorriso命令行获取El Torito启动映像 像软盘一样。 (-boot-load-size 4也是过时的。) 软盘映像必须具有2400或2880或5760个512的扇区 字节,否则它将被xorriso拒绝。

其他尺寸的图像可以模拟为第一个的硬盘 (并且仅)MBR分区表中的分区条目告诉大小 磁盘。 xorriso -as mkisofs选项-hard-disk-boot选择此仿真。

答案 1 :(得分:0)

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

可启动CD是具有多种不同模式的奇怪的野兽,并且有许多BIOS可以启动这些CD,但它们也可能有自己的怪癖。当您将-no-emul-boot XORRISO 一起使用时,您要求磁盘既不被视为软盘也不被视为硬盘。您可以删除应生成被视为软盘的ISO的-no-emul-boot -boot-load-size 4。问题是许多真正的BIOS,仿真器(BOCH和QEMU)和虚拟机在使用软盘仿真启动CD时不支持Int 13h/AH=42h扩展磁盘读取。您可能被迫通过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个字节,它们持有我们的引导加载程序。如果我们修改引导加载程序代码的顶部看起来像这样,我们有效地创建一个空白引导信息表:

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位指令,则将一个加到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