Browse Source

:new: 完成了简单的内存管理单元,能分配内存页面

fslongjin 3 years ago
parent
commit
98e62e1e19
4 changed files with 195 additions and 23 deletions
  1. 59 0
      kernel/common/kprint.h
  2. 20 2
      kernel/main.c
  3. 84 9
      kernel/mm/mm.c
  4. 32 12
      kernel/mm/mm.h

+ 59 - 0
kernel/common/kprint.h

@@ -0,0 +1,59 @@
+/**
+ * @file kprint.h
+ * @author longjin
+ * @brief 内核日志打印程序
+ * @date 2022-01-28
+ * 
+ * @copyright Copyright (c) 2022 longjin
+ * 
+ */
+
+#pragma once
+#include "printk.h"
+
+#define kinfo(...)           \
+    do                       \
+    {                        \
+        printk("[ INFO ] "); \
+        printk(__VA_ARGS__); \
+        printk("\n");        \
+    } while (0);
+
+#define kdebug(...)           \
+    do                        \
+    {                         \
+        printk("[ DEBUG ] "); \
+        printk(__VA_ARGS__);  \
+        printk("\n");         \
+    } while (0);
+
+#define kwarn(...)                           \
+    do                                       \
+    {                                        \
+        printk("[ ");                        \
+        printk_color(YELLOW, BLACK, "WARN"); \
+        printk(" ] ");                       \
+        printk(__VA_ARGS__);                 \
+        printk("\n");                        \
+    } while (0);
+
+#define kerror(...)                        \
+    do                                     \
+    {                                      \
+        printk("[ ");                      \
+        printk_color(RED, BLACK, "ERROR"); \
+        printk(" ] ");                     \
+        printk(__VA_ARGS__);               \
+        printk("\n");                      \
+    } while (0);
+
+#define kterminated(...)                        \
+    do                                     \
+    {                                      \
+        printk("[ ");                      \
+        printk_color(RED, BLACK, "TERMINATED"); \
+        printk(" ] ");                     \
+        printk(__VA_ARGS__);               \
+        printk("\n");                      \
+    } while (0);
+

+ 20 - 2
kernel/main.c

@@ -7,6 +7,7 @@
 #include "exception/gate.h"
 #include "exception/trap.h"
 #include "mm/mm.h"
+#include "common/kprint.h"
 
 int *FR_address = (int *)0xffff800000a00000; //帧缓存区的地址
 //char fxsave_region[512] __attribute__((aligned(16)));
@@ -61,6 +62,23 @@ void test_printk()
     printk("\nTest base 16 : %d --> %#X\n", 255, 255);
 }
 
