Прыжок ассемблера в защищенном режиме с GDT

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

ситуация следующая:

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

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

jmp ProtectedMode


[...]

bits 32

ProtectedMode:
    .halt:
        hlt
        jmp .halt

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

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

так как процессор теперь находится в 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

2 ответов


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

во-первых, ваш 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.Базы 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-битный режим, так как вы спотыкаетесь об относительно простые вещи.


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

jmp (CODE_DESC-NULL_DESC):ProtectedMode

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

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

jmp 0x8000

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

jmp 0x200

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