general protection fault when running os on iso

1

I have the following bootloader code which seems to run perfectly fine on a hard disk:

[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

The problem is that when I convert it to an ISO with these commands:

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

...it fails with a triple fault. When I run it with qemu-system-i386 -boot d -cdrom os-image.iso -m 512 -d int -no-reboot -no-shutdown, it outputs (excluding useless SMM exceptions):

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

Which means that I got a 0x0d (general protection fault), then a 0x08 (double fault), then it triple faulted. Why is this happening?

EDIT: I have changed the command to:

xorriso -as mkisofs -R -J -c boot/bootcat -b boot/boot.flp -o nmos.iso nmos.flp

But I am now getting the following error:

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

Does anyone know what this means?

EDIT 2:

I have changed the code to read using ah=0x02 like this:

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

But it is still triple-faulting. Why?

nasm
qemu
iso
osdev
general-protection-fault
asked on Stack Overflow May 29, 2017 by nm111 • edited May 31, 2017 by nm111

2 Answers

1

I am the developer of xorriso. If image.flp is a floppy disk image with MBR, possibly a partition table, and a filesystem, then the hint of Michael goes to the right direction. El Torito specifies emulations which lets the boot image file appear to the BIOS as floppy or hard disk.

The options -no-emul-boot -boot-load-size 4 causes BIOS to load the first 2048 bytes of file image.flp and to execute them as x86 program. Obviously a floppy image is not suitable as plain program.

According to mkisofs traditions floppy emulation is the default with option -b. So you would just have to remove the option -no-emul-boot from your xorriso command line in order to get the El Torito boot image as floppy. (-boot-load-size 4 is then obsolete, too.) The floppy image must have either 2400, or 2880, or 5760 sectors of 512 bytes, or else it will be rejected by xorriso.

Images of other sizes may be emulated as hard disks where the first (and only) partition entry in the MBR partition table tells the size of the disk. xorriso -as mkisofs option -hard-disk-boot chooses this emulation.

answered on Stack Overflow May 29, 2017 by Thomas Schmitt
0

The primary cause of all the triple faults in your question really come down to the fact that your kernel isn't being loaded properly into the memory at 0x0000:0x2000. When you transfer control to this location with a JMP you end up running what happens to be in the memory region and the CPU executes until it hits an instruction that causes a fault.


Bootable CDs are strange beasts that have a number of different modes, and there are many BIOSes that boot such CDs but they too may have their own quirks. When you use -no-emul-boot with XORRISO you are requesting the disk neither be treated as a floppy nor hard disk. You could remove -no-emul-boot -boot-load-size 4 that should generate an ISO that gets treated as a floppy. The problem with that is many real BIOSes, Emulators (BOCHs and QEMU) and Virtual machines do not support 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.

You should be able to use extended disk reads via Int 13h/AH=42h if you use -no-emul-boot -boot-load-size 4 but it will require some changes to your bootloader. When using -no-emul-boot -boot-load-size 4 CDROMs sector sizes are 2048 bytes, not 512. This will require a bit of modification to your bootloader and kernel. The -boot-load-size 4 writes information to the ISO that informs the BIOS to read 4 512-byte chunks from the beginning of the disk image inside the ISO. The 0xaa55 boot signature is no longer needed.

If you use -no-emul-boot there is one other snag that needs to be dealt with. On the CD-ROM LBA 0 isn't where the disk image gets placed in the final ISO. The question is, how can you get the LBA where the disk image is in the ISO? You can have XORRISO write this information into a special section of the bootloader you create, and you enable this feature with -boot-info-table.

Creating the special section at the beginning of the bootloader is relatively easy. In the El Torito Specification Supplement they mention this:

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).

This is talking about the 56 bytes at offset 8 of the virtual disk we create holding our bootloader. If we modify the top of your bootloader code to look like this we effectively create a blank boot information 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

When using XORRISO with -boot-info-table this table will be filled in once the ISO is generated. bi_file is the important piece of information we will need since it is the LBA where our disk image is placed inside the ISO. We can use this to fill in the Disk Access Packet used by extended disk reads to read from the proper location of the ISO.

To make the DAP a little more readable and to account for 2048 byte sectors I've amended it to look like:

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

One issue is that the LBA placed into the Boot Information table is from the start of the disk image (sector with our bootloader). We need to increment that LBA by 1 and place it into the DAP so we are using the LBA where our kernel starts. Using 32-bit instruction we can just read the 32-bit value from the Boot Information Table, add 1 and save it to the DAP. If using strictly 16-bit instructions add one to a 32-bit value is more complex. Since we are going into 386 protected mode we can assume instruction with 32-bit operands are supported in real mode. The code to update the DAP with the LBA of the kernel could look like:

    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

The only other issue is that the bootloader sector needs to be padded out to 2048 (the size of a CD-ROM sector) rather than 512 and we can remove the boot signature. Change:

times 510-($-$$) db 0
db 0x55
db 0xAA

To:

times 2048-($-$$) db 0

The modified bootloader code could look like:

[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

You can then modify your original XORRISO command to be:

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
answered on Stack Overflow May 29, 2017 by Michael Petch • edited Sep 15, 2019 by Michael Petch

User contributions licensed under CC BY-SA 3.0