+// 测试内存管理单元
+void test_mm()
+{
+    kinfo("Testing memory management unit...");
+    printk("bmp[0]:%#018x\tbmp[1]%#018lx\n", *memory_management_struct.bmp, *memory_management_struct.bmp + 1);
+    kinfo("Try to allocate 64 memory pages.");
+    struct Page *page = alloc_pages(ZONE_NORMAL, 64, PAGE_PGT_MAPPED | PAGE_ACTIVE | PAGE_KERNEL);
+    for (int i = 0; i <= 65; ++i)
+    {
+        printk("page%d\tattr:%#018lx\tphys_addr:%#018lx\t", i, page->attr, page->addr_phys);
+        ++page;
+        if (((i + 1) % 2) == 0)
+            printk("\n");
+    }
+    printk("bmp[0]:%#018x\tbmp[1]%#018lx\n", *memory_management_struct.bmp, *memory_management_struct.bmp + 1);
+}
+
 void init()
 {
     // 初始化printk
@@ -76,7 +94,6 @@ void init()
     // 初始化中断描述符表
     init_sys_vector();
 
-    
     //asm volatile(" fxsave %0 " ::"m"(fxsave_region));
     // 初始化内存管理单元
     printk("[ DragonOS ] Initializing memory manage unit...\n");
@@ -87,7 +104,8 @@ void Start_Kernel(void)
 {
 
     init();
-    show_welcome();
+    //show_welcome();
+    test_mm();
 
     //test_printk();
 

+ 84 - 9
kernel/mm/mm.c

@@ -67,7 +67,6 @@ void mm_init()
     memory_management_struct.bits_size = max_addr >> PAGE_2M_SHIFT;                                                                                         // 物理地址空间的最大页面数
     memory_management_struct.bmp_len = ((unsigned long)((max_addr >> PAGE_2M_SHIFT) + sizeof(unsigned long) * 8 - 1) / 8) & (~(sizeof(unsigned long) - 1)); // bmp由多少个unsigned long变量组成
 
-
     // 初始化bitmap, 先将整个bmp空间全部置位。稍后再将可用物理内存页复位。
     memset(memory_management_struct.bmp, 0xff, memory_management_struct.bmp_len);
 
@@ -179,23 +178,22 @@ void mm_init()
 
     for (ul j = 0; j <= mms_max_page; ++j)
     {
-        page_init(memory_management_struct.pages_struct+j, PAGE_PGT_MAPPED|PAGE_KERNEL|PAGE_KERNEL_INIT|PAGE_ACTIVE);
+        page_init(memory_management_struct.pages_struct + j, PAGE_PGT_MAPPED | PAGE_KERNEL | PAGE_KERNEL_INIT | PAGE_ACTIVE);
     }
 
-    ul* cr3 = get_CR3();
+    ul *cr3 = get_CR3();
 
     printk_color(INDIGO, BLACK, "cr3:\t%#018lx\n", cr3);
-    printk_color(INDIGO, BLACK, "*cr3:\t%#018lx\n", *(phys_2_virt(cr3))&(~0xff));
-    printk_color(INDIGO, BLACK, "**cr3:\t%#018lx\n", *phys_2_virt(*(phys_2_virt(cr3))&(~0xff))&(~0xff));
+    printk_color(INDIGO, BLACK, "*cr3:\t%#018lx\n", *(phys_2_virt(cr3)) & (~0xff));
+    printk_color(INDIGO, BLACK, "**cr3:\t%#018lx\n", *phys_2_virt(*(phys_2_virt(cr3)) & (~0xff)) & (~0xff));
 
     // 消除一致性页表映射,将页目录(PML4E)的前10项清空
-    for(int i=0;i<10;++i)
-        *(phys_2_virt(cr3)+i) = 0UL;
-    
+    for (int i = 0; i < 10; ++i)
+        *(phys_2_virt(cr3) + i) = 0UL;
+
     flush_tlb();
 
     printk("[ INFO ] Memory management unit initialized.\n");
-
 }
 
 /**
@@ -235,4 +233,81 @@ unsigned long page_init(struct Page *page, ul flags)
         page->attr |= flags;
     }
     return 0;
+}
+
+/**
+ * @brief 从已初始化的页结构中搜索符合申请条件的、连续num个struct page
+ * 
+ * @param zone_select 选择内存区域, 可选项:dma, mapped in pgt, unmapped in pgt
+ * @param num 需要申请的连续内存页的数量 num<=64
+ * @param flags 将页面属性设置成flag
+ * @return struct Page* 
+ */
+struct Page *alloc_pages(unsigned int zone_select, int num, ul flags)
+{
+    ul zone_start = 0, zone_end = 0;
+    switch (zone_select)
+    {
+    case ZONE_DMA:
+        // DMA区域
+        zone_start = 0;
+        zone_end = ZONE_DMA_INDEX;
+        break;
+    case ZONE_NORMAL:
+        zone_start = ZONE_DMA_INDEX;
+        zone_end = ZONE_NORMAL_INDEX;
+        break;
+    case ZONE_UNMAPPED_IN_PGT:
+        zone_start = ZONE_NORMAL_INDEX;
+        zone_end = ZONE_UNMAPED_INDEX;
+        break;
+
+    default:
+        printk("[ ");
+        printk_color(YELLOW, BLACK, "WARN");
+        printk(" ] In alloc_pages: param: zone_select incorrect.\n");
+        // 返回空
+        return NULL;
+        break;
+    }
+
+    for (int i = zone_start; i <= zone_end; ++i)
+    {
+        if ((memory_management_struct.zones_struct + i)->count_pages_free < num)
+            continue;
+
+        struct Zone *z = memory_management_struct.zones_struct + i;
+
+        // 区域对应的起止页号以及区域拥有的页面数
+        ul page_start = (z->zone_addr_start >> PAGE_2M_SHIFT);
+        ul page_end = (z->zone_addr_end >> PAGE_2M_SHIFT);
+        ul page_num = (z->zone_length >> PAGE_2M_SHIFT);
+
+        ul tmp = 64 - page_start % 64;
+        for (ul j = page_start; j <= page_end; j += ((j % 64) ? tmp : 64))
+        {
+            // 按照bmp中的每一个元素进行查找
+            // 先将p定位到bmp的起始元素
+            ul *p = memory_management_struct.bmp + (j >> 6);
+
+            ul shift = j % 64;
+
+            for (int k = shift; k < 64 - shift; ++k)
+            {
+                // 寻找连续num个空页
+                if (!(((*p >> k) | (*(p + 1) << (64 - k))) & (num == 64 ? 0xffffffffffffffffUL : ((1 << num) - 1))))
+                {
+                    ul start_page_num = j + k - shift; // 计算得到要开始获取的内存页的页号(书上的公式有问题,这个是改过之后的版本)
+                    for(int l=0;l<num;++l)
+                    {
+                        struct Page* x = memory_management_struct.pages_struct+start_page_num+l;
+                        
+                        page_init(x, flags);
+                    }
+                    // 成功分配了页面,返回第一个页面的指针
+                    return (struct Page*)(memory_management_struct.pages_struct+start_page_num);
+                }
+            }
+        }
+    }
 }

+ 32 - 12
kernel/mm/mm.h

@@ -30,6 +30,14 @@
 #define virt_2_phys(addr) ((unsigned long)(addr)-KERNEL_BASE_ADDR)
 #define phys_2_virt(addr) ((unsigned long *)((unsigned long)(addr) + KERNEL_BASE_ADDR))
 
+// ===== 内存区域属性 =====
+// DMA区域
+#define ZONE_DMA (1<<0)
+// 已在页表中映射的区域
+#define ZONE_NORMAL (1<<1)
+// 未在页表中映射的区域
+#define ZONE_UNMAPPED_IN_PGT (1<<2)
+
 // ===== 页面属性 =====
 // 页面在页表中已被映射
 #define PAGE_PGT_MAPPED (1 << 0)
@@ -52,6 +60,23 @@
 // slab内存分配器的页
 #define PAGE_SLAB (1 << 9)
 
+
+/**
+ * @brief 刷新TLB的宏定义
+ * 由于任何写入cr3的操作都会刷新TLB,因此这个宏定义可以刷新TLB
+ */
+#define flush_tlb()                \
+    do                             \
+    {                              \
+        ul tmp;                    \
+        __asm__ __volatile__(      \
+            "movq %%cr3, %0\n\t"   \
+            "movq %0, %%cr3\n\t"   \
+            : "=r"(tmp)::"memory"); \
+                                   \
+    } while (0);
+
+
 // Address Range Descriptor Structure 地址范围描述符
 struct ARDS
 {
@@ -168,16 +193,11 @@ unsigned long *get_CR3()
 }
 
 /**
- * @brief 刷新TLB的宏定义
- * 由于任何写入cr3的操作都会刷新TLB,因此这个宏定义可以刷新TLB
+ * @brief 从已初始化的页结构中搜索符合申请条件的、连续num个struct page
+ * 
+ * @param zone_select 选择内存区域, 可选项:dma, mapped in pgt, unmapped in pgt
+ * @param num 需要申请的内存页的数量 num<=64
+ * @param flags 将页面属性设置成flag
+ * @return struct Page* 
  */
-#define flush_tlb()                \
-    do                             \
-    {                              \
-        ul tmp;                    \
-        __asm__ __volatile__(      \
-            "movq %%cr3, %0\n\t"   \
-            "movq %0, %%cr3\n\t"   \
-            : "=r"(tmp)::"memory"); \
-                                   \
-    } while (0);
+struct Page* alloc_pages(unsigned int zone_select, int num, ul flags);