x86保护模式下GDT表的使用--使用更大的内存(代码实现)

GDT表

这一部分在手册第三章
视频讲解可以看这一个课程

不使用GDT表之前段寄存器保存的是一个地址基地址, 左移四位以后加上偏移是实际地址这样可以操控的内存实际比较小, 进入32位模式以后不够用

所以x86的CPU使用GDT表进行保存一个地址的基地址, 可以存储更高的基地址, 用于使用高位的内存

CPU进入保护模式需要一些设置, 首先需要一个GDP表, 还需要修改一个寄存器开启使用GDT表, 然后修改段寄存器的设置, 开启以后段寄存器实际保存的是GDT表的偏移, 这个表的位置保存在GDTR寄存器里面, 表里面记录有大小, 位置, 权限等

在进入32位的模式以后段寄存器记录的值实际上是这一个表里面的偏移, 使用某一段记录的信息进行寻址

每一个字段是8字节

这一个表里面的信息除了可以用来记录地址段, 还可以用来进行记录任务切换信息TSS表的位置等

表项的格式

image-20230911165013102

Base: 指明段的地址

limit: 段的长度

S: 0的时候是系统段, TSS/LDT等, 1的时候表示这一段是数据段或者代码段

DPL: 段的访问权限, 0-3

P: 这一个段是否有效

D/B: 代码段的时候制定了操作数和地址是32位还是16位, 栈的时候指定了栈是32位还是16位

G: 指定limit的单位是byte还是4KB

L: 64位下面使用

AVL: 保留

type: 段的类型

image-20230911170239279

  • 第一个表段必须为0
  • 进入模式的时候需要指定一个代码段和一个数据段

实际使用(设置一个代码段一个数据段)

0xcf9a: 1100 1111 1001 1010

// 各选择子
#define KERNEL_CODE_SEG         (1 * 8)
#define KERNEL_DATA_SEG         (2 * 8)
//任务段, 设置任务等级为3  之后由于使用LDT便不再使用
#define APP_CODE_SEG            ((3 * 8) | 3)
#define APP_DATA_SEG            ((4 * 8) | 3)

//任务的TSS保存位置
#define TASK0_TSS_SEG           ((5 * 8))
#define TASK1_TSS_SEG           ((6 * 8))
//系统调用
#define SYSCALL_SEG             ((7 * 8))
//保存两个LDT的描述
#define TASK0_LDT_SEG           ((8 * 8))
#define TASK1_LDT_SEG           ((9 * 8))


//这里是任务的LDT的配置
#define TASK_CODE_SEG           (0 * 8 | 0x4 | 3)
#define TASK_DATA_SEG           (1 * 8 | 0x4 | 3)

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
//这一个表需要进行八字节对齐
struct {uint16_t limit_l, base_l, basehl_attr, base_limit;}gdt_table[256] __attribute__((aligned(8))) = {
    // 0x00cf9a000000ffff - 从0地址开始,P存在,DPL=0,Type=非系统段,32位代码段(非一致代码段),界限4G,
    [KERNEL_CODE_SEG / 8] = {0xffff, 0x0000, 0x9a00, 0x00cf},
    // 0x00cf93000000ffff - 从0地址开始,P存在,DPL=0,Type=非系统段,数据段,界限4G,可读写
    [KERNEL_DATA_SEG/ 8] = {0xffff, 0x0000, 0x9200, 0x00cf},
};

这里是初步初始化一个GDT表

开启并使用

  • 设置GDT的位置之后需要将CR0的最低位PE设置为1

image-20240204212101934

lmsw: 将源操作数加载到机器状态字,即寄存器 CR0 的位 0 到 15。源操作数可以是 16 位通用寄存器或内存位置。只有源操作数的低 4 位(也就是 PE、MP、EM 及 TS 标志)会加载到 CR0。CR0 的 PG、CD、NW、AM、WP、NE 及 ET 标志不受影响。操作数大小属性不影响此指令。

  • 之后需要进行一次跳转, 跳转位置是选择子+偏移量, 用于清除流水线

image-20240204212645492

	//进入保护模式
	//关中断
	cli
	//加载新的GDT表
	lgdt gdt_desc
	//设置CR0的0位, 操作的时候使用16位的操作寄存器
	mov $1, %eax
	lmsw %ax
	//跳转到内核代码段,进入32位模式,第二个数字是偏移量,也就是C语言程序被复制到的位置, 这一个会改变cs
	jmp $(KERNEL_CODE_SEG),$_start_32
	jmp .

	//以下是标志位
	.org 0x1fe				//十进制是510,在这里跳转到对应的位置
	.byte 0x55, 0xaa
	
	//标记下面是32位, 以及是代码段
	.code32
	.text
_start_32:
	//在这里设置段地址
	mov $KERNEL_DATA_SEG, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %ss
	mov %ax, %gs
	mov %ax, %fs
	mov $_start, %esp
	jmp .

//这里记录的是GDT表的数据,包括32位的基地址以及16位的大小界限
gdt_desc:
	//界限+地址
	.word (256*8)-1
	.long gdt_table

这个gdt_desc的值会放到GDTR寄存器里面, 是一个48位的寄存器, 用来描述GDT表的格式

image-20240204211734186

  • 调试

image-20230911213220046

image-20230911213417535

使用命令info registers

image-20230911213826872

进入32位模式

  • 37
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值