فهرست منبع

new: 初步实现了mmio的伙伴系统

fslongjin 2 سال پیش
والد
کامیت
77633e2f19
9فایلهای تغییر یافته به همراه384 افزوده شده و 5 حذف شده
  1. 2 1
      .vscode/settings.json
  2. 8 2
      kernel/mm/Makefile
  3. 1 0
      kernel/mm/mm.c
  4. 2 0
      kernel/mm/mm.h
  5. 260 0
      kernel/mm/mmio-buddy.c
  6. 51 0
      kernel/mm/mmio-buddy.h
  7. 34 0
      kernel/mm/mmio.c
  8. 25 0
      kernel/mm/mmio.h
  9. 1 2
      kernel/process/process.c

+ 2 - 1
.vscode/settings.json

@@ -126,7 +126,8 @@
         "mm-types.h": "c",
         "vfs.h": "c",
         "current.h": "c",
-        "proc-types.h": "c"
+        "proc-types.h": "c",
+        "traceback.h": "c"
     },
     "C_Cpp.errorSquiggles": "Enabled",
     "esbonio.sphinx.confDir": ""

+ 8 - 2
kernel/mm/Makefile

@@ -2,7 +2,7 @@
 CFLAGS += -I .
 
 
-all:mm.o slab.o mm-stat.o vma.o mmap.o utils.o
+all:mm.o slab.o mm-stat.o vma.o mmap.o utils.o mmio.o mmio-buddy.o
 
 mm.o: mm.c
 	gcc $(CFLAGS) -c mm.c -o mm.o
@@ -20,4 +20,10 @@ mmap.o: mmap.c
 	gcc $(CFLAGS) -c mmap.c -o mmap.o
 
 utils.o: utils.c
-	gcc $(CFLAGS) -c utils.c -o utils.o
+	gcc $(CFLAGS) -c utils.c -o utils.o
+
+mmio.o: mmio.c
+	gcc $(CFLAGS) -c mmio.c -o mmio.o
+
+mmio-buddy.o: mmio-buddy.c
+	gcc $(CFLAGS) -c mmio-buddy.c -o mmio-buddy.o

+ 1 - 0
kernel/mm/mm.c

@@ -11,6 +11,7 @@
 
 uint64_t mm_Total_Memory = 0;
 uint64_t mm_total_2M_pages = 0;
+struct mm_struct initial_mm = {0};
 
 struct memory_desc memory_management_struct = {{0}, 0};
 

+ 2 - 0
kernel/mm/mm.h

@@ -13,6 +13,8 @@
 #define PAGE_OFFSET (0xffff800000000000UL)
 #define KERNEL_BASE_LINEAR_ADDR (0xffff800000000000UL)
 #define USER_MAX_LINEAR_ADDR 0x00007fffffffffffUL
+#define MMIO_BASE (0xffffa10000000000UL)
+#define MMIO_TOP (0xffffa20000000000UL)
 
 #define PAGE_4K_SHIFT 12
 #define PAGE_2M_SHIFT 21

+ 260 - 0
kernel/mm/mmio-buddy.c

