Hi:
I developed an os, in a user process, I had loaded cr3 register in corresponding kernel thread, here is the code:
Code: Select all
/* 激活页表 */
void page_dir_activate(struct task_struct* p_thread) {
/********************************************************************
* 执行此函数时,当前任务可能是线程
* 之所以对线程也要重新安装页表,原因是上一次被调度的可能是进程,
* 否则不恢复页表的话,线程就会使用进程的页表了。
* *****************************************************/
/* 若为内核线程,需要重新填充页表为 0x100000 */
uint32_t pagedir_phy_addr = 0x100000; // 默认为内核的页目录物理地址,也就是内核线程所用的页目录表
if(p_thread->pgdir != NULL) { // 用户态进程有自己的页目录表
pagedir_phy_addr = addr_v2p((uint32_t)p_thread->pgdir);
}
/* 更新页目录寄存器 cr3, 使新页表生效 */
asm volatile ("movl %0, %%cr3" : : "r" (pagedir_phy_addr) : "memory");
}
in the user process,when use malloc or free,I used 'page_table_pte_remove' and 'page_table_add' function to flush TLB, here is the code
Code: Select all
/* 去掉页表中虚拟地址vaddr的映射,只去掉vaddr对应的pte */
static void page_table_pte_remove(uint32_t vaddr) {
uint32_t* pte = pte_ptr(vaddr);
*pte &= ~PG_P_1; // 将页表项pte的P位置0
asm volatile ("
%0"::"m" (vaddr):"memory"); //更新tlb
}
Code: Select all
/* 页表中添加虚拟地址_vaddr与物理地址_page_phyaddr的映射 */
void page_table_add(void* _vaddr, void* _page_phyaddr) {
uint32_t vaddr = (uint32_t)_vaddr, page_phyaddr = (uint32_t)_page_phyaddr;
uint32_t* pde = pde_ptr(vaddr);
uint32_t* pte = pte_ptr(vaddr);
#ifdef KERNEL_MEMORY_DEBUG
//printk("_vaddr-::0x%x-::0x%x-::0x%x:",_vaddr,pde,pte);
put_str("_vaddr-::");
put_int(_vaddr);
put_str("-::");
put_int(pde);
put_str("-::");
put_int(*pte);
put_str("-::");
put_str("\n");
#endif
/************************ 注意 *************************
* 执行*pte,会访问到pde。所以确保pde创建完成后才能执行*pte,
* 否则会引发page_fault。因此在pde未创建时,
* *pte只能出现在下面最外层else语句块中的*pde后面。
* *********************************************************/
/* 先在页目录内判断目录项的P位,若为1,则表示该表已存在 */
if (*pde & 0x00000001) {
ASSERT(!(*pte & 0x00000001));
if (!(*pte & 0x00000001)) { // 只要是创建页表,pte就应该不存在,多判断一下放心
*pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); // US=1,RW=1,P=1
} else { // 调试模式下不会执行到此,上面的ASSERT会先执行.关闭调试时下面的PANIC会起作用
PANIC("pte repeat");
}
} else { // 页目录项不存在,所以要先创建页目录项再创建页表项.
/* 页表中用到的页框一律从内核空间分配 */
uint32_t pde_phyaddr = (uint32_t)palloc(&kernel_pool);
*pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
/******************* 必须将页表所在的页清0 *********************
* 必须把分配到的物理页地址pde_phyaddr对应的物理内存清0,
* 避免里面的陈旧数据变成了页表中的页表项,从而让页表混乱.
* pte的高20位会映射到pde所指向的页表的物理起始地址.*/
memset((void*)((int)pte & 0xfffff000), 0, PG_SIZE);
/************************************************************/
ASSERT(!(*pte & 0x00000001));
*pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); // US=1,RW=1,P=1
}
}
in the bochs, when used 'info tab' command, it's shown:
Code: Select all
<bochs:25> info tab
cr3: 0x00000025a000
0x0000000008048000-0x0000000008049fff -> 0x000010105000-0x000010106fff
0x000000000804a000-0x000000000804afff -> 0x000010108000-0x000010108fff
0x000000000804b000-0x000000000804bfff -> 0x00001010a000-0x00001010afff
0x00000000080a0000-0x00000000080a0fff -> 0x000010109000-0x000010109fff
0x00000000bffff000-0x00000000bfffffff -> 0x000010107000-0x000010107fff
0x00000000c0000000-0x00000000c00fffff -> 0x000000000000-0x0000000fffff
0x00000000c0100000-0x00000000c011dfff -> 0x000000200000-0x00000021dfff
0x00000000c011e000-0x00000000c0138fff -> 0x000000220000-0x00000023afff
0x00000000c0139000-0x00000000c013dfff -> 0x00000023d000-0x000000241fff
0x00000000c013f000-0x00000000c0156fff -> 0x000000243000-0x00000025afff
0x00000000ffc20000-0x00000000ffc20fff -> 0x00000025b000-0x00000025bfff
0x00000000ffeff000-0x00000000ffefffff -> 0x00000025c000-0x00000025cfff
0x00000000fff00000-0x00000000ffffefff -> 0x000000101000-0x0000001fffff
0x00000000fffff000-0x00000000ffffffff -> 0x00000025a000-0x00000025afff
<bochs:26>
everything is as I expected, but when the os exec code writes data to the '0x804b000' virtual address, it wrote to the '0x000010109000' physical address, not the '0x00001010a000' physical address.
I don't know why could anyone help me? thanks a lot!