gpt4 book ai didi

assembly - 使用 GDT 保护模式下的汇编器跳转

转载 作者:行者123 更新时间:2023-12-02 20:56:05 28 4
gpt4 key购买 nike

我目前正在使用 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 使用描述符选择器在描述符表中查找基地址和限制,以添加给定的偏移量并检索物理地址(据我所知)。因此,在进入保护模式之前有必要安装一个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]

到目前为止我不明白的是,现在如何使用 GDT 在 ProtectedMode 下跳转到物理地址 0x8000?

我的第一个想法是选择代码描述符(CODE_DESC),它应该指向 0x7e00(当前程序已加载)并使用到达 0x8000(512 字节)所需的偏移量,从而产生跳转指令:

jmp CODE_DESC:0x200

但这不起作用。

jmp 0x7e0:0x200 

也不起作用...

你知道我在这里缺少什么吗?也许我不理解 32 位保护模式寻址方案中的一些基本内容以及 GDT 的用法。

[编辑]完整代码:

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

最佳答案

代码中存在多个问题。

首先,您的 GDTR.Base 包含 GDT 从代码开头的偏移量,因为您的代码被编译为从地址 0 开始(因为 org 0)。基地址必须是物理地址,而不是相对地址。 IOW,如果您保留此 org 0,则必须将 CS*16 (=0x7e00) 添加到 Base

其次,由于相同的 org 0,代码中的 32 位偏移量(在 bits 32ProtectedMode: 之后)是不等于它们对应的物理地址,它们比物理地址小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 位模式,因为您会被相对简单的东西绊倒。

关于assembly - 使用 GDT 保护模式下的汇编器跳转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9137947/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com