Прыжок ассемблера в защищенном режиме с помощью GDT - PullRequest
12 голосов
/ 04 февраля 2012

В настоящее время я играю с 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 в начале) - в результате чего сегмент кода указывает на правильное местоположение.

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

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

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

%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-битного защищенного режима и использовании 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 ]

13 голосов
/ 04 февраля 2012

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

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

3 голосов
/ 04 февраля 2012

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

jmp (CODE_DESC-NULL_DESC):ProtectedMode

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

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

jmp 0x8000

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

jmp 0x200

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

...