Подтвердить что ты не робот

Ассемблер переходит в защищенный режим с помощью GDT

В настоящее время я играю с x86 Assember, чтобы улучшить свои навыки программирования на низком уровне. В настоящее время у меня возникла небольшая проблема с схемой адресации в 32-битном защищенном режиме.

Ситуация такова:

У меня есть программа, загруженная с 0x7e0, которая переключает CPU в защищенный режим и переходит к соответствующей метке в коде:

[...]
code to switch CPU in Protected Mode
[...]

jmp ProtectedMode


[...]

bits 32

ProtectedMode:
    .halt:
        hlt
        jmp .halt

Пока все работает отлично. "Jmp ProtectedMode" работает без явного далекого перехода, чтобы очистить очередь предварительной выборки, поскольку эта программа загружается со смещением 0 (org 0 в начале), заставляя сегмент кода указывать на нужное место.

Теперь моя текущая проблема заключается в том, что в ярлыке "ProtectedMode" я хочу перейти к другой программе, загруженной с 0x8000 (я проверил это с дампом памяти, функция загрузки работала правильно и программа загружалась правильно до 0x8000).

Поскольку CPU теперь находится в ProtectedMode, а не в RealMode, схема адресации отличается. ProtectedMode использует дескрипторы для поиска базового адреса и лимита в таблице дескриптора, чтобы добавить данное смещение и получить физический адрес (как я понял). Поэтому перед входом ProtectedMode необходимо было установить GDT.

Моя выглядит следующим образом:

%ifndef __GDT_INC_INCLUDED__
%define __GDT_INC_INCLUDED__

;*********************************
;* Global Descriptor Table (GDT) *
;*********************************
NULL_DESC:
    dd 0            ; null descriptor
    dd 0

CODE_DESC:
    dw 0xFFFF       ; limit low
    dw 0            ; base low
    db 0            ; base middle
    db 10011010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

DATA_DESC:
    dw 0xFFFF       ; data descriptor
    dw 0            ; limit low
    db 0            ; base low
    db 10010010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

gdtr:
    Limit dw 24         ; length of GDT
    Base dd NULL_DESC   ; base of GDT

%endif ;__GDT_INC_INCLUDED__

и загружается в регистр GDT через

lgdt [gdtr]

То, что я до сих пор не понял, как мне теперь перейти на физический адрес 0x8000 в ProtectedMode с помощью GDT?

Мои первые мысли заключались в том, чтобы выбрать дескриптор кода (CODE_DESC), который должен указывать на 0x7e00 (были загружены текущие программы) и использовать смещение, необходимое для получения 0x8000 (512 байт), в результате чего команда перехода:

jmp CODE_DESC:0x200

Но это не работает.

jmp 0x7e0:0x200 

тоже не работает...

Есть ли у вас какие-то идеи, что мне здесь не хватает? Возможно, я не понял чего-то существенного в схеме адресации 32-бит ProtectedMode и использовании GDT.

[EDIT] Полный код:

bits 16
org 0                       ; loaded with offset 0000 (phys addr: 0x7e00)

jmp Start

Start:
    xor ax, ax
    mov ax, cs
    mov ds, ax              ; update data segment

    cli                     ; clear interrupts

    lgdt [gdtr]             ; load GDT from GDTR (see gdt_32.inc)

    call OpenA20Gate        ; open the A20 gate 

    call EnablePMode        ; jumps to ProtectedMode

;******************
;* Opens A20 Gate *
;******************
OpenA20Gate:
    in al, 0x93         ; switch A20 gate via fast A20 port 92

    or al, 2            ; set A20 Gate bit 1
    and al, ~1          ; clear INIT_NOW bit
    out 0x92, al

    ret

;**************************
;* Enables Protected Mode *
;**************************
EnablePMode:
    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp ProtectedMode ; this works (jumps to label and halts)
    ;jmp (CODE_DESC-NULL_DESC):ProtectedMode ; => does not work
    ;jmp 08h:ProtectedMode , => does not work

;***************
;* data fields *
;*  &includes  *
;***************
%include "gdt_32.inc"

;******************
;* Protected Mode *
;******************
bits 32

ProtectedMode:
    ;here I want to jump to physical addr 0x8000 (elf64 asm program)

    .halt:
        hlt
        jmp .halt
4b9b3361

Ответ 1

В коде есть несколько проблем.

