Общая ошибка защиты при работе ОС на 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 выбирает эту эмуляцию.