Seb*_* B. 12 x86 assembly protected-mode addressing-mode
我目前正在玩x86 Assember,以提高我的低级编程技能.目前,我在32位保护模式下的寻址方案面临一些问题.
情况如下:
我有一个程序加载在0x7e0,它将CPU切换到保护模式并跳转到代码中的相应标签:
[...]
code to switch CPU in Protected Mode
[...]
jmp ProtectedMode
[...]
bits 32
ProtectedMode:
.halt:
hlt
jmp .halt
Run Code Online (Sandbox Code Playgroud)
到目前为止这个工作绝对正常."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__
Run Code Online (Sandbox Code Playgroud)
并通过加载到GDT寄存器
lgdt [gdtr]
Run Code Online (Sandbox Code Playgroud)
到目前为止我还不明白的是,我现在如何使用GDT跳转到ProtectedMode中的物理地址0x8000?
我的第一个想法是选择代码描述符(CODE_DESC),它应该指向0x7e00(当前程序被加载)并使用必要的偏移量来获得0x8000(512字节),从而产生跳转指令:
jmp CODE_DESC:0x200
Run Code Online (Sandbox Code Playgroud)
但这不起作用.
jmp 0x7e0:0x200
Run Code Online (Sandbox Code Playgroud)
不起作用......
你知道我在这里缺少什么吗?也许我不理解32位ProtectedMode寻址方案和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
Run Code Online (Sandbox Code Playgroud)
Ale*_*nze 13
代码中存在多个问题.
首先,您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
Run Code Online (Sandbox Code Playgroud)
请注意,段限制等于段大小减1.
还有几点......用有效选择器加载所有段寄存器或0.然后,设置堆栈.如果你有垃圾(或实模式的旧值),当你开始玩中断/异常时,你会有更多的崩溃.
最后,我不知道elf64是什么,但你必须org为其他模块处理事情,并确保所有生成的地址都对应于加载地址.如果您打算启用64位模式,那么还有很多工作要做.我建议不要急于进入64位模式,因为你正在绊倒相对简单的东西.