From 77633e2f19c5d3c01fe3cf35c2b47ed1abf9fd1a Mon Sep 17 00:00:00 2001 From: fslongjin Date: Thu, 18 Aug 2022 22:06:29 +0800 Subject: [PATCH] =?UTF-8?q?new:=20=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E4=BA=86mmio=E7=9A=84=E4=BC=99=E4=BC=B4=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 +- kernel/mm/Makefile | 10 +- kernel/mm/mm.c | 1 + kernel/mm/mm.h | 2 + kernel/mm/mmio-buddy.c | 260 +++++++++++++++++++++++++++++++++++++++ kernel/mm/mmio-buddy.h | 51 ++++++++ kernel/mm/mmio.c | 34 +++++ kernel/mm/mmio.h | 25 ++++ kernel/process/process.c | 3 +- 9 files changed, 384 insertions(+), 5 deletions(-) create mode 100644 kernel/mm/mmio-buddy.c create mode 100644 kernel/mm/mmio-buddy.h create mode 100644 kernel/mm/mmio.c create mode 100644 kernel/mm/mmio.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 3ca7c3d3..eb07d92f 100644 --- a/.vscode/settings.json +++ b/.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": "" diff --git a/kernel/mm/Makefile b/kernel/mm/Makefile index cb9b69ea..2761d842 100644 --- a/kernel/mm/Makefile +++ b/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 \ No newline at end of file + 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 \ No newline at end of file diff --git a/kernel/mm/mm.c b/kernel/mm/mm.c index 689b3c37..298cf8c1 100644 --- a/kernel/mm/mm.c +++ b/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}; diff --git a/kernel/mm/mm.h b/kernel/mm/mm.h index 0a74fd60..9656fa5f 100644 --- a/kernel/mm/mm.h +++ b/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 diff --git a/kernel/mm/mmio-buddy.c b/kernel/mm/mmio-buddy.c new file mode 100644 index 00000000..7c8a08bf --- /dev/null +++ b/kernel/mm/mmio-buddy.c @@ -0,0 +1,260 @@ +#include "mmio-buddy.h" +#include + +/** + * @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(®ion->list); + list_append(&lst->list_head, ®ion->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(®ion->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); + +} \ No newline at end of file diff --git a/kernel/mm/mmio-buddy.h b/kernel/mm/mmio-buddy.h new file mode 100644 index 00000000..af7d675e --- /dev/null +++ b/kernel/mm/mmio-buddy.h @@ -0,0 +1,51 @@ +#pragma once +#include +#include +#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(); \ No newline at end of file diff --git a/kernel/mm/mmio.c b/kernel/mm/mmio.c new file mode 100644 index 00000000..a84a26c9 --- /dev/null +++ b/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) +{ + +} \ No newline at end of file diff --git a/kernel/mm/mmio.h b/kernel/mm/mmio.h new file mode 100644 index 00000000..bf9b159d --- /dev/null +++ b/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); \ No newline at end of file diff --git a/kernel/process/process.c b/kernel/process/process.c index f860d81d..a216ca27 100644 --- a/kernel/process/process.c +++ b/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();