Прыжок ассемблера в защищенном режиме с 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
дополнительная информация о входе в защищенный режим