@@ -0,0 +1,260 @@
+#include "mmio-buddy.h"
+#include <mm/slab.h>
+
+/**
+ * @brief 将内存对象大小的幂转换成内存池中的数组的下标
+ *
+ */
+#define __exp2index(exp) (exp - 12)
+
+/**
+ * @brief 计算伙伴块的内存虚拟地址
+ *
+ */
+#define buddy_block_vaddr(vaddr, exp) (vaddr ^ (1UL << exp))
+
+static struct mmio_buddy_mem_pool __mmio_pool; // mmio buddy内存池
+
+/**
+ * @brief 往指定的地址空间链表中添加一个地址区域
+ *
+ * @param index
+ * @param region
+ * @return __always_inline
+ */
+static __always_inline void __buddy_add_region_obj(int index, struct __mmio_buddy_addr_region *region)
+{
+    struct __mmio_free_region_list *lst = &__mmio_pool.free_regions[index];
+    list_init(&region->list);
+    list_append(&lst->list_head, &region->list);
+    ++lst->num_free;
+}
+
+/**
+ * @brief 创建新的地址区域结构体
+ *
+ * @param vaddr 虚拟地址
+ * @return 创建好的地址区域结构体
+ */
+static __always_inline struct __mmio_buddy_addr_region *__mmio_buddy_create_region(uint64_t vaddr)
+{
+    // 申请内存块的空间
+    struct __mmio_buddy_addr_region *region = (struct __mmio_buddy_addr_region *)kzalloc(sizeof(struct __mmio_buddy_addr_region), 0);
+    list_init(&region->list);
+    region->vaddr = vaddr;
+    return region;
+}
+
+/**
+ * @brief 释放address region结构体
+ *
+ * @param region 待释放的结构体
+ */
+static __always_inline void __release_addr_region(struct __mmio_buddy_addr_region *region)
+{
+    kfree(region);
+}
+
+/**
+ * @brief 将给定大小为(2^exp)的地址空间一分为二,并插入下一级的链表中
+ *
+ * @param region 要被分割的地址区域
+ * @param exp 要被分割的地址区域的大小的幂
+ */
+static __always_inline void __buddy_split(struct __mmio_buddy_addr_region *region, int exp)
+{
+    // 计算分裂出来的新的伙伴块的地址
+    struct __mmio_buddy_addr_region *new_region = __mmio_buddy_create_region(buddy_block_vaddr(region->vaddr, exp - 1));
+    __buddy_add_region_obj(__exp2index(exp - 1), region);
+    __buddy_add_region_obj(__exp2index(exp - 1), new_region);
+}
+
+/**
+ * @brief 合并两个伙伴块
+ *
+ * @param x 第一个伙伴块
+ * @param y 第二个伙伴块
+ * @param exp x、y大小的幂
+ * @return int 错误码
+ */
+static __always_inline int __buddy_merge_blocks(struct __mmio_buddy_addr_region *x, struct __mmio_buddy_addr_region *y, int exp)
+{
+    // 判断这两个是否是一对伙伴
+    if (unlikely(x->vaddr != buddy_block_vaddr(y->vaddr, exp))) // 不是一对伙伴
+        return -EINVAL;
+
+    // === 是一对伙伴,将他们合并
+    // __mmio_pool.free_regions[__exp2index(exp)].num_free -=2;
+    // 释放y
+    __release_addr_region(y);
+    // 插入x
+    __buddy_add_region_obj(__exp2index(exp + 1), x);
+
+    return 0;
+}
+
+/**
+ * @brief 从空闲链表中取出指定大小的内存区域, 并从链表中删除
+ *
+ * @param exp 内存大小的幂
+ * @return __always_inline struct* 内存区域结构体
+ */
+static __always_inline struct __mmio_buddy_addr_region *__buddy_pop_region(int exp)
+{
+    if (unlikely(&__mmio_pool.free_regions[__exp2index(exp)].list_head))
+        return NULL;
+    struct __mmio_buddy_addr_region *r = container_of(list_next(&__mmio_pool.free_regions[__exp2index(exp)].list_head), struct __mmio_buddy_addr_region, list);
+    list_del(&r->list);
+    // 区域计数减1
+    --__mmio_pool.free_regions[__exp2index(exp)].num_free;
+    return r;
+}
+
+/**
+ * @brief 寻找给定块的伙伴块
+ *
+ * @param x 给定的内存块
+ * @param exp 内存块大小
+ * @return 伙伴块的指针
+ */
+static __always_inline struct __mmio_buddy_addr_region *__find_buddy(struct __mmio_buddy_addr_region *x, int exp)
+{
+    // 当前为空
+    if (unlikely(list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head)))
+        return NULL;
+    // 遍历链表以寻找伙伴块
+    uint64_t buddy_vaddr = buddy_block_vaddr(x->vaddr, exp);
+    struct List *list = &__mmio_pool.free_regions[__exp2index(exp)].list_head;
+
+    do
+    {
+        list = list_next(list);
+        struct __mmio_buddy_addr_region *bd = container_of(list, struct __mmio_buddy_addr_region, list);
+        if (bd->vaddr == buddy_vaddr) // 找到了伙伴块
+            return bd;
+    } while (list_next(list) != &__mmio_pool.free_regions[__exp2index(exp)].list_head);
+
+    return NULL;
+}
+/**
+ * @brief 把某个大小的伙伴块全都合并成大小为(2^(exp+1))的块
+ *
+ * @param exp 地址空间大小(2^exp)
+ */
+static void __buddy_merge(int exp)
+{
+    struct __mmio_free_region_list *free_list = &__mmio_pool.free_regions[__exp2index(exp)];
+    // 若链表为空
+    if (list_empty(&free_list->list_head))
+        return;
+
+    struct List *list = list_next(&free_list->list_head);
+
+    do
+    {
+        struct __mmio_buddy_addr_region *ptr = container_of(list, struct __mmio_buddy_addr_region, list);
+        // 寻找是否有伙伴块
+        struct __mmio_buddy_addr_region *bd = __find_buddy(ptr, exp);
+
+        // 一定要在merge之前执行,否则list就被重置了
+        list = list_next(list);
+
+        if (bd != NULL) // 找到伙伴块
+        {
+            free_list->num_free -= 2;
+            list_del(&ptr->list);
+            list_del(&bd->list);
+            __buddy_merge_blocks(ptr, bd, exp);
+        }
+
+    } while (list != &free_list->list_head);
+}
+
+/**
+ * @brief 从buddy中申请一块指定大小的内存区域
+ *
+ * @param exp 内存区域的大小(2^exp)
+ * @return struct __mmio_buddy_addr_region* 符合要求的内存区域。没有满足要求的时候,返回NULL
+ */
+static struct __mmio_buddy_addr_region *__buddy_query_addr_region(int exp)
+{
+    if (exp >= MMIO_BUDDY_MAX_EXP)
+        return NULL;
+    if (!list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head))
+        goto has_block;
+
+    // 若没有符合要求的内存块,则先尝试分裂大的块
+    for (int cur_exp = exp; exp <= MMIO_BUDDY_MAX_EXP; ++cur_exp)
+    {
+        if (unlikely(list_empty(&__mmio_pool.free_regions[__exp2index(cur_exp)].list_head))) // 一直寻找到有空闲空间的链表
+            continue;
+
+        // 找到了,逐级向下split
+        for (int down_exp = cur_exp; down_exp > exp; --down_exp)
+        {
+            // 取出一块空闲区域
+            struct __mmio_buddy_addr_region *r = __buddy_pop_region(down_exp);
+            __buddy_split(r, down_exp);
+        }
+        break;
+    }
+
+    if (!list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head))
+        goto has_block;
+
+    // 尝试合并小的伙伴块
+    for (int cur_exp = MMIO_BUDDY_MIN_EXP; cur_exp < exp; ++cur_exp)
+        __buddy_merge(cur_exp);
+    // 再次尝试获取符合要求的内存块,若仍不成功,则说明mmio空间耗尽
+    if (!list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head))
+        goto has_block;
+    else
+        goto failed;
+failed:;
+    return NULL;
+has_block:; // 有可用的内存块,分配
+    return __buddy_pop_region(exp);
+}
+
+/**
+ * @brief 归还一块内存空间到buddy
+ *
+ * @param vaddr 虚拟地址
+ * @param exp 内存空间的大小(2^exp)
+ * @return int 返回码
+ */
+static __always_inline int __buddy_give_back(uint64_t vaddr, int exp)
+{
+    // 确保内存对齐,低位都要为0
+    if (vaddr & ((1UL << exp) - 1))
+        return -EINVAL;
+
+    struct __mmio_buddy_addr_region *region = __mmio_buddy_create_region(vaddr);
+    // 加入buddy
+    __buddy_add_region_obj(__exp2index(exp), region);
+    return 0;
+}
+
+/**
+ * @brief 初始化mmio的伙伴系统
+ *
+ */
+void mmio_buddy_init()
+{
+    memset(&__mmio_pool, 0, sizeof(struct mmio_buddy_mem_pool));
+    spin_init(&__mmio_pool.op_lock);
+
+    // 初始化各个链表的头部
+    for (int i = 0; i < MMIO_BUDDY_REGION_COUNT; ++i)
+    {
+        list_init(&__mmio_pool.free_regions[i].list_head);
+        __mmio_pool.free_regions[i].num_free = 0;
+    }
+
+    // 创建一堆1GB的地址块
+    uint32_t cnt_1g_blocks = (MMIO_TOP - MMIO_BASE) / PAGE_1G_SIZE;
+    uint64_t vaddr_base = MMIO_BASE;
+    for (uint32_t i = 0; i < cnt_1g_blocks; ++i, vaddr_base += PAGE_1G_SIZE)
+        __buddy_give_back(vaddr_base, PAGE_1G_SHIFT);
+    
+}

