Как войти в 32-битный защищенный режим в сборке NASM?

Я изучаю сборку x86 и пытаюсь сделать игрушечную операционную систему в NASM, но кое-чего не понимаю.

Я сделал загрузчик, который успешно загружает мое ядро:

  1. Загружает 14 секторов с дискеты, которая содержит файл ядра;
  2. Поиск файла в этих секторах с пометкой kernel.feo;
  3. Загружает этот файл в память в смещение 0x2000;
  4. Выполняет ядро, используя дальний переход jmp 0x2000:0x0000,

Так что у меня есть код ядра, расположенный на 0x2000:0 в памяти. CS может быть правильно установлен, потому что использование прыжка в длину. В этом коде ядра я хочу войти в 32-битный защищенный режим, но я не уверен, как работают GDT. Когда я запускаю приведенный ниже код на виртуальной машине (QEMU)это ничего не делать.

Я хочу порадовать вас, чтобы помочь мне войти в 32-битный защищенный режим!

Тем не менее, у вас есть следующие проблемы:

  1. Вы предполагаете, что код загружен в 0x7c00:0 из-за org 0, но это может быть не так. Гарантируется только физический адрес. Вы должны использовать дальний переход к точке входа, чтобы CS правильно установлено.
  2. Вы по каким-то причинам настраиваете DS в 0x2000 поэтому ваш код не найдет никаких данных вообще. Вы должны установить DS соответствовать CSили используйте CS переопределить везде (не рекомендуется).
  3. Код защищенного режима предполагает нулевой сегмент, что, в свою очередь, означает, что он ожидает org 0x7c00 что, конечно, конфликтует с вашей настройкой. Вы должны переключиться на org 0x7c00 и сегменты 0,
  4. Сегмент текстового режима VGA находится на 0xb8000 не 0xb80000 (на один меньше нуля).
  5. У вас нет байтов подписи загрузки 0x55 0xaa в конце загрузочного сектора.

Я исправил эти вещи в своем коде:

  1. [org 0x0] исправлен на [org 0x2000] и сегменты установлены в 0;
  2. DS исправлен на 0 вместо 0x2000так что теперь он совпадает с CS;
  3. Сегмент текстового режима VGA исправлен на 0xb8000;

Но код не будет работать с этими исправлениями, он должен напечатать две строки, но он ничего не делает!

Обратите внимание, что этот код ядра не должен заканчиваться загрузочной подписью 0x55 0xAAпотому что это не загрузочный сектор.

Вот исправленный код ядра (который не работает):

[bits 16]
[org 0x2000]

    jmp 0:kernel_start

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

print:
    mov ah, 14
    mov bh, 0
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp print
.done:
    ret

uzenet_real db 'uzenet16', 0
uzenet_prot db 'uzenet32', 0

kernel_start:
    mov ax, 0
    mov ss, ax
    mov sp, 0xFFFC

    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov si, uzenet_real
    call print

    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

    mov ebp, 0x90000
    mov esp, ebp

    mov ebx, uzenet_prot
    call print32

    jmp $

1 ответ

Программирование ОС - сложная задача. Вы, по крайней мере, ожидаете, что сможете использовать отладчик, чтобы найти свои собственные ошибки и понять основные вещи. Возможно, вы захотите пересмотреть, есть ли у вас все предпосылки для этого усилия.

Тем не менее, у вас есть следующие проблемы:

  1. Вы предполагаете, что код загружен в 0x7c00:0 из-за org 0, но это может быть не так. Гарантируется только физический адрес. Вы должны использовать дальний переход к точке входа, чтобы CS правильно установлено.
  2. Вы по каким-то причинам настраиваете DS в 0x2000 поэтому ваш код не найдет никаких данных вообще. Вы должны установить DS соответствовать CSили используйте CS переопределить везде (не рекомендуется).
  3. Код защищенного режима предполагает нулевой сегмент, что, в свою очередь, означает, что он ожидает org 0x7c00 что, конечно, конфликтует с вашей настройкой. Вы должны переключиться на org 0x7c00 и сегменты 0,
  4. Сегмент текстового режима VGA находится на 0xb8000 не 0xb80000 (на один меньше нуля).
  5. У вас нет байтов подписи загрузки 0x55 0xaa в конце загрузочного сектора.

Фиксированный код:

[bits 16]
[org 0x7c00]

    jmp 0:kernel_start

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

print:
    pusha
    mov ah, 14
    mov bh, 0
.loop:
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp .loop
.done:
    popa
    ret

uzenet16 db 'uzenet16', 0
uzenet32 db 'uzenet32', 0

kernel_start:
    mov ax, 0
    mov ss, ax
    mov sp, 0xFFFC

    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov si, uzenet16
    call print

    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

    mov ebp, 0x2000
    mov esp, ebp

    mov ebx, uzenet32
    call print32

    jmp $

[SECTION signature start=0x7dfe]
dw 0AA55h

Ваш обновленный вопрос все еще кажется запутанным по поводу того, где загружен код: вы говорите offset 0x2000 но тогда поговорим о Executes the kernel using a far jump jmp 0x2000:0x0000 что, конечно, неправильно, потому что у него есть еще один ноль в сегменте, и в любом случае он должен быть дальним прыжком с нулевого сегмента: jmp 0:0x2000, Кроме этого, убедитесь, что ваш код действительно загружен в память в правильном месте. Научитесь пользоваться отладчиком.

Вот небольшой загрузочный сектор, который загружает вышеуказанный код из второго сектора по адресу 0x2000, Он работает нормально, проблема не в GDT, особенно если вы даже не печатаете сообщение в реальном режиме (вы тоже не поняли).

[bits 16]
[org 0x7c00]
mov ax, 0201h
mov cx, 0002h
mov dh, 0
mov bx, 0
mov es, bx
mov bx, 2000h
int 13h
jmp 0:2000h

[SECTION signature start=0x7dfe]
dw 0AA55h
Другие вопросы по тегам