Bochs: сборщик удаленных прыжков потерялся в области фальшивой памяти (неверная ошибка кода операции)


0

Я начал разрабатывать небольшую игрушку в сборке (NASM), только для моего развлечения. Я написал загрузчик, который загружает первый (и только один) файл из файловой системы FAT12 под названием «kernel.sys» в память со смещением 0x7E00. В реальном режиме ядро ​​устанавливает только соответствующий режим видео через BIOS и переходит в 32-разрядный (защищенный) режим. И в этом и заключается моя проблема.

Прежде всего, я установил GDT с 3 дескрипторами (нуль, код кольца 0, данные кольца 0), и я загружаю его непосредственно в область памяти 0x0500. Затем я использую команду LGDT, чтобы рассказать об этом процессору, затем я установил бит PE в регистр CR0, и я хочу войти в защищенный режим с даун-прыжком, чтобы установить соответствующий сегмент (0x08 - сегмент кода в GDT) и указатель команд ,

Первая версия этого была обработана в QEMU, но не в Bochs. Bochs необходимо было установить сегменты перед дальним прыжком, поэтому я изменил это в своем коде: непосредственно перед дальним прыжком я загружаю селектора с сегментом данных из своего GDT. Но, Bochs по-прежнему не могут войти в защищенный режим из-за ошибки «неправильный код операции».

Пожалуйста, помогите решить эту проблему!

Вот мой код ядра: (! Обратите внимание, что метка b32 никогда не достиг)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;                 ;; 
;;       16-BIT ENTRY       ;; 
;;                 ;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 

use16 
org 0x7e00 
jmp start 

sys_gdt  equ 0x00000500 
sys_gdt_ring0c equ 0x00000508 
sys_gdt_ring0d equ 0x00000510 
sys_gdtr  equ 0x00000518 

start: 
    cli 

    mov ax, 0 
    mov ds, ax 
    mov es, ax 
    mov fs, ax 
    mov gs, ax 
    mov ss, ax 
    mov sp, 0x1000 
    sti 

    mov ax, 3 
    int 0x10 

set_a20: 
    in al, 0x64 
    test al, 2 
    jnz set_a20 

    mov al, 0xd1 
    out 0x64, al 

test_a20: 
    in al, 0x64 
    test al, 2 
    jnz test_a20 

    mov al, 0xdf 
    out 0x60, al 

    mov dword [sys_gdt+0], 0 
    mov dword [sys_gdt+4], 0 

    mov word [sys_gdt_ring0c+0], 0xffff 
    mov word [sys_gdt_ring0c+2], 0 
    mov byte [sys_gdt_ring0c+4], 0 
    mov byte [sys_gdt_ring0c+5], 10011010b 
    mov byte [sys_gdt_ring0c+6], 01001111b 
    mov byte [sys_gdt_ring0c+7], 0 

    mov word [sys_gdt_ring0d+0], 0xffff 
    mov word [sys_gdt_ring0d+2], 0 
    mov byte [sys_gdt_ring0d+4], 0 
    mov byte [sys_gdt_ring0d+5], 10010010b 
    mov byte [sys_gdt_ring0d+6], 01001111b 
    mov byte [sys_gdt_ring0d+7], 0 

    mov word [sys_gdtr+0], sys_gdtr-sys_gdt-1 
    mov dword [sys_gdtr+2], sys_gdt 

    cli 
    lgdt [sys_gdtr] ;; :96 

    mov eax, cr0 
    or eax, 0x1 
    mov cr0, eax 

    mov ax, 0x10 
    mov ds, ax 
    mov es, ax 
    mov fs, ax 
    mov gs, ax 
    mov ss, ax 
    mov esp, 0x90000 

    jmp 0x08:b32 

use32 

b32: 
    mov cx, 5 
    jmp $ 

Здесь необходимо войти в Bochs:

00014041550i[BIOS ] Booting from 0000:7c00 
00015625085e[CPU0 ] write_virtual_checks(): write beyond limit, r/w 
00015625085i[CPU0 ] CPU is in protected mode (active) 
00015625085i[CPU0 ] CS.d_b = 32 bit 
00015625085i[CPU0 ] SS.d_b = 32 bit 
00015625085i[CPU0 ] EFER = 0x00000000 
00015625085i[CPU0 ] | RAX=0000000060000010 RBX=0000000000000204 
00015625085i[CPU0 ] | RCX=0000000000090000 RDX=0000000000000fff 
00015625085i[CPU0 ] | RSP=0000000000090000 RBP=0000000000000000 
00015625085i[CPU0 ] | RSI=00000000000e018e RDI=0000000000008000 
00015625085i[CPU0 ] | R8=0000000000000000 R9=0000000000000000 
00015625085i[CPU0 ] | R10=0000000000000000 R11=0000000000000000 
00015625085i[CPU0 ] | R12=0000000000000000 R13=0000000000000000 
00015625085i[CPU0 ] | R14=0000000000000000 R15=0000000000000000 
00015625085i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af PF cf 
00015625085i[CPU0 ] | SEG selector  base limit G D 
00015625085i[CPU0 ] | SEG sltr(index|ti|rpl)  base limit G D 
00015625085i[CPU0 ] | CS:0008(0001| 0| 0) 00000000 000fffff 0 1 
00015625085i[CPU0 ] | DS:0010(0002| 0| 0) 00000000 000fffff 0 1 
00015625085i[CPU0 ] | SS:0010(0002| 0| 0) 00000000 000fffff 0 1 
00015625085i[CPU0 ] | ES:0010(0002| 0| 0) 00000000 000fffff 0 1 
00015625085i[CPU0 ] | FS:0010(0002| 0| 0) 00000000 000fffff 0 1 
00015625085i[CPU0 ] | GS:0010(0002| 0| 0) 00000000 000fffff 0 1 
00015625085i[CPU0 ] | MSR_FS_BASE:0000000000000000 
00015625085i[CPU0 ] | MSR_GS_BASE:0000000000000000 
00015625085i[CPU0 ] | RIP=0000000000007ebb (0000000000007eb9) 
00015625085i[CPU0 ] | CR0=0x60000011 CR2=0x0000000000000000 
00015625085i[CPU0 ] | CR3=0x00000000 CR4=0x00000000 
00015625085i[CPU0 ] 0x0000000000007eb9>> add byte ptr ds:[eax], al : 0000 
00015625085i[CMOS ] Last time is 1459506108 (Fri Apr 1 12:21:48 2016) 
00015625085i[  ] restoring default signal behavior 
00015625085i[CTRL ] quit_sim called with exit code 1 

Вот мой Загрузчик:

use16 
jmp start 

    OEMLabel   db 'SYRACUSE' 
    BytesPerSector dw 512 
    SectorsPerCluster db 1 
    ReservedForBoot dw 1 
    NumberOfFats  db 2 
    RootDirEntries dw 224 
    LogicalSectors dw 2880 
    MediumByte  db 0xf0 
    SectorsPerFat  dw 9 
    SectorsPerTrack dw 18 
    Heads    dw 2 
    HiddenSectors  dd 0 
    LargeSectors  dd 0 
    DriveNo   dw 0 
    Signature   db 41 
    VolumeID   dd 0 
    VolumeLabel  db 'Syracuse1.0' 
    FileSystem  db 'FAT12 ' 

chs_lba: 
    sub ax, 2 
    xor cx, cx 
    mov cl, byte [SectorsPerCluster] 
    mul cx 
    add ax, word [datasector] 
    ret 

lba_chs: 
    xor dx, dx 
    div word [SectorsPerTrack] 
    inc dl 
    mov byte [absoluteSector], dl 
    xor dx, dx 
    div word [Heads] 
    mov byte [absoluteHead], dl 
    mov byte [absoluteTrack], al 
    ret 

print: 
    pusha 
    mov ah, 0xe 