+ 51 - 0
kernel/mm/mmio-buddy.h

@@ -0,0 +1,51 @@
+#pragma once
+#include <common/sys/types.h>
+#include <common/glib.h>
+#include "mm-types.h"
+#include "mm.h"
+
+#define MMIO_BUDDY_MAX_EXP PAGE_1G_SHIFT
+#define MMIO_BUDDY_MIN_EXP PAGE_4K_SHIFT
+#define MMIO_BUDDY_REGION_COUNT (MMIO_BUDDY_MAX_EXP - MMIO_BUDDY_MIN_EXP + 1)
+
+/**
+ * @brief mmio伙伴系统内部的地址区域结构体
+ *
+ */
+struct __mmio_buddy_addr_region
+{
+    struct List list;
+    uint64_t vaddr; // 该内存对象起始位置的虚拟地址
+};
+
+/**
+ * @brief 空闲页数组结构体
+ *
+ */
+struct __mmio_free_region_list
+{
+    struct List list_head;
+    int64_t num_free; // 空闲页的数量
+};
+
+/**
+ * @brief buddy内存池
+ *
+ */
+struct mmio_buddy_mem_pool
+{
+    uint64_t pool_start_addr; // 内存池的起始地址
+    uint64_t pool_size;       // 内存池的内存空间总大小
+    spinlock_t op_lock;       // 操作锁
+    /**
+     * @brief 空闲地址区域链表
+     * 数组的第i个元素代表大小为2^(i+12)的内存区域
+     */
+    struct __mmio_free_region_list free_regions[MMIO_BUDDY_REGION_COUNT];
+};
+
+/**
+ * @brief 初始化mmio的伙伴系统
+ *
+ */
+void mmio_buddy_init();

