2017-05-29 102 views
1

OS时,我有以下bootloader代码似乎运行在硬盘上完美的罚款:一般性保护错误上运行ISO

[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:

我已经改变了代码中使用啊= 0×02像这样写着:

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 

,但它仍然是三重断裂。为什么?

+0

I'我不是ISO的专家,但看起来你正在使用xorriso来创建一个ISO,但已经关闭了软盘模拟。由于ISO不是模拟软盘,所以您的DAP读取可能会执行2048字节的扇区读取,而读取扇区1是相对于CD的开始。可能你并没有将你的内核读入内存,当你跳到KERNEL_OFFSET(0x2000)时,你正在执行内存,直到它在EIP 0x6616发生故障。我怀疑你的内核只是没有被正确读取。 –

+0

'xorriso -as mkisofs -R -J -c boot/bootcat -b boot/boot.flp -o nmos.iso nmos.flp'应该是'xorriso -as mkisofs -R -J -c boot/bootcat -b boot /boot.flp -o nmos.iso'。允许软盘模拟的ISO生成是'nmos.iso'。你可能会发现这个**可能不适用于BOCHS,QEMU和一些真正的硬件,因为int 13h/ah = 42h可能甚至不能用作模拟软盘的CD-ROM。 –

+0

查看我之前评论的最后一句。 –

回答

0

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


引导光盘是具有多种不同的模式的奇兽,并且有引导这样的CD,但他们也可能有自己的怪癖很多的BIOS。当您使用-no-emul-bootXORRISO您正在请求磁盘既不被视为软盘也不被视为硬盘。你可以删除-no-emul-boot -boot-load-size 4,它应该生成一个被视为软盘的ISO。问题在于许多真正的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). 

这是在谈论56个字节,我们创建拿着我们的bootloader虚拟磁盘的偏移量为8。如果我们修改你的引导代码的顶部看起来像这样,我们有效地创建一个空的引导信息表:

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 

修改后的Bootloader代码可能看起来像:

[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 
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也已过时)。 软盘映像必须具有512 字节的2400或2880或5760扇区,否则它将被xorriso拒绝。

其他大小的图像可能会模拟为硬盘,其中MBR分区表中的第一个 (且仅限)分区条目指示磁盘的大小为 。 xorriso -as mkisofs选项-hard-disk-boot选择此仿真。

+0

关于引导映像内容 和BIOS问题的唯一知识是: http:// git.zytor.com/syslinux/syslinux.git/tree/core/isolinux.asm 是一个经过测试的-no-emul-boot引导程序示例。 我唯一的软盘模拟ISO示例是名为MooseDT-SD1A-2D-8-16-32MB.iso的Seagate固件升级程序 。 –

+0

El Torito,4.1“INT 13 Function 08”给出了仿真的十六进制软盘几何结构。 轨道x头x扇区:1.44 Meg = 0x50 x 2 x 0x12, 2.88 Meg = 0x50 x 2 x 0x24,1.2 Meg = 0x50 x 2 x 0x0F。 –