.repeat: 
    lodsb 
    cmp al, 0 
    je .done 
    int 0x10 
    jmp short .repeat 
.done: 
    popa 
    ret 

read_sectors: 
    mov di, 5 
.loop: 
    pusha 

    call lba_chs 
    mov ah, 2 
    mov al, 1 
    mov ch, byte [absoluteTrack] 
    mov cl, byte [absoluteSector] 
    mov dh, byte [absoluteHead] 
    mov dl, byte [DriveNo] 
    int 0x13 
    jnc .done 

    xor ax, ax 
    int 0x13 

    dec di 
    popa 
    jnz .loop 
    int 0x18 
.done: 
    popa 

    inc ax 
    add bx, word [BytesPerSector] 
    loop read_sectors 
    ret 

start: 
    cli 
    mov ax, 0x07c0 
    mov ds, ax 
    mov es, ax 
    mov fs, ax 
    mov gs, ax 

    mov ax, 0 
    mov ss, ax 
    mov sp, 0xffff 
    sti 

load_root: 
    xor cx, cx 
    xor dx, dx 
    mov ax, 32 
    mul word [RootDirEntries] 
    div word [BytesPerSector] 
    xchg ax, cx 

    mov al, byte [NumberOfFats] 
    mul word [SectorsPerFat] 
    add ax, word [ReservedForBoot] 
    mov word [datasector], ax 
    add word [datasector], cx 

    mov bx, 0x0200 
    call read_sectors 

    mov cx, word [RootDirEntries] 
    mov di, 0x0200 

.loop: 
    push cx 

    mov cx, 11 
    mov si, kernel 

    push di 
    rep cmpsb 
    pop di 
    je load_fat 

    pop cx 
    add di, 32 
    loop .loop 

    jmp failure 

load_fat: 
    mov dx, word [di+0x001a] 
    mov word [cluster], dx 

    xor ax, ax 
    mov al, byte [NumberOfFats] 
    mul word [SectorsPerFat] 
    mov cx, ax 

    mov ax, word [ReservedForBoot] 

    mov bx, 0x0200 
    call read_sectors 

    mov ax, 0x7e00 
    mov es, ax 
    mov bx, 0x0000 

load_kernel: 
    mov ax, word [cluster] 
    call chs_lba 
    xor cx, cx 
    mov cl, byte [SectorsPerCluster] 
    call read_sectors 

    mov ax, word [cluster] 
    mov cx, ax 
    mov dx, ax 
    shr dx, 1 
    add cx, dx 
    mov bx, 0x0200 
    add bx, cx 
    mov dx, word [bx] 
    test ax, 1 
    jnz .odd 

.even: 
    and dx, 0000111111111111b 
    jmp .done 

.odd: 
    shr dx, 4 

.done: 
    mov word [cluster], dx 
    cmp dx, 0x0ff0 
    jb load_kernel 

    pusha 
    mov di, 0x7e00 
    xor ax, ax 
    mov cx, 512 
    rep stosb 


execute_kernel: 
    ;push word 0x7e00 
    ;push word 0x0000 
    ;retf 

    jmp 0x7e00:0x0000 

failure: 
    mov si, msg 
    call print 

    mov ah, 0 
    int 0x16 
    int 0x19 

absoluteSector db 0 
absoluteHead db 0 
absoluteTrack db 0 

datasector dw 0 
cluster dw 0 

kernel db 'KERNEL SYS' 
msg db 'MISSING KERNEL. Press any key to reboot...', 0xA, 0xD, 0 

times 510-($-$$) db 0 
dw 0xAA55 
  0

Хорошо работает здесь, в боч 2.6.8. Надеюсь, когда вы сказали, что загрузили адрес «0x7E00: 0x0000», это была просто опечатка. 01 апр. 162016-04-01 10:56:16

  0

Ох. Я загрузил его для смещения '0x7E00'. Все еще работает на вас? 01 апр. 162016-04-01 11:02:57

  0