+ 34 - 0
kernel/mm/mmio.c

@@ -0,0 +1,34 @@
+#include "mmio.h"
+#include "mmio-buddy.h"
+
+void mmio_init()
+{
+    mmio_buddy_init();
+}
+
+/**
+ * @brief 创建一块mmio区域,并将vma绑定到initial_mm
+ *
+ * @param size mmio区域的大小(字节)
+ * @param length mmio区域长度
+ * @param vm_flags 要把vma设置成的标志
+ * @param res_vaddr 返回值-分配得到的虚拟地址
+ * @param res_length 返回值-分配的虚拟地址空间长度
+ * @return int 错误码
+ */
+int mmio_create(uint32_t size, uint64_t length, vm_flags_t vm_flags, uint64_t * res_vaddr, uint64_t *res_length)
+{
+    
+}
+
+/**
+ * @brief 取消mmio的映射并将地址空间归还到buddy中
+ * 
+ * @param vaddr 起始的虚拟地址
+ * @param length 要归还的地址空间的长度
+ * @return int 错误码
+ */
+int mmio_release(uint64_t vaddr, uint64_t length)
+{
+
+}

+ 25 - 0
kernel/mm/mmio.h

@@ -0,0 +1,25 @@
+#pragma once
+#include "mm.h"
+
+void mmio_init();
+
+/**
+ * @brief 创建一块mmio区域,并将vma绑定到initial_mm
+ *
+ * @param size mmio区域的大小(字节)
+ * @param length mmio区域长度
+ * @param vm_flags 要把vma设置成的标志
+ * @param res_vaddr 返回值-分配得到的虚拟地址
+ * @param res_length 返回值-分配的虚拟地址空间长度
+ * @return int 错误码
+ */
+int mmio_create(uint32_t size, uint64_t length, vm_flags_t vm_flags, uint64_t * res_vaddr, uint64_t *res_length);
+
+/**
+ * @brief 取消mmio的映射并将地址空间归还到buddy中
+ * 
+ * @param vaddr 起始的虚拟地址
+ * @param length 要归还的地址空间的长度
+ * @return int 错误码
+ */
+int mmio_release(uint64_t vaddr, uint64_t length);

+ 1 - 2
kernel/process/process.c

@@ -32,7 +32,7 @@ extern void system_call(void);
 extern void kernel_thread_func(void);
 
 ul _stack_start; // initial proc的栈基地址(虚拟地址)
-struct mm_struct initial_mm = {0};
+extern struct mm_struct initial_mm;
 struct thread_struct initial_thread =
     {
         .rbp = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)),
@@ -458,7 +458,6 @@ exec_failed:;
 ul initial_kernel_thread(ul arg)
 {
     // kinfo("initial proc running...\targ:%#018lx", arg);
-
     fat32_init();
     usb_init();