admin管理员组

文章数量:1516870

疑问: CR3 与 PDE是不同的东西吗?

1 建立 4M的PDE 表

2 初始化第0个表项

3 打开页表

4 配置cr3 寄存器

5 配置cr4 寄存器

6 配置cr0 寄存器

注意 cr0 寄存器的打开页表必须是 在cr4 之后。否则报错

7定义二级页表 , 映射 80000000 地址。

cr4 寄存器的设置

cr0 寄存器的最高位 打开分页机制。

os.c 代码

#include "os.h"
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
//关于也表的宏定义  
#define PDE_P   (1<<0)       //4M 的页必须为1
#define PDE_W    (1<<1)         //读写权限
#define PDE_U    (1<<2)             // superviser 模式
#define PDE_PS    (1<<7)            //一级页表必须为1
//定义要映射的地址
#define MAP_ADDR  (0x80000000)
//定义一个用于 0x80000000 的映射的内存
//定义的一个4k大小的内存,存在与 0-4M 的物理内存
uint8_t map_phy_buffer[4096] __attribute__((aligned(4096))) = {0x36};
// 定义二级页表
static uint32_t page_table[1024] __attribute__((aligned(4096)))= {PDE_U}; 
//建立页表,数组中的每个元素都要4M 对其。
//只是初始化了第0个表项
uint32_t pg_dir[1024] __attribute__((aligned(4096))) = {
    [0] = (0) |PDE_P| PDE_W |PDE_U |PDE_PS,
};
//定义二级页表的函数
void os_init(void){
    // PDE_PS 默认是0
    pg_dir[MAP_ADDR >> 22] = (uint32_t)page_table | PDE_P|PDE_W|PDE_U;    // 将二级页表的地址副给一级页表
    page_table[(MAP_ADDR >> 12) & 0x3ff] = (uint32_t)map_phy_buffer | PDE_P|PDE_W|PDE_U; // 设置二级页表到物理地址
};
// 1 直接定义的结构体数组,而不是先定义数组,在定义数组
// 2 __attribute__(aligned()) 是指的对于数组中的每一项的字节对其方式
// 3   base[31:24]:31:24       G:23      D/B:22      L:21    AVL:20-19   segment_limit:19-16   P:16-15   DPL :14-13     S:12   TYPE:11-8  base[23:16]:7-0
//       0000 0000             1           1           0       0               1111             1              00          1        1010   0000 0000                                       
//     base_addr[15:00]:31-16      segment_limit[15:00]:15-00
//     0000 0000                       1111 1111
//这个表的内容应该在 栈段
struct {uint16_t limit_l,base_l,basehl_attr,base_limit;}gdt_table[256] __attribute__((aligned(8))) = {
    [KERNEL_CODE_SEG/8] = {0xffff,0x0000,0x9a00,0x00cf},
    [KERNEL_DATA_SEG/8] = {0xffff,0x0000,0x9200,0x00cf}, 
};

start.s

	#include "os.h"
	// 声明本地以下符号是全局的,在其它源文件中可以访问
	.global _start
	//引用 C 函数+数组
	.extern os_init,gdt_table,,pg_dir
	// 指定以下的代码生成16位的机器指令,这样才能在启动时的实模式下运行
  	.code16
	// 以下是代码区
 	.text
_start: 
	jmp $0, $offset   // 用于设置cs寄存器, cs:ip  不支持这种语法
offset:
	mov $0, %ax
	mov %ax, %ds
	mov %ax, %ss
	mov %ax, %es
	mov %ax, %gs
	mov %ax, %fs		
	mov $offset, %esp    // 设置了栈顶指针
read_self_all:
	mov $0x7e00, %bx   // 读取到内存的位置
	mov $0x2, %cx      // 读取的扇区数量
	mov $0x240, %ax    // AH:读磁盘, AL:读取的数量
	mov $0x80, %dx     // 读取第一块磁盘
	int $0x13          // 13号中断
	jc read_self_all   // 读取出错就反复读取
	cli   				//关中断
	lgdt [gdt_desc]		//设置GDT 表的起始位置
	mov $1, %eax			//设置cr0寄存器
	lmsw %ax
	jmp $KERNEL_CODE_SEG, $_start_32	//jump:cs:ip , 设置选择子
	.org 0x1fe   //从510的地方开始
	.byte 0x55, 0xaa
// 这是第二个扇区的内容
	.code32
	.text
_start_32:
	//设置32位下的数据段, 栈段
	mov $KERNEL_DATA_SEG, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %ss
	mov $_start, %esp
	//调用C 函数,初始化,二级页表
	call os_init
	//初始化cr3寄存器,设置页表地址
	//pg_dir 在c语言中
	mov $pg_dir, %eax
	mov %eax, %cr3
	//设置cr4 寄存器
	// 是能 4M 对 4M 
	mov %cr4, %eax
	orl $(1<<4), %eax
	mov %eax, %cr4
	//设置cr0 寄存器
	mov %cr0, %eax
	orl $(1<<31) , %eax     // 设置的最高位
	mov %eax, %cr0
	jmp .						
gdt_desc:
	.word ((256*8)-1)		//gdt 界限
	.long gdt_table			//gdt 地址
	


测试:

本文标签: 寄存器编程配置