lab7-fix2023/kernel/pagetbl.c

379 lines
15 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*************************************************************
*页式管理相关代码 add by visual 2016.4.19
**************************************************************/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "global.h"
#include "proto.h"
#include "memman.h"
#include "stdio.h"
// to determine if a page fault is reparable. added by xw, 18/6/11
u32 cr2_save;
u32 cr2_count = 0;
/*======================================================================*
switch_pde added by xw, 17/12/11
*switch the page directory table after schedule() is called
*======================================================================*/
void switch_pde()
{
cr3_ready = p_proc_current->task.cr3;
}
/*======================================================================*
init_page_pte add by visual 2016.4.19
*该函数只初始化了进程的高端(内核端)地址页表
*======================================================================*/
u32 init_page_pte(u32 pid)
{ //页表初始化函数
u32 AddrLin, pde_addr_phy_temp, err_temp;
pde_addr_phy_temp = do_kmalloc_4k(); //为页目录申请一页
memset((void *)K_PHY2LIN(pde_addr_phy_temp), 0, num_4K); // add by visual 2016.5.26
if (pde_addr_phy_temp < 0 || (pde_addr_phy_temp & 0x3FF) != 0) // add by visual 2016.5.9
{
vga_write_str_color("init_page_pte Error:pde_addr_phy_temp", 0x74);
return -1;
}
proc_table[pid].task.cr3 = pde_addr_phy_temp; //初始化了进程表中cr3寄存器变量属性位暂时不管
/*********************页表初始化部分*********************************/
u32 phy_addr = 0;
for (AddrLin = KernelLinBase, phy_addr = 0; AddrLin < KernelLinBase + KernelSize; AddrLin += num_4K, phy_addr += num_4K)
{ //只初始化内核部分3G后的线性地址映射到物理地址开始处
err_temp = lin_mapping_phy(AddrLin, //线性地址 //add by visual 2016.5.9
phy_addr, //物理地址
pid, //进程pid //edit by visual 2016.5.19
PG_P | PG_USU | PG_RWW, //页目录的属性位(用户权限) //edit by visual 2016.5.26
PG_P | PG_USS | PG_RWW); //页表的属性位(系统权限) //edit by visual 2016.5.17
if (err_temp != 0)
{
vga_write_str_color("init_page_pte Error:lin_mapping_phy", 0x74);
return -1;
}
}
return 0;
}
/*======================================================================*
page_fault_handle edit by visual 2016.5.9
*======================================================================*/
void page_fault_handler(u32 vec_no, //异常编号此时应该是14代表缺页异常
u32 err_code, //错误码
u32 eip, //导致缺页的指令的线性地址
u32 cs, //发生错误时的代码段寄存器内容
u32 eflags) //时发生错误的标志寄存器内容
{ //缺页中断处理函数
u32 pde_addr_phy_temp;
u32 pte_addr_phy_temp;
u32 cr2;
cr2 = read_cr2();
// if page fault happens in kernel, it's an error.
if (kernel_initial == 1)
{
kprintf("\n");
vga_write_str_color("Page Fault\n", 0x74);
vga_write_str_color("eip=", 0x74); //灰底红字
kprintf("%d", eip);
vga_write_str_color("eflags=", 0x74);
kprintf("%d", eflags);
vga_write_str_color("cs=", 0x74);
kprintf("%d", cs);
vga_write_str_color("err_code=", 0x74);
kprintf("%d", err_code);
vga_write_str_color("Cr2=", 0x74); //灰底红字
kprintf("%d", cr2);
halt();
}
//获取该进程页目录物理地址
pde_addr_phy_temp = get_pde_phy_addr(p_proc_current->task.pid);
//获取该线性地址对应的页表的物理地址
pte_addr_phy_temp = get_pte_phy_addr(p_proc_current->task.pid, cr2);
if (cr2 == cr2_save)
{
cr2_count++;
if (cr2_count == 5)
{
vga_set_disppos(0);
kprintf("\n");
vga_write_str_color("Page Fault\n", 0x74);
vga_write_str_color("eip=", 0x74); //灰底红字
kprintf("%x", eip);
vga_write_str_color("eflags=", 0x74);
kprintf("%x", eflags);
vga_write_str_color("cs=", 0x74);
kprintf("%x", cs);
vga_write_str_color("err_code=", 0x74);
kprintf("%x", err_code);
vga_write_str_color("Cr2=", 0x74); //灰底红字
kprintf("%x", cr2);
vga_write_str_color("Cr3=", 0x74);
kprintf("%x", p_proc_current->task.cr3);
//获取页目录中填写的内容
vga_write_str_color("Pde=", 0x74);
kprintf("%x", *((u32 *)K_PHY2LIN(pde_addr_phy_temp) + get_pde_index(cr2)));
//获取页表中填写的内容
vga_write_str_color("Pte=", 0x74);
kprintf("%x", *((u32 *)K_PHY2LIN(pte_addr_phy_temp) + get_pte_index(cr2)));
asm volatile("cli");
halt();
}
}
else
{
cr2_save = cr2;
cr2_count = 0;
}
if (0 == pte_exist(pde_addr_phy_temp, cr2))
{ //页表不存在
// vga_write_str_color("[Pde Fault!]",0x74); //灰底红字
(*((u32 *)K_PHY2LIN(pde_addr_phy_temp) + get_pde_index(cr2))) |= PG_P;
// vga_write_str_color("[Solved]",0x74);
}
else
{ //只是缺少物理页
// vga_write_str_color("[Pte Fault!]",0x74); //灰底红字
(*((u32 *)K_PHY2LIN(pte_addr_phy_temp) + get_pte_index(cr2))) |= PG_P;
// vga_write_str_color("[Solved]",0x74);
}
refresh_page_cache();
}
/***************************地址转换过程***************************
*
*第一步CR3包含着页目录的起始地址用32位线性地址的最高10位A31~A22作为页目录的页目录项的索引
*将它乘以4与CR3中的页目录的起始地址相加形成相应页表的地址。
*
*第二步从指定的地址中取出32位页目录项它的低12位为0这32位是页表的起始地址。
*用32位线性地址中的A21~A12位作为页表中的页面的索引将它乘以4与页表的起始地址相加形成32位页面地址。
*
*第三步将A11~A0作为相对于页面地址的偏移量与32位页面地址相加形成32位物理地址。
*************************************************************************/
/*======================================================================*
get_pde_index add by visual 2016.4.28
*======================================================================*/
inline u32 get_pde_index(u32 AddrLin)
{ //由 线性地址 得到 页目录项编号
return (AddrLin >> 22); //高10位A31~A22
}
/*======================================================================*
get_pte_index add by visual 2016.4.28
*======================================================================*/
inline u32 get_pte_index(u32 AddrLin)
{ //由 线性地址 得到 页表项编号
return (((AddrLin)&0x003FFFFF) >> 12); //中间10位A21~A12,0x3FFFFF = 0000 0000 0011 1111 1111 1111 1111 1111
}
/*======================================================================*
get_pde_phy_addr add by visual 2016.4.28
*======================================================================*/
inline u32 get_pde_phy_addr(u32 pid)
{ //获取页目录物理地址
if (proc_table[pid].task.cr3 == 0)
{ //还没有初始化页目录
return -1;
}
else
{
return ((proc_table[pid].task.cr3) & 0xFFFFF000);
}
}
/*======================================================================*
get_pte_phy_addr add by visual 2016.4.28
*======================================================================*/
inline u32 get_pte_phy_addr(u32 pid, //页目录物理地址 //edit by visual 2016.5.19
u32 AddrLin) //线性地址
{ //获取该线性地址所属页表的物理地址
u32 PageDirPhyAddr = get_pde_phy_addr(pid); // add by visual 2016.5.19
return (*((u32 *)K_PHY2LIN(PageDirPhyAddr) + get_pde_index(AddrLin))) & 0xFFFFF000; //先找到该进程页目录首地址,然后计算出该线性地址对应的页目录项,再访问,最后注意4k对齐
}
/*======================================================================*
get_page_phy_addr add by visual 2016.5.9
*======================================================================*/
inline u32 get_page_phy_addr(u32 pid, //页表物理地址 //edit by visual 2016.5.19
u32 AddrLin) //线性地址
{ //获取该线性地址对应的物理页物理地址
u32 PageTblPhyAddr = get_pte_phy_addr(pid, AddrLin); // add by visual 2016.5.19
return (*((u32 *)K_PHY2LIN(PageTblPhyAddr) + get_pte_index(AddrLin))) & 0xFFFFF000;
}
/*======================================================================*
pte_exist add by visual 2016.4.28
*======================================================================*/
u32 pte_exist(u32 PageDirPhyAddr, //页目录物理地址
u32 AddrLin) //线性地址
{ //判断 有没有 页表
if ((0x00000001 & (*((u32 *)K_PHY2LIN(PageDirPhyAddr) + get_pde_index(AddrLin)))) == 0) //先找到该进程页目录,然后计算出该线性地址对应的页目录项,访问并判断其是否存在
{ //标志位为0不存在
return 0;
}
else
{
return 1;
}
}
/*======================================================================*
phy_exist add by visual 2016.4.28
*======================================================================*/
u32 phy_exist(u32 PageTblPhyAddr, //页表物理地址
u32 AddrLin) //线性地址
{ //判断 该线性地址 有没有 对应的 物理页
if ((0x00000001 & (*((u32 *)K_PHY2LIN(PageTblPhyAddr) + get_pte_index(AddrLin)))) == 0)
{ //标志位为0不存在
return 0;
}
else
{
return 1;
}
}
/*======================================================================*
write_page_pde add by visual 2016.4.28
*======================================================================*/
void write_page_pde(u32 PageDirPhyAddr, //页目录物理地址
u32 AddrLin, //线性地址
u32 TblPhyAddr, //要填写的页表的物理地址函数会进行4k对齐
u32 Attribute) //属性
{ //填写页目录
(*((u32 *)K_PHY2LIN(PageDirPhyAddr) + get_pde_index(AddrLin))) = (TblPhyAddr & 0xFFFFF000) | Attribute;
//进程页目录起始地址+每一项的大小*所属的项
}
/*======================================================================*
write_page_pte add by visual 2016.4.28
*======================================================================*/
void write_page_pte(u32 TblPhyAddr, //页表物理地址
u32 AddrLin, //线性地址
u32 PhyAddr, //(4k对齐)
u32 Attribute) //属性
{ //填写页目录,会添加属性
(*((u32 *)K_PHY2LIN(TblPhyAddr) + get_pte_index(AddrLin))) = (PhyAddr & 0xFFFFF000) | Attribute;
//页表起始地址+一项的大小*所属的项
}
/*======================================================================*
* vmalloc add by visual 2016.5.4
*从堆中分配size大小的内存返回线性地址
*======================================================================*/
u32 vmalloc(u32 size)
{
u32 temp;
if (p_proc_current->task.info.type == TYPE_PROCESS)
{ //进程直接就是标识
temp = p_proc_current->task.memmap.heap_lin_limit;
p_proc_current->task.memmap.heap_lin_limit += size;
}
else
{ //线程需要取父进程的标识
temp = *((u32 *)p_proc_current->task.memmap.heap_lin_limit);
(*((u32 *)p_proc_current->task.memmap.heap_lin_limit)) += size;
}
return temp;
}
/*======================================================================*
* lin_mapping_phy add by visual 2016.5.9
*将线性地址映射到物理地址上去,函数内部会分配物理地址
*======================================================================*/
int lin_mapping_phy(u32 AddrLin, //线性地址
u32 phy_addr, //,MAX_UNSIGNED_INT(0xFFFFFFFF)phy_addr直接和AddrLin建立映射
u32 pid, //进程pid //edit by visual 2016.5.19
u32 pde_Attribute, //页目录中的属性位
u32 pte_Attribute) //页表中的属性位
{
u32 pte_addr_phy;
u32 pde_addr_phy = get_pde_phy_addr(pid); // add by visual 2016.5.19
if (0 == pte_exist(pde_addr_phy, AddrLin))
{ //页表不存在,创建一个,并填进页目录中
pte_addr_phy = (u32)do_kmalloc_4k(); //为页表申请一页
memset((void *)K_PHY2LIN(pte_addr_phy), 0, num_4K); // add by visual 2016.5.26
if (pte_addr_phy < 0 || (pte_addr_phy & 0x3FF) != 0) // add by visual 2016.5.9
{
vga_write_str_color("lin_mapping_phy Error:pte_addr_phy", 0x74);
return -1;
}
write_page_pde(pde_addr_phy, //页目录物理地址
AddrLin, //线性地址
pte_addr_phy, //页表物理地址
pde_Attribute); //属性
}
else
{ //页表存在,获取该页表物理地址
pte_addr_phy = get_pte_phy_addr(pid, //进程pid //edit by visual 2016.5.19
AddrLin); //线性地址
}
if (MAX_UNSIGNED_INT == phy_addr) // add by visual 2016.5.19
{ //由函数申请内存
if (0 == phy_exist(pte_addr_phy, AddrLin))
{ //无物理页申请物理页并修改phy_addr
if (AddrLin >= K_PHY2LIN(0))
phy_addr = do_kmalloc_4k(); //从内核物理地址申请一页
else
{
// kprintf("%");
phy_addr = do_malloc_4k(); //从用户物理地址空间申请一页
}
}
else
{
//有物理页,什么也不做,直接返回,必须返回
return 0;
}
}
else
{ //指定填写phy_addr
//不用修改phy_addr
}
if (phy_addr < 0 || (phy_addr & 0x3FF) != 0)
{
vga_write_str_color("lin_mapping_phy:phy_addr ERROR", 0x74);
return -1;
}
write_page_pte(pte_addr_phy, //页表物理地址
AddrLin, //线性地址
phy_addr, //物理页物理地址
pte_Attribute); //属性
refresh_page_cache();
return 0;
}
/*======================================================================*
* clear_kernel_pagepte_low add by visual 2016.5.12
*将内核低端页表清除
*======================================================================*/
void clear_kernel_pagepte_low()
{
u32 page_num = *(u32 *)PageTblNumAddr;
memset((void *)(K_PHY2LIN(KernelPageTblAddr)), 0, 4 * page_num); //从内核页目录中清除内核页目录项前8项
memset((void *)(K_PHY2LIN(KernelPageTblAddr + 0x1000)), 0, 4096 * page_num); //从内核页表中清除线性地址的低端映射关系
refresh_page_cache();
}