Преглед изворни кода

:new: 完成了初始化内存管理单元的代码

fslongjin пре 3 година
родитељ
комит
8131264e3f
5 измењених фајлова са 403 додато и 22 уклоњено
  1. 9 1
      kernel/common/glib.h
  2. 4 2
      kernel/main.c
  3. 217 8
      kernel/mm/mm.c
  4. 172 10
      kernel/mm/mm.h
  5. 1 1
      run_in_bochs.sh

+ 9 - 1
kernel/common/glib.h

@@ -114,8 +114,9 @@ static inline int strlen(char *s)
 
 
 
-inline void *memset(void *dst, unsigned char C, ul Count)
+void *memset(void *dst, unsigned char C, ul Count)
 {
+   
     int d0, d1;
     unsigned long tmp = C * 0x0101010101010101UL;
     __asm__ __volatile__("cld	\n\t"
@@ -136,3 +137,10 @@ inline void *memset(void *dst, unsigned char C, ul Count)
                          : "memory");
     return dst;
 }
+void *memset_c(void *dst, unsigned char c, ul n)
+{
+    unsigned char *s = (unsigned char *)dst;
+    for(int i=0;i<n;++i)
+        s[i] = c;
+    return dst;
+}

+ 4 - 2
kernel/main.c

@@ -9,7 +9,9 @@
 #include "mm/mm.h"
 
 int *FR_address = (int *)0xffff800000a00000; //帧缓存区的地址
