|
@@ -4,9 +4,12 @@
|
|
|
#include <common/kprint.h>
|
|
|
#include <driver/multiboot2/multiboot2.h>
|
|
|
#include <process/process.h>
|
|
|
+#include <common/compiler.h>
|
|
|
+#include <common/errno.h>
|
|
|
+#include <debug/traceback/traceback.h>
|
|
|
|
|
|
-ul Total_Memory = 0;
|
|
|
-ul total_2M_pages = 0;
|
|
|
+static ul Total_Memory = 0;
|
|
|
+static ul total_2M_pages = 0;
|
|
|
static ul root_page_table_phys_addr = 0; // 内核层根页表的物理地址
|
|
|
|
|
|
/**
|
|
@@ -48,6 +51,23 @@ static void mm_calculate_entry_num(uint64_t length, mm_pgt_entry_num_t *ent)
|
|
|
*/
|
|
|
uint64_t mm_get_PDE(ul proc_page_table_addr, bool is_phys, ul virt_addr, bool clear);
|
|
|
|
|
|
+/**
|
|
|
+ * @brief 检查页表是否存在不为0的页表项
|
|
|
+ *
|
|
|
+ * @param ptr 页表基指针
|
|
|
+ * @return int8_t 存在 -> 1
|
|
|
+ * 不存在 -> 0
|
|
|
+ */
|
|
|
+int8_t mm_check_page_table(uint64_t *ptr)
|
|
|
+{
|
|
|
+ for (int i = 0; i < 512; ++i, ++ptr)
|
|
|
+ {
|
|
|
+ if (*ptr != 0)
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
void mm_init()
|
|
|
{
|
|
|
kinfo("Initializing memory management unit...");
|
|
@@ -451,7 +471,7 @@ void free_pages(struct Page *page, int number)
|
|
|
|
|
|
/**
|
|
|
* @brief 重新初始化页表的函数
|
|
|
- * 将0~4GB的物理页映射到线性地址空间
|
|
|
+ * 将所有物理页映射到线性地址空间
|
|
|
*/
|
|
|
void page_table_init()
|
|
|
{
|
|
@@ -470,10 +490,7 @@ void page_table_init()
|
|
|
|
|
|
for (int j = 0; j < z->count_pages; ++j)
|
|
|
{
|
|
|
- // if (p->addr_phys)
|
|
|
- // kdebug("(ul)phys_2_virt(p->addr_phys)=%#018lx",(ul)phys_2_virt(p->addr_phys));
|
|
|
- // mm_map_phys_addr((ul)phys_2_virt(p->addr_phys), p->addr_phys, PAGE_2M_SIZE, PAGE_KERNEL_PAGE);
|
|
|
- mm_map_proc_page_table((uint64_t)get_CR3(), true, (ul)phys_2_virt(p->addr_phys), p->addr_phys, PAGE_2M_SIZE, PAGE_KERNEL_PAGE, false, true);
|
|
|
+ mm_map_proc_page_table((uint64_t)get_CR3(), true, (ul)phys_2_virt(p->addr_phys), p->addr_phys, PAGE_2M_SIZE, PAGE_KERNEL_PAGE, false, true, false);
|
|
|
|
|
|
++p;
|
|
|
++js;
|
|
@@ -491,18 +508,20 @@ void page_table_init()
|
|
|
* @param virt_addr_start 要映射到的虚拟地址的起始位置
|
|
|
* @param phys_addr_start 物理地址的起始位置
|
|
|
* @param length 要映射的区域的长度(字节)
|
|
|
+ * @param flags 标志位
|
|
|
+ * @param use4k 是否使用4k页
|
|
|
*/
|
|
|
-void mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flags)
|
|
|
+int mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool use4k)
|
|
|
{
|
|
|
uint64_t global_CR3 = (uint64_t)get_CR3();
|
|
|
|
|
|
- mm_map_proc_page_table(global_CR3, true, virt_addr_start, phys_addr_start, length, flags, false, true);
|
|
|
+ return mm_map_proc_page_table(global_CR3, true, virt_addr_start, phys_addr_start, length, flags, false, true, use4k);
|
|
|
}
|
|
|
|
|
|
-void mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul flags)
|
|
|
+int mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul flags)
|
|
|
{
|
|
|
uint64_t global_CR3 = (uint64_t)get_CR3();
|
|
|
- mm_map_proc_page_table(global_CR3, true, virt_addr_start, phys_addr_start, length, flags, true, true);
|
|
|
+ return mm_map_proc_page_table(global_CR3, true, virt_addr_start, phys_addr_start, length, flags, true, true, false);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -515,8 +534,9 @@ void mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul
|
|
|
* @param length 要映射的区域的长度(字节)
|
|
|
* @param user 用户态是否可访问
|
|
|
* @param flush 是否刷新tlb
|
|
|
+ * @param use4k 是否使用4k页
|
|
|
*/
|
|
|
-void mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user, bool flush)
|
|
|
+int mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user, bool flush, bool use4k)
|
|
|
{
|
|
|
|
|
|
// 计算线性地址对应的pml4页表项的地址
|
|
@@ -579,22 +599,68 @@ void mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_
|
|
|
--pgt_num.num_PDE;
|
|
|
// 计算当前2M物理页对应的pdt的页表项的物理地址
|
|
|
ul *pde_ptr = pd_ptr + pde_id;
|
|
|
- if (*pde_ptr != 0 && user)
|
|
|
+
|
|
|
+ // ====== 使用4k页 =======
|
|
|
+ if (unlikely(use4k))
|
|
|
+ {
|
|
|
+ // kdebug("use 4k");
|
|
|
+ if (*pde_ptr == 0)
|
|
|
+ {
|
|
|
+ // 创建四级页表
|
|
|
+ // kdebug("create PT");
|
|
|
+ uint64_t *vaddr = kmalloc(PAGE_4K_SIZE, 0);
|
|
|
+ memset(vaddr, 0, PAGE_4K_SIZE);
|
|
|
+ set_pdt(pde_ptr, mk_pdt(virt_2_phys(vaddr), (user ? PAGE_USER_PDE : PAGE_KERNEL_PDE)));
|
|
|
+ }
|
|
|
+ else if (unlikely(*pde_ptr & (1 << 7)))
|
|
|
+ {
|
|
|
+ // 当前页表项已经被映射了2MB物理页
|
|
|
+ goto failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint64_t pte_id = (((virt_addr_start + length_mapped) >> PAGE_4K_SHIFT) & 0x1ff);
|
|
|
+ uint64_t *pt_ptr = (uint64_t *)phys_2_virt(*pde_ptr & (~0x1fffUL));
|
|
|
+
|
|
|
+ // 循环填写4级页表,初始化4K页
|
|
|
+ for (; pgt_num.num_PTE > 0 && pte_id < 512; ++pte_id)
|
|
|
+ {
|
|
|
+ --pgt_num.num_PTE;
|
|
|
+ uint64_t *pte_ptr = pt_ptr + pte_id;
|
|
|
+
|
|
|
+ if (unlikely(*pte_ptr != 0))
|
|
|
+ {
|
|
|
+ kwarn("pte already exists.");
|
|
|
+ length_mapped += PAGE_4K_SIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ set_pt(pte_ptr, mk_pt((ul)phys_addr_start + length_mapped, flags | (user ? PAGE_USER_4K_PAGE : PAGE_KERNEL_4K_PAGE)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // ======= 使用2M页 ========
|
|
|
+ else
|
|
|
{
|
|
|
- // kwarn("page already mapped!");
|
|
|
- // 如果是用户态可访问的页,则释放当前新获取的物理页
|
|
|
- free_pages(Phy_to_2M_Page((ul)phys_addr_start + length_mapped), 1);
|
|
|
+ if (unlikely(*pde_ptr != 0 && user))
|
|
|
+ {
|
|
|
+ kwarn("page already mapped!");
|
|
|
+ // 如果是用户态可访问的页,则释放当前新获取的物理页
|
|
|
+ if (likely(((ul)phys_addr_start + length_mapped) < total_2M_pages)) // 校验是否为内存中的物理页
|
|
|
+ free_pages(Phy_to_2M_Page((ul)phys_addr_start + length_mapped), 1);
|
|
|
+ length_mapped += PAGE_2M_SIZE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 页面写穿,禁止缓存
|
|
|
+ set_pdt(pde_ptr, mk_pdt((ul)phys_addr_start + length_mapped, flags | (user ? PAGE_USER_PAGE : PAGE_KERNEL_PAGE)));
|
|
|
length_mapped += PAGE_2M_SIZE;
|
|
|
- continue;
|
|
|
}
|
|
|
- // 页面写穿,禁止缓存
|
|
|
- set_pdt(pde_ptr, mk_pdt((ul)phys_addr_start + length_mapped, flags | (user ? PAGE_USER_PAGE : PAGE_KERNEL_PAGE)));
|
|
|
- length_mapped += PAGE_2M_SIZE;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if (flush)
|
|
|
+ if (likely(flush))
|
|
|
flush_tlb();
|
|
|
+ return 0;
|
|
|
+failed:;
|
|
|
+ kerror("Map memory failed. use4k=%d, vaddr=%#018lx, paddr=%#018lx", use4k, virt_addr_start, phys_addr_start);
|
|
|
+ return -EFAULT;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -701,11 +767,41 @@ void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_sta
|
|
|
// 计算当前2M物理页对应的pdt的页表项的物理地址
|
|
|
ul *pde_ptr = pd_ptr + pde_id;
|
|
|
|
|
|
- *pde_ptr = 0;
|
|
|
+ // 存在4级页表
|
|
|
+ if (unlikely(((*pde_ptr) & (1 << 7)) == 0))
|
|
|
+ {
|
|
|
+ // 存在4K页
|
|
|
+ uint64_t pte_id = (((virt_addr_start + length_unmapped) >> PAGE_4K_SHIFT) & 0x1ff);
|
|
|
+ uint64_t *pt_ptr = (uint64_t *)phys_2_virt(*pde_ptr & (~0x1fffUL));
|
|
|
+ uint64_t *pte_ptr = pt_ptr + pte_id;
|
|
|
+
|
|
|
+ // 循环处理4K页表
|
|
|
+ for (; pgt_num.num_PTE > 0 && pte_id < 512; ++pte_id, ++pte_ptr)
|
|
|
+ {
|
|
|
+ --pgt_num.num_PTE;
|
|
|
+ // todo: 当支持使用slab分配4K内存作为进程的4K页之后,在这里需要释放这些4K对象
|
|
|
+ *pte_ptr = 0;
|
|
|
+ length_unmapped += PAGE_4K_SIZE;
|
|
|
+ }
|
|
|
|
|
|
- length_unmapped += PAGE_2M_SIZE;
|
|
|
+ // 4级页表已经空了,释放页表
|
|
|
+ if (unlikely(mm_check_page_table(pt_ptr)) == 0)
|
|
|
+ kfree(pt_ptr);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *pde_ptr = 0;
|
|
|
+ length_unmapped += PAGE_2M_SIZE;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ // 3级页表已经空了,释放页表
|
|
|
+ if (unlikely(mm_check_page_table(pd_ptr)) == 0)
|
|
|
+ kfree(pd_ptr);
|
|
|
}
|
|
|
+ // 2级页表已经空了,释放页表
|
|
|
+ if (unlikely(mm_check_page_table(pdpt_ptr)) == 0)
|
|
|
+ kfree(pdpt_ptr);
|
|
|
}
|
|
|
flush_tlb();
|
|
|
}
|
|
@@ -793,8 +889,8 @@ uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset)
|
|
|
{
|
|
|
for (uint64_t i = old_brk_end_addr; i < end_addr; i += PAGE_2M_SIZE)
|
|
|
{
|
|
|
- kdebug("map [%#018lx]", i);
|
|
|
- mm_map_proc_page_table((uint64_t)current_pcb->mm->pgd, true, i, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, PAGE_2M_SIZE, PAGE_USER_PAGE, true, true);
|
|
|
+ // kdebug("map [%#018lx]", i);
|
|
|
+ mm_map_proc_page_table((uint64_t)current_pcb->mm->pgd, true, i, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, PAGE_2M_SIZE, PAGE_USER_PAGE, true, true, false);
|
|
|
}
|
|
|
current_pcb->mm->brk_end = end_addr;
|
|
|
}
|
|
@@ -850,9 +946,36 @@ bool mm_check_mapped(ul page_table_phys_addr, uint64_t virt_addr)
|
|
|
// 读取pdt页表项
|
|
|
tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr) >> PAGE_2M_SHIFT) & 0x1ff)));
|
|
|
|
|
|
- // todo: 增加对使用了4K页的页表的检测
|
|
|
- if (*tmp != 0)
|
|
|
+ // pde页表项为0
|
|
|
+ if (*tmp == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (*tmp & (1 << 7))
|
|
|
+ {
|
|
|
+ // 当前为2M物理页
|
|
|
return true;
|
|
|
+ }
|
|
|
else
|
|
|
- return false;
|
|
|
+ {
|
|
|
+ // 存在4级页表
|
|
|
+ tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr) >> PAGE_4K_SHIFT) & 0x1ff)));
|
|
|
+ if (*tmp != 0)
|
|
|
+ return true;
|
|
|
+ else
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief 检测是否为有效的2M页(物理内存页)
|
|
|
+ *
|
|
|
+ * @param paddr 物理地址
|
|
|
+ * @return int8_t 是 -> 1
|
|
|
+ * 否 -> 0
|
|
|
+ */
|
|
|
+int8_t mm_is_2M_page(uint64_t paddr)
|
|
|
+{
|
|
|
+ if(likely((paddr >> PAGE_2M_SHIFT)<total_2M_pages))
|
|
|
+ return 1;
|
|
|
+ else return 0;
|
|
|
}
|