Ну, у меня нет вашего загрузчика, поэтому я загрузил его в '0x7C00' в качестве загрузочного сектора, но это работает, и я не понимаю, почему он не будет работать нигде. Кроме того, проверьте свой список/разборку, чтобы узнать, что находится на '7eb9', потому что, по-видимому, кажется, что у вас есть' 00 00' (это место ошибки). Не говоря уже просто используйте встроенный отладчик и один шаг. 01 апр. 162016-04-01 11:04:51

  0

Хм. Я добавил свой загрузочный код. Я думаю, '7eb9' - это адрес, где метка' b32' находится в физической памяти. 01 апр. 162016-04-01 11:09:39

  0

Нет, вы загрузили его в ** сегмент ** '0x7E00', который является физическим адресом' 0x7E000', но код ожидает физический адрес '0x7E00'. 01 апр. 162016-04-01 12:31:17

  0

Я мог бы быть немного глупым, но я тебя не понимаю. Тогда что мне делать? 01 апр. 162016-04-01 12:35:50

2

Вы знаете, что адресация реального режима использует 16*segment+offset как ph ysical адрес, не так ли? Вы загружаете код по адресу 0x7E00:0000, что, таким образом, является физическим адресом 0x7E000 (уведомление 3 нуля). Но ваше ядро ​​ожидает адрес 0x7E00 (обратите внимание на 2 ноля).

Ваш код вдвойне ошибочен. Во-первых, вы фактически переходите к офсету 0, поэтому вы должны использовать org 0 (что по умолчанию). Во-вторых, физический адрес должен быть скорректирован для сегмента реального режима, то есть jmp dword 0x8:b32+0x7e000. Это исправит текущий код, но 32-разрядная часть снова будет использовать неправильный org.

Вы делаете свою жизнь излишне сложной. Обычная передовая практика заключается в том, чтобы загрузить код в адрес в течение первых 64k, где вы можете использовать смещения сегмента 0 и 16 бит, которые отображаются непосредственно в физическую память как в реальном, так и в плоском защищенном режиме. Поэтому я предлагаю загрузить, скажем, 0:0x8000.

  0

О, действительно. Как вы уже сказали, '0: 0x8000' все еще работает, но до сих пор у меня есть вопрос. Мы знаем, что загрузчик загружает ядро ​​в адрес, обозначенный 'ES: BX', поэтому если' ES = 0' и 'BX = 0x8000', а ядро ​​содержит' ORG 0x8000', код полностью работает. Но если я устанавливаю 'ES: BX = 0: 0x7e00' и' ORG 0x7e00', то мое ядро ​​просто не работает, но почему? 01 апр. 162016-04-01 13:34:54

  0

Не знаете, как выглядит ваш код сейчас, но оригинал обнулял блок на '0x7e00', чтобы очистить загруженное ядро;) Не знаю, почему вы это сделали. 01 апр. 162016-04-01 13:50:13

  0

Я имел в виду, что уже был удален в моем текущем коде загрузчика, что команда 'rep stosb' уже ушла. Но если я загружу ядро ​​в '0: 0x7e00', это не работает и не сейчас. Я также исправил «ORG» в ядре. 01 апр. 162016-04-01 14:04:10

  0

Потому что вы загружаете FAT на то же место, может быть? 01 апр. 162016-04-01 14:20:43

  0

Нет. Я думаю, вы сказали это, потому что есть указатель на '0: 0x7e00' под меткой' load_fat', но, если вы видите, что более точно указываете, вы заметите, что код будет указывать 'ES: BX' на адрес, в котором ядро ​​будет загружено, точно. 01 апр. 162016-04-01 16:11:45

  0

Поверьте мне, вы загружаете FAT в '0x7e00'. По крайней мере, в приведенном выше коде, вы делаете. В частности, к '0x7c0: 0x200', который, очевидно, равен« 0x7e00 ». Это означает, что после того, как вы приобретете один сектор вашего ядра, загрузчик попытается использовать это как FAT и потерпеть неудачу. Научитесь использовать отладчик. 01 апр. 162016-04-01 16:16:33