-char fxsave_region[512] __attribute__((aligned(16)));
+//char fxsave_region[512] __attribute__((aligned(16)));
+
+struct memory_desc memory_management_struct = {{0}, 0};
 
 void show_welcome()
 {
@@ -75,7 +77,7 @@ void init()
     init_sys_vector();
 
     
-    asm volatile(" fxsave %0 " ::"m"(fxsave_region));
+    //asm volatile(" fxsave %0 " ::"m"(fxsave_region));
     // 初始化内存管理单元
     printk("[ DragonOS ] Initializing memory manage unit...\n");
     mm_init();

+ 217 - 8
kernel/mm/mm.c

@@ -2,28 +2,237 @@
 #include "../common/printk.h"
 
 ul Total_Memory = 0;
+ul total_2M_pages = 0;
+
 void mm_init()
 {
+    printk("[ INFO ] Initializing memory management unit...\n");
     // 实模式下获取到的信息的起始地址,转换为ARDS指针
     struct ARDS *ards_ptr = (struct ARDS *)0xffff800000007e00;
 
     for (int i = 0; i < 32; ++i)
     {
-        printk("Addr = %#10lx,%8lx\tLength = %#10lx,%8lx\tType = %#10lx\n",
-               ards_ptr->BaseAddrH, ards_ptr->BaseAddrL, ards_ptr->LengthH, ards_ptr->LengthL, ards_ptr->type);
+        //printk("Addr = %#18lx\tLength = %#18lx\tType = %#10lx\n",
+        //     ards_ptr->BaseAddr, ards_ptr->Length, ards_ptr->type);
 
         //可用的内存
         if (ards_ptr->type == 1)
-        {
-            Total_Memory += ards_ptr->LengthL;
-            Total_Memory += ((ul)(ards_ptr->LengthH)) << 32;
-        }
+            Total_Memory += ards_ptr->Length;
+
+        // 保存信息到mms
+        memory_management_struct.e820[i].BaseAddr = ards_ptr->BaseAddr;
+        memory_management_struct.e820[i].Length = ards_ptr->Length;
+        memory_management_struct.e820[i].type = ards_ptr->type;
+        memory_management_struct.len_e820 = i;
 
         ++ards_ptr;
 
         // 脏数据
-        if (ards_ptr->type > 4)
+        if (ards_ptr->type > 4 || ards_ptr->Length == 0 || ards_ptr->type < 1)
             break;
     }
-    printk_color(ORANGE, BLACK, "Total amount of RAM DragonOS can use: %ld bytes\n", Total_Memory);
+    printk("[ INFO ] Total amounts of RAM : %ld bytes\n", Total_Memory);
+
+    // 计算有效内存页数
+
+    for (int i = 0; i < memory_management_struct.len_e820; ++i)
+    {
+        if (memory_management_struct.e820[i].type != 1)
+            continue;
+
+        // 将内存段的起始物理地址按照2M进行对齐
+        ul addr_start = PAGE_2M_ALIGN(memory_management_struct.e820[i].BaseAddr);
+        // 将内存段的终止物理地址的低2M区域清空,以实现对齐
+        ul addr_end = ((memory_management_struct.e820[i].BaseAddr + memory_management_struct.e820[i].Length) & PAGE_2M_MASK);
+
+        // 内存段不可用
+        if (addr_end <= addr_start)
+            continue;
+
+        total_2M_pages += ((addr_end - addr_start) >> PAGE_2M_SHIFT);
+    }
+    printk("[ INFO ] Total amounts of 2M pages : %ld.\n", total_2M_pages);
+
+    // 设置内核程序不同部分的起止地址
+    memory_management_struct.kernel_code_start = (ul)&_text;
+    memory_management_struct.kernel_code_end = (ul)&_etext;
+    memory_management_struct.kernel_data_end = (ul)&_edata;
+    memory_management_struct.kernel_end = (ul)&_end;
+
+    // 物理地址空间的最大地址(包含了物理内存、内存空洞、ROM等)
+    ul max_addr = memory_management_struct.e820[memory_management_struct.len_e820].BaseAddr + memory_management_struct.e820[memory_management_struct.len_e820].Length;
+    // 初始化mms的bitmap
+    // bmp的指针指向截止位置的4k对齐的上边界(防止修改了别的数据)
+    memory_management_struct.bmp = (unsigned long *)((memory_management_struct.kernel_end + PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
+    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);
+
+    // 初始化内存页结构
+    // 将页结构映射于bmp之后
+
+    memory_management_struct.pages_struct = (struct Page *)(((unsigned long)memory_management_struct.bmp + memory_management_struct.bmp_len + PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
+
+    memory_management_struct.count_pages = max_addr >> PAGE_2M_SHIFT;
+    memory_management_struct.pages_struct_len = ((max_addr >> PAGE_2M_SHIFT) * sizeof(struct Page) + sizeof(long) - 1) & (~(sizeof(long) - 1));
+    // 将pages_struct全部清空,以备后续初始化
+    memset(memory_management_struct.pages_struct, 0x00, memory_management_struct.pages_struct_len); //init pages memory
+
+    // 初始化内存区域
+    memory_management_struct.zones_struct = (struct Zone *)(((ul)memory_management_struct.pages_struct + memory_management_struct.pages_struct_len + PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
+    // 由于暂时无法计算zone结构体的数量,因此先将其设为0
+    memory_management_struct.count_zones = 0;
+    // zones-struct 成员变量暂时按照5个来计算
+    memory_management_struct.zones_struct_len = (5 * sizeof(struct Zone) + sizeof(ul) - 1) & (~(sizeof(ul) - 1));
+    memset(memory_management_struct.zones_struct, 0x00, memory_management_struct.zones_struct_len);
+
+    // ==== 遍历e820数组,完成成员变量初始化工作 ===
+
+    for (int i = 0; i < memory_management_struct.len_e820; ++i)
+    {
+        if (memory_management_struct.e820[i].type != 1) // 不是操作系统可以使用的物理内存
+            continue;
+        ul addr_start = PAGE_2M_ALIGN(memory_management_struct.e820[i].BaseAddr);
+        ul addr_end = (memory_management_struct.e820[i].BaseAddr + memory_management_struct.e820[i].Length) & PAGE_2M_MASK;
+
+        if (addr_end <= addr_start)
+            continue;
+
+        // zone init
+        struct Zone *z = memory_management_struct.zones_struct + memory_management_struct.count_zones;
+        ++memory_management_struct.count_zones;
+
+        z->zone_addr_start = addr_start;
+        z->zone_addr_end = addr_end;
+        z->zone_length = addr_end - addr_start;
+
+        z->count_pages_using = 0;
+        z->count_pages_free = (addr_end - addr_start) >> PAGE_2M_SHIFT;
+        z->total_pages_link = 0;
+
+        z->attr = 0;
+        z->gmd_struct = &memory_management_struct;
+
+        z->count_pages = (addr_end - addr_start) >> PAGE_2M_SHIFT;
+        z->pages_group = (struct Page *)(memory_management_struct.pages_struct + (addr_start >> PAGE_2M_SHIFT));
+
+        // 初始化页
+        struct Page *p = z->pages_group;
+
+        for (int j = 0; j < z->count_pages; ++j, ++p)
+        {
+            p->zone = z;
+            p->addr_phys = addr_start + PAGE_2M_SIZE * j;
+            p->attr = 0;
+
+            p->ref_counts = 0;
+            p->age = 0;
+
+            // 将bmp中对应的位 复位
+            *(memory_management_struct.bmp + ((p->addr_phys >> PAGE_2M_SHIFT) >> 6)) ^= (1UL << ((p->addr_phys >> PAGE_2M_SHIFT) % 64));
+        }
+    }
+
+    // 初始化0~2MB的物理页
+    // 由于这个区间的内存由多个内存段组成,因此不会被以上代码初始化,需要我们手动配置page[0]。
+
+    memory_management_struct.pages_struct->zone = memory_management_struct.zones_struct;
+    memory_management_struct.pages_struct->addr_phys = 0UL;
+    memory_management_struct.pages_struct->attr = 0;
+    memory_management_struct.pages_struct->ref_counts = 0;
+    memory_management_struct.pages_struct->age = 0;
+
+    // 计算zone结构体的总长度(按照64位对齐)
+    memory_management_struct.zones_struct_len = (memory_management_struct.count_zones * sizeof(struct Zone) + sizeof(ul) - 1) & (~(sizeof(ul) - 1));
+
+    printk_color(ORANGE, BLACK, "bmp:%#18lx, bmp_len:%#18lx, bits_size:%#18lx\n", memory_management_struct.bmp, memory_management_struct.bmp_len, memory_management_struct.bits_size);
+
+    printk_color(ORANGE, BLACK, "pages_struct:%#18lx, count_pages:%#18lx, pages_struct_len:%#18lx\n", memory_management_struct.pages_struct, memory_management_struct.count_pages, memory_management_struct.pages_struct_len);
+
+    printk_color(ORANGE, BLACK, "zones_struct:%#18lx, count_zones:%#18lx, zones_struct_len:%#18lx\n", memory_management_struct.zones_struct, memory_management_struct.count_zones, memory_management_struct.zones_struct_len);
+
+    ZONE_DMA_INDEX = 0;    //need rewrite in the future
+    ZONE_NORMAL_INDEX = 0; //need rewrite in the future
+
+    for (int i = 0; i < memory_management_struct.count_zones; ++i) //need rewrite in the future
+    {
+        struct Zone *z = memory_management_struct.zones_struct + i;
+        printk_color(ORANGE, BLACK, "zone_addr_start:%#18lx, zone_addr_end:%#18lx, zone_length:%#18lx, pages_group:%#18lx, count_pages:%#18lx\n",
+                     z->zone_addr_start, z->zone_addr_end, z->zone_length, z->pages_group, z->count_pages);
+
+        // 1GB以上的内存空间不做映射
+        if (z->zone_addr_start == 0x100000000)
+            ZONE_UNMAPED_INDEX = i;
+    }
+    // 设置内存页管理结构的地址,预留了一段空间,防止内存越界。
+    memory_management_struct.end_of_struct = (ul)((ul)memory_management_struct.zones_struct + memory_management_struct.zones_struct_len + sizeof(long) * 32) & (~(sizeof(long) - 1));
+
+    printk_color(ORANGE, BLACK, "code_start:%#18lx, code_end:%#18lx, data_end:%#18lx, kernel_end:%#18lx, end_of_struct:%#18lx\n",
+                 memory_management_struct.kernel_code_start, memory_management_struct.kernel_code_end, memory_management_struct.kernel_data_end, memory_management_struct.kernel_end, memory_management_struct.end_of_struct);
+
+    // 初始化内存管理单元结构所占的物理页的结构体
+
+    ul mms_max_page = (virt_2_phys(memory_management_struct.end_of_struct) >> PAGE_2M_SHIFT); // 内存管理单元所占据的序号最大的物理页
+
+    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);
+    }
+
+    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));
+
+    // 消除一致性页表映射,将页目录(PML4E)的前10项清空
+    for(int i=0;i<10;++i)
+        *(phys_2_virt(cr3)+i) = 0UL;
+    
+    flush_tlb();
+
+    printk("[ INFO ] Memory management unit initialized.\n");
+
+}
+
+/**
+ * @brief 初始化内存页
+ * 
+ * @param page 内存页结构体
+ * @param flags 标志位
+ * 对于新页面: 初始化struct page
+ * 对于当前页面属性/flags中含有引用属性或共享属性时,则只增加struct page和struct zone的被引用计数。否则就只是添加页表属性,并置位bmp的相应位。
+ * @return unsigned long 
+ */
+unsigned long page_init(struct Page *page, ul flags)
+{
+    // 全新的页面
+    if (!page->attr)
+    {
+        // 将bmp对应的标志位置位
+        *(memory_management_struct.bmp + ((page->addr_phys >> PAGE_2M_SHIFT) >> 6)) |= (1UL << ((page->addr_phys >> PAGE_2M_SHIFT) % 64));
+
+        page->attr = flags;
+        ++(page->ref_counts);
+        ++(page->zone->count_pages_using);
+        --(page->zone->count_pages_free);
+        ++(page->zone->total_pages_link);
+    }
+    // 不是全新的页面,而是含有引用属性/共享属性
+    else if ((page->attr & PAGE_REFERENCED) || (page->attr & PAGE_K_SHARE_TO_U) || (flags & PAGE_REFERENCED) || (flags & PAGE_K_SHARE_TO_U))
+    {
+        page->attr |= flags;
+        ++(page->ref_counts);
+        ++(page->zone->total_pages_link);
+    }
+    else
+    {
+        // 将bmp对应的标志位置位
+        *(memory_management_struct.bmp + ((page->addr_phys >> PAGE_2M_SHIFT) >> 6)) |= (1UL << ((page->addr_phys >> PAGE_2M_SHIFT) % 64));
+        page->attr |= flags;
+    }
+    return 0;
 }

+ 172 - 10
kernel/mm/mm.h

@@ -1,21 +1,183 @@
 #pragma once
 
-#include"../common/glib.h"
+#include "../common/glib.h"
+
+// 每个页表的项数
+// 64位下,每个页表4k,每条页表项8B,故一个页表有512条
+#define PTRS_PER_PGT 512
+
+// 内核层的起始地址
+#define KERNEL_BASE_ADDR 0xffff800000000000
+
+#define PAGE_4K_SHIFT 12
+#define PAGE_2M_SHIFT 21
+#define PAGE_1G_SHIFT 30
+
+// 不同大小的页的容量
+#define PAGE_4K_SIZE (1UL << PAGE_4K_SHIFT)
+#define PAGE_2M_SIZE (1UL << PAGE_2M_SHIFT)
+#define PAGE_1G_SIZE (1UL << PAGE_1G_SHIFT)
+
+// 屏蔽低于x的数值
+#define PAGE_4K_MASK (~(PAGE_4K_SIZE - 1))
+#define PAGE_2M_MASK (~(PAGE_2M_SIZE - 1))
+
+// 将addr按照x的上边界对齐
+#define PAGE_4K_ALIGN(addr) (((unsigned long)(addr) + PAGE_4K_SIZE - 1) & PAGE_4K_MASK)
+#define PAGE_2M_ALIGN(addr) (((unsigned long)(addr) + PAGE_2M_SIZE - 1) & PAGE_2M_MASK)
+
+// 虚拟地址与物理地址转换
+#define virt_2_phys(addr) ((unsigned long)(addr)-KERNEL_BASE_ADDR)
+#define phys_2_virt(addr) ((unsigned long *)((unsigned long)(addr) + KERNEL_BASE_ADDR))
+
+// ===== 页面属性 =====
+// 页面在页表中已被映射
+#define PAGE_PGT_MAPPED (1 << 0)
+// 内核初始化程序的页
+#define PAGE_KERNEL_INIT (1 << 1)
+// 引用的页
+#define PAGE_REFERENCED (1 << 2)
+// 脏页
+#define PAGE_DIRTY (1 << 3)
+// 使用中的页
+#define PAGE_ACTIVE (1 << 4)
+// 过时的页
+#define PAGE_UP_TO_DATE (1 << 5)
+// 设备对应的页
+#define PAGE_DEVICE (1 << 6)
+// 内核层页
+#define PAGE_KERNEL (1 << 7)
+// 内核共享给用户态程序的页面
+#define PAGE_K_SHARE_TO_U (1 << 8)
+// slab内存分配器的页
+#define PAGE_SLAB (1 << 9)
 
 // Address Range Descriptor Structure 地址范围描述符
 struct ARDS
 {
-    unsigned int BaseAddrL; // 基地址低32位
-    unsigned int BaseAddrH; // 基地址高32位
-    unsigned int LengthL;   // 内存长度低32位   以字节为单位
-    unsigned int LengthH;   // 内存长度高32位
-    unsigned int type;      // 本段内存的类型
-                            // type=1 表示可以被操作系统使用
-                            // type=2 ARR - 内存使用中或被保留,操作系统不能使用
-                            // 其他 未定义,操作系统需要将其视为ARR
+    ul BaseAddr;           // 基地址
+    ul Length;             // 内存长度   以字节为单位
+    unsigned int type;     // 本段内存的类型
+                           // type=1 表示可以被操作系统使用
+                           // type=2 ARR - 内存使用中或被保留,操作系统不能使用
+                           // 其他 未定义,操作系统需要将其视为ARR
+} __attribute__((packed)); // 修饰该结构体不会生成对齐空间,改用紧凑格式
+
+struct memory_desc
+{
+
+    struct ARDS e820[32]; // 物理内存段结构数组
+    ul len_e820;          // 物理内存段长度
+
+    ul *bmp;      // 物理空间页映射位图
+    ul bmp_len;   //  bmp的长度
+    ul bits_size; // 物理地址空间页数量
+
+    struct Page *pages_struct;
+    ul count_pages;      // struct page结构体的总数
+    ul pages_struct_len; // pages_struct链表的长度
+
+    struct Zone *zones_struct;
+    ul count_zones;      // zone结构体的数量
+    ul zones_struct_len; // zones_struct列表的长度
+
+    ul kernel_code_start, kernel_code_end; // 内核程序代码段起始地址、结束地址
+    ul kernel_data_end, kernel_end;        // 内核程序数据段结束地址、 内核程序结束地址
+
+    ul end_of_struct; // 内存页管理结构的结束地址
+};
+
+struct Zone
+{
+    // 指向内存页的指针
+    struct Page *pages_group;
+    ul count_pages; // 本区域的struct page结构体总数
+
+    // 本内存区域的起始、结束的页对齐地址
+    ul zone_addr_start;
+    ul zone_addr_end;
+    ul zone_length; // 区域长度
+
+    // 本区域空间的属性
+    ul attr;
+
+    struct memory_desc *gmd_struct;
+
+    // 本区域正在使用中和空闲中的物理页面数量
+    ul count_pages_using;
+    ul count_pages_free;
+
+    // 物理页被引用次数
+    ul total_pages_link;
+};
+
+struct Page
+{
+    // 本页所属的内存域结构体
+    struct Zone *zone;
+    // 本页对应的物理地址
+    ul addr_phys;
+    // 页面属性
+    ul attr;
+    // 页面被引用的次数
+    ul ref_counts;
+    // 本页的创建时间
+    ul age;
 };
 
+extern struct memory_desc memory_management_struct;
+
+// 导出内核程序的几个段的起止地址
+extern char _text;
+extern char _etext;
+extern char _edata;
+extern char _end;
 
+// 每个区域的索引
 
+int ZONE_DMA_INDEX = 0;
+int ZONE_NORMAL_INDEX = 0;  //low 1GB RAM ,was mapped in pagetable
+int ZONE_UNMAPED_INDEX = 0; //above 1GB RAM,unmapped in pagetable
+
+// 初始化内存管理单元
+void mm_init();
+
+/**
+ * @brief 初始化内存页
+ * 
+ * @param page 内存页结构体
+ * @param flags 标志位
+ * 对于新页面: 初始化struct page
+ * 对于当前页面属性/flags中含有引用属性或共享属性时,则只增加struct page和struct zone的被引用计数。否则就只是添加页表属性,并置位bmp的相应位。
+ * @return unsigned long 
+ */
+unsigned long page_init(struct Page *page, ul flags);
+
+/**
+ * @brief 读取CR3寄存器的值(存储了页目录的基地址)
+ * 
+ * @return unsigned*  cr3的值的指针
+ */
+unsigned long *get_CR3()
+{
+    ul *tmp;
+    __asm__ __volatile__(
+        "movq %%cr3, %0\n\t"
+        : "=r"(tmp)::"memory");
+    return tmp;
+}
 
-void mm_init();
+/**
+ * @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);

+ 1 - 1
run_in_bochs.sh

@@ -8,7 +8,7 @@ fi
 # 第一个参数如果是--notbuild 那就不构建,直接运行
 if [ ! "$1" == "--nobuild" ]; then
     echo "开始构建..."
-    make all
+    make all -j 16
     make clean
 fi