Общая ошибка защиты при работе ОС на 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

Но это все еще трижды. Зачем?

2 ответа

Решение

Основная причина всех тройных ошибок в вашем вопросе на самом деле сводится к тому, что ваше ядро ​​не загружается должным образом в память в 0x0000:0x2000. Когда вы передаете управление в это местоположение с помощью JMP, вы в конечном итоге выполняете то, что происходит в области памяти, и ЦП выполняет до тех пор, пока не выполнит команду, вызывающую ошибку.


Загрузочные компакт-диски - это странные звери, которые имеют несколько различных режимов, и существует множество BIOS, которые загружают такие компакт-диски, но у них тоже могут быть свои причуды. Когда вы используете -no-emul-boot с XORRISO вы запрашиваете, чтобы диск не рассматривался как дискета или жесткий диск. Вы могли бы удалить -no-emul-boot -boot-load-size 4 это должно генерировать ISO, который рассматривается как дискета. Проблема заключается в том, что многие настоящие BIOS, эмуляторы (BOCH и QEMU) и виртуальные машины не поддерживают расширенное чтение дисков 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 4 Размеры сектора CDROM составляют 2048 байт, а не 512. Это потребует небольшой модификации вашего загрузчика и ядра. -boot-load-size 4 записывает информацию в ISO, которая сообщает BIOS для чтения 4 512-байтовые фрагменты от начала образа диска внутри ISO. 0xaa55 подпись загрузки больше не нужна.

Если вы используете -no-emul-boot есть еще одна загвоздка, с которой нужно разобраться. На компакт-диске LBA 0 находится не там, где образ диска помещается в окончательный ISO. Вопрос в том, как вы можете получить LBA, где образ диска находится в ISO? Вы можете заставить 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).

Это говорит о 56 байтах по смещению 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 это важная часть информации, которая нам понадобится, поскольку это LBA, где наш образ диска находится внутри ISO. Мы можем использовать это для заполнения пакета доступа к диску, используемого расширенными операциями чтения с диска для чтения из правильного расположения 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, помещенный в таблицу Boot Information, находится в начале образа диска (сектор с нашим загрузчиком). Нам нужно увеличить этот LBA на 1 и поместить его в DAP, чтобы мы использовали LBA, где запускается наше ядро. Используя 32-битную инструкцию, мы можем просто прочитать 32-битное значение из Таблицы загрузочной информации, добавить 1 и сохранить его в DAP. Если использовать строго 16-битные инструкции, добавить одно к 32-битному значению более сложно. Поскольку мы входим в защищенный режим 386, мы можем предположить, что инструкция с 32-битными операндами поддерживается в реальном режиме. Код для обновления DAP с помощью LBA ядра может выглядеть так:

    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

Я разработчик Xorriso. Если image.flp - это образ дискеты с MBR, возможно, таблицей разделов и файловой системой, то намек на Михаэля идет в правильном направлении. El Torito определяет эмуляции, которые позволяют файлу образа загрузки отображаться в BIOS как дискета или жесткий диск.

Опция -no-emul-boot -boot-load-size 4 заставляет BIOS загрузить первые 2048 байт файла image.flp и выполнить их как программу 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 выбирает эту эмуляцию.

Другие вопросы по тегам