the virtual address points to the wrong physical address!

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Princekin
Posts: 15
Joined: Mon Jul 05, 2021 7:05 am

the virtual address points to the wrong physical address!

Post by Princekin »

Hi:
I developed a os, in one user process,I had loaded cr3 register in the corresponding kernel thread,the code is follow:

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");
}

here is the code which I use 'info tab' in the Bochs

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 code write data to the '804b000' virtual address,it always write into '0x000010109000' physical address, not the '0x00001010a000'.
I don't know why, could anyone help me, thanks a lot!
Princekin
Posts: 15
Joined: Mon Jul 05, 2021 7:05 am

the virtual address points to the wrong physical address!

Post by Princekin »

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!
Octocontrabass
Member
Member
Posts: 5567
Joined: Mon Mar 25, 2013 7:01 pm

Re: the virtual address points to the wrong physical address

Post by Octocontrabass »

Princekin wrote:

Code: Select all

	asm volatile ("
				  %0"::"m" (vaddr):"memory");    //更新tlb
Your code does not flush the TLB. Are you trying to use INVLPG?

If you want to use INVLPG, it should look like this:

Code: Select all

asm volatile ( "invlpg %0" :: "m"(*(uint8_t*)vaddr) : "memory" );
Post Reply