我目前正在尝试使用x86汇编语言来提高我低级编程技能。 目前,我在32位保护模式下的寻址方案中遇到了一些小问题。
情况如下:
我有一个程序加载在0x7e0处,它将CPU切换到保护模式并跳转到代码中相应的标签:
[...]
code to switch CPU in Protected Mode
[...]
jmp ProtectedMode
[...]
bits 32
ProtectedMode:
.halt:
hlt
jmp .halt
到目前为止这个程序完美运行,"jmp ProtectedMode"可以正常工作而不需要显式的far jump指令去清除预取队列 – 因为这个程序是从offset 0(在开始时的org 0)处加载,所以代码端口自然指向正确的位置。
现在我的问题是,在“ProtectedMode”标签中,我想要跳转到另一个程序,该程序已经被加载至0x8000(通过内存转储检查过了,加载函数也顺利地将程序正确地加载到了0x8000)。
由于CPU现在处于保护模式而不是实模式,地址寻址机制不同了。保护模式使用描述符选择器在一个描述符表中查找基址和限制,并将给定的偏移量加上,以获取物理地址(根据我的理解)。因此,在进入保护模式之前安装GDT是必要的。
我的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跳转到物理地址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