Во-первых, ваш GDTR.Base содержит смещение GDT от начала кода, так как ваш код скомпилирован для начала с адреса 0 (из-за org 0). Базовый адрес должен быть физическим адресом, а не относительным адресом. IOW, если вы сохраните этот org 0, вы должны добавить CS * 16 (= 0x7e00) в Base.

Во-вторых, из-за того же org 0, 32-битные смещения в вашем коде (после bits 32 и ProtectedMode:) не равны физическим адресам, которым они соответствуют, они на 0x7e00 меньше физического адреса. OTOH, сегменты, определенные в вашем GDT, начинаются с физического адреса 0 (поскольку базовые части записей GDT равны 0), а не 0x7e00. Это означает, что при попытке использовать эти сегменты с кодом/данными вы будете пропускать адреса 0x7e00. Если вы хотите сохранить org 0, базовые адреса в GDT должны быть установлены в 0x7e00.

Или вы можете изменить org 0 на org 0x7e00, а затем базы в GDT должны быть равны 0. И вам не нужно будет настраивать GDTR.Base на 0x7e00, 0 будет делать.

Это должно работать:

bits 16
org 0x7e00                  ; loaded at phys addr 0x7e00
                            ; control must be transferred with jmp 0:0x7e00

    xor ax, ax
    mov ds, ax              ; update data segment

    cli                     ; clear interrupts

    lgdt [gdtr]             ; load GDT from GDTR (see gdt_32.inc)

    call OpenA20Gate        ; open the A20 gate 

    call EnablePMode        ; jumps to ProtectedMode

;******************
;* Opens A20 Gate *
;******************
OpenA20Gate:
    in al, 0x93         ; switch A20 gate via fast A20 port 92

    or al, 2            ; set A20 Gate bit 1
    and al, ~1          ; clear INIT_NOW bit
    out 0x92, al

    ret

;**************************
;* Enables Protected Mode *
;**************************
EnablePMode:
    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp (CODE_DESC - NULL_DESC) : ProtectedMode

;***************
;* data fields *
;*  &includes  *
;***************
;%include "gdt_32.inc"
;*********************************
;* Global Descriptor Table (GDT) *
;*********************************
NULL_DESC:
    dd 0            ; null descriptor
    dd 0

CODE_DESC:
    dw 0xFFFF       ; limit low
    dw 0            ; base low
    db 0            ; base middle
    db 10011010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

DATA_DESC:
    dw 0xFFFF       ; limit low
    dw 0            ; base low
    db 0            ; base middle
    db 10010010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

gdtr:
    Limit dw gdtr - NULL_DESC - 1 ; length of GDT
    Base dd NULL_DESC   ; base of GDT

;******************
;* Protected Mode *
;******************
bits 32

ProtectedMode:
    mov     ax, DATA_DESC - NULL_DESC
    mov     ds, ax ; update data segment

    .halt:
        hlt
        jmp .halt

Обратите внимание, что предел сегмента равен размеру сегмента минус 1.

Еще несколько точек... Загрузите все регистры сегментов с допустимыми селекторами или 0. Также настройте стек. Если у вас есть мусор (или старые значения из реального режима), когда вы начинаете играть с прерываниями/исключениями, вы получите больше сбоев.

Наконец, я не знаю, что такое elf64, но вам придется позаботиться о org для других модулей и убедиться, что все сгенерированные адреса соответствуют адресам загрузки. И если вы намереваетесь включить 64-битный режим, там будет много работы. Я бы посоветовал не спешить в 64-битный режим, так как вы отключаете относительно простые вещи.

Ответ 2

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

jmp (CODE_DESC-NULL_DESC):ProtectedMode

Во-вторых, база для вашего сегмента кода равна 0, а не 0x7e00. Если вы посмотрите на четыре байта с надписью "base", все они равны 0. У вас есть два варианта. Либо измените свой GDT на базу 0x7e00, либо добавьте директивы, чтобы изменить адрес загрузки для всего кода защищенного режима для базы 0.

Как только вы выполнили обе эти вещи, вы можете перейти к своей программе с помощью обычной инструкции перехода. Если вы решите оставить свой GDT как есть, вы должны использовать полный адрес:

jmp 0x8000

Если вы решите изменить базу своего сегмента кода, вам нужно будет использовать адрес относительно этого.

jmp 0x200

Дополнительная информация о GDT
Дополнительная информация о входе в защищенный режим