1. 重新规划内存
如下图所示:
注:上图蓝色的还没有实现
2.
a. mbr.S:
a. 从0x7c00启动,打印mbr三个字符
b. 从硬盘LBA_2读1个sector的loader.bin到0x40000
c. 从硬盘LBA_9读0x80+0x48=200个sector的kernel.elf到0x70000
d. 跳到jmp 0x4000:0, loader.bin
b. loader.S:
a. 从0x40000运行,打开A20,加载GDT,cr0第0位置1
b. 跳到保护模式,并打印P
c. 在0x0处设置setup_page_table
d. 新gdt_ptr的base设为0xC0009000,将gdt_base复制到0x9000,现在固定复制64字节
e. 打开分页机制,重新加载gdt
f. 将0x70000处的kernel.elf,复制到0x50000
g. 跳到0x50000处运行
c. 关于setup_page_table
a. 将PDE清0,即将0x0000-0x1000大小为4K的地址清0
b. PDE0(0x0) PDE768(768*4=0xc00) 指向PTE(0x1000)
c. 设置0x1000处的PTE, [0-1M]=1*1024*1024/4096=256项=0x100PAGE
pg0[0x1000,0x1400]-->物理地址[0x0--1M] ;只映射1M空间
3. 因为PDE是从0开始的,其转化过程比较简洁
-
cong@msi:/work/os/code/8memory$ cat mm/memory.c
-
#include <memory.h>
-
#include <printk.h>
-
#include <string.h>
-
#define MM_K_VADDR_START 0xC0100000 //1M
-
#define MM_SIZE (32*1024*1024) //32M
-
#define MM_START (1*1024*1024) //1M
-
#define MM_FOR_K (15*1024*1024) //15M for kernel
-
#define MM_FOR_U (16*1024*1024) //16M for user
-
-
#define PDE_IDX(addr) ((addr & 0xffc00000) >> 22)
-
#define PTE_IDX(addr) ((addr & 0x003ff000) >> 12)
-
-
MM_POOL k_pool, u_pool, v_pool; //kernel, user, vaddr
-
-
uint8_t mm_map_k[MM_FOR_K>>12] = {0,}; //15*1024*1024/4096
-
uint8_t mm_map_k_vaddr[MM_FOR_K>>12] = {0,}; //15*1024*1024/4096
-
uint8_t mm_map_u[MM_FOR_U>>12] = {0,}; //16*1024*1024/4096
-
static void* addr_get(MM_POOL* pool, uint32_t cnt);
-
static void map_one_page(uint32_t paddr, uint32_t vaddr);
-
static void* addr_get(MM_POOL* pool, uint32_t cnt)
-
{
-
uint32_t addr;
-
uint32_t i;
-
int32_t bit_idx = -1;
-
bit_idx = bitmap_scan(&pool->bm, cnt);
-
if(-1 == bit_idx)
-
return NULL;
-
for(i=0; i<cnt; i++)
-
{
-
bitmap_set(&pool->bm, bit_idx+i, 1);
-
}
-
addr = (bit_idx<<12) + pool->start;
-
return ((void*)addr);
-
}
-
-
/* 得到虚拟地址vaddr对应的pde的指针 */
-
uint32_t* pde_ptr(uint32_t vaddr) {
-
/* 0xfffff是用来访问到页表本身所在的地址 */
-
uint32_t* pde = (uint32_t*)(PDE_IDX(vaddr) * 4);
-
return pde;
-
}
-
-
/* 得到虚拟地址vaddr对应的pte指针*/
-
uint32_t* pte_ptr(uint32_t vaddr) {
-
/* 先访问到页表自己 +
-
* 再用页目录项pde(页目录内页表的索引)做为pte的索引访问到页表 +
-
* 再用pte的索引做为页内偏移*/
-
//uint32_t* pte = (uint32_t*)(((vaddr&0xffc00000)>>10) + PTE_IDX(vaddr)*4);
-
uint32_t* pte = (uint32_t*)(PAGE + PTE_IDX(vaddr)*4);
-
return pte;
-
}
-
-
/* 页表中添加虚拟地址_vaddr与物理地址_page_phyaddr的映射 */
-
static void map_one_page(uint32_t paddr, uint32_t vaddr)
-
{
-
uint32_t* pde = pde_ptr(vaddr);
-
uint32_t* pte = pte_ptr(vaddr);
-
/************************ 注意 *************************
-
* 执行*pte,会访问到空的pde。所以确保pde创建完成后才能执行*pte,
-
* 否则会引发page_fault。因此在*pde为0时,*pte只能出现在下面else语句块中的*pde后面。
-
* *********************************************************/
-
/* 先在页目录内判断目录项的P位,若为1,则表示该表已存在 */
-
if (*pde & 0x00000001) { // 页目录项和页表项的第0位为P,此处判断目录项是否存在
-
if (!(*pte & 0x00000001)) { // 只要是创建页表,pte就应该不存在,多判断一下放心
-
*pte = (paddr | PG_US_U | PG_RW_W | PG_P_1); // US=1,RW=1,P=1
-
} else { //应该不会执行到这,因为上面的ASSERT会先执行。
-
printk("pte repeat");
-
*pte = (paddr | PG_US_U | PG_RW_W | PG_P_1); // US=1,RW=1,P=1
-
}
-
} else {
-
printk("not exist\n");
-
// 页目录项不存在,所以要先创建页目录再创建页表项.
-
/* 页表中用到的页框一律从内核空间分配 */
-
uint32_t pde_phyaddr = (uint32_t)addr_get(&k_pool, 1);
-
-
*pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
-
-
/* 分配到的物理页地址pde_phyaddr对应的物理内存清0,
-
* 避免里面的陈旧数据变成了页表项,从而让页表混乱.
-
* 访问到pde对应的物理地址,用pte取高20位便可.
-
* 因为pte是基于该pde对应的物理地址内再寻址,
-
* 把低12位置0便是该pde对应的物理页的起始*/
-
memset((void*)((int)pte & 0xfffff000), 0, PAGE);
-
-
*pte = (paddr | PG_US_U | PG_RW_W | PG_P_1); // US=1,RW=1,P=1
-
}
-
}
-
-
void* malloc_pages(uint32_t flag, uint32_t cnt)
-
{
-
uint32_t i;
-
void* vaddr, *paddr;
-
MM_POOL* mm_pool;
-
if(cnt > (uint32_t)bitmap_get_left_bits(&v_pool.bm))
-
{
-
printk("no enough vaddr mem left\n");
-
return NULL;
-
}
-
if(cnt > (uint32_t)bitmap_get_left_bits(&k_pool.bm))
-
{
-
printk("no enough phy mem left\n");
-
return NULL;
-
}
-
//a. get vaddr
-
vaddr = addr_get(&v_pool, cnt);
-
if(NULL == vaddr)
-
{
-
printk("vaddr get error\n");
-
return NULL;
-
}
-
mm_pool = (flag&PF_KERNEL)?(&k_pool):(&u_pool);
-
for(i=0; i<cnt; i++)
-
{
-
//b. get phy addr
-
paddr = addr_get(mm_pool, 1);
-
//c. map vaddr to phy addr
-
map_one_page((uint32_t)paddr, (uint32_t)vaddr);
-
vaddr += PAGE;
-
}
-
return vaddr;
-
}
-
-
//从内核物理内存池中申请pg_cnt页内存,成功则返回其虚拟地址,失败则返回NULL
-
void* get_kernel_pages(uint32_t cnt)
-
{
-
void* vaddr = malloc_pages(PF_KERNEL, cnt);
-
if(NULL == vaddr)
-
{
-
printk("malloc page eror\n");
-
return NULL;
-
}
-
return vaddr;
-
}
-
-
//内存管理部分初始化入口
-
int mem_init(void)
-
{
-
k_pool.bm.len = (MM_FOR_K>>12)>>3; //k_pages in byte
-
k_pool.bm.pbit = mm_map_k;
-
k_pool.start = MM_START;
-
-
u_pool.bm.len = (MM_FOR_U>>12)>>3; //user_pages in bits
-
u_pool.bm.pbit = mm_map_u;
-
u_pool.start = MM_START + MM_FOR_K;
-
-
v_pool.bm.len = (MM_FOR_K>>12)>>3; //k_pages in bits
-
v_pool.bm.pbit = mm_map_k_vaddr;
-
v_pool.start = MM_K_VADDR_START;
-
-
bitmap_init(&k_pool.bm);
-
bitmap_init(&u_pool.bm);
-
bitmap_init(&v_pool.bm);
-
-
printk("k_pages in bytes=%d\n", k_pool.bm.len);
-
printk("u_pages in bytes=%d\n", u_pool.bm.len);
-
return 0;
-
}
说明: 《操作系统真相还原》中页目录表是放在0x100000(1M)处,还要存放256个页表
现在是把页目录表放在0x0处后面跟8个页表这样32M的内存都被映射了,
页表的分配是只分配1M地址之后的空间,这样就可以直接从1M-32M去分配内存了。
不需要去整那么多边界,这样代码也会简洁很多。
4. 运行
-
<bochs:2> info tab
-
cr3: 0x000000000000
-
0x00000000-0x00102fff -> 0x000000000000-0x000000102fff
-
0xc0000000-0xc0102fff -> 0x000000000000-0x000000102fff
-
0xffc00000-0xffc00fff -> 0x000000001000-0x000000001fff
-
0xfff00000-0xfff00fff -> 0x000000001000-0x000000001fff
-
0xfffff000-0xffffffff -> 0x000000000000-0x000000000fff
-
<bochs:3> page 0xc0103000
-
PDE: 0x0000000000001027 ps A pcd pwt U W P
-
PTE: 0x0000000000000000 g pat d a pcd pwt S R p
-
physical address not available for linear 0xc0103000
-
<bochs:4> page 0xc0102000
-
PDE: 0x0000000000001027 ps A pcd pwt U W P
-
PTE: 0x0000000000102007 g pat d a pcd pwt U W P
-
linear page 0xc0102000 maps to physical page 0x000000102000
-
<bochs:5> page 0xc0101000
-
PDE: 0x0000000000001027 ps A pcd pwt U W P
-
PTE: 0x0000000000101007 g pat d a pcd pwt U W P
-
linear page 0xc0101000 maps to physical page 0x000000101000
-
<bochs:6> page 0xc0100000
-
PDE: 0x0000000000001027 ps A pcd pwt U W P
-
PTE: 0x0000000000100007 g pat d a pcd pwt U W P
-
linear page 0xc0100000 maps to physical page 0x000000100000
5. 代码打包
8memory.rar(下载后改名为8memory.tar.gz)
以前的printk打印老有问题重新整了一个
阅读(796) | 评论(0) | 转发(0) |