diff --git a/.vscode/settings.json b/.vscode/settings.json index 28d66802..4e904bad 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -174,7 +174,8 @@ "sleep.h": "c", "net.h": "c", "lz4.h": "c", - "cmd_test.h": "c" + "cmd_test.h": "c", + "cmpxchg.h": "c" }, "C_Cpp.errorSquiggles": "enabled", "esbonio.sphinx.confDir": "", diff --git a/Makefile b/Makefile index 4cbcaaef..876a19c1 100644 --- a/Makefile +++ b/Makefile @@ -61,14 +61,24 @@ clean: cd .. ;\ done +.PHONY: ECHO +ECHO: + @echo "$@" + cppcheck-xml: cppcheck kernel user --platform=unix64 --std=c11 -I user/libs/ -I=kernel/ --force -j $(NPROCS) --xml 2> cppcheck.xml cppcheck: cppcheck kernel user --platform=unix64 --std=c11 -I user/libs/ -I=kernel/ --force -j $(NPROCS) +docs: ECHO + bash -c "cd docs && make html && cd .." + +clean-docs: + bash -c "cd docs && make clean && cd .." + gdb: - gdb -n -x tools/.gdbinit + rust-gdb -n -x tools/.gdbinit # 写入磁盘镜像 write_diskimage: diff --git a/README.md b/README.md index 84961f3c..9c881711 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # DragonOS +## 打造完全自主可控的数字化未来! + +--- + **Languages** 中文|[English](README_EN.md)   @@ -18,6 +22,8 @@ - 项目文档 **[docs.DragonOS.org](https://docs.dragonos.org)** +- **了解开发动态、开发任务,请访问DragonOS的zulip社群**: [https://DragonOS.zulipchat.com](https://DragonOS.zulipchat.com) + - 开源论坛 **[bbs.DragonOS.org](https://bbs.dragonos.org)** - 软件镜像站 **[mirrors.DragonOS.org](https://mirrors.DragonOS.org)** @@ -42,9 +48,9 @@ ## 如何加入? -  如果你愿意加入我们,你可以查看GitHub仓库的Project面板,选择近期已规划的功能,对他们进行完善。 +  如果你愿意加入我们,你可以访问DragonOS的zulip社群,了解开发动态、开发任务: [https://DragonOS.zulipchat.com](https://DragonOS.zulipchat.com) -  或者,你也可以带着你的创意与想法,和社区的小伙伴一起讨论,为DragonOS创造一些新的功能。 +  你也可以带着你的创意与想法,和社区的小伙伴一起讨论,为DragonOS创造一些新的功能。 ## 如何与社区建立联系? @@ -52,9 +58,8 @@   或者是加入我们的开发交流QQ群:**115763565** -  对于正式问题的讨论,我们建议在论坛[bbs.DragonOS.org](https://bbs.dragonos.org/)上的对应板块,使用正式的语言发帖讨论。亦或者是在本仓库的issue下提出问题。 +  对于正式问题的讨论,我们建议在 **[DragonOS的zulip社群](https://DragonOS.zulipchat.com)** 上的对应板块,使用正式的语言发帖讨论。亦或者是在本仓库的issue下提出问题。 -  在发帖的同时,可以把帖子转发到交流QQ群,这样能使得问题的交流更加高效,也便于问题的归档。 ## 贡献者名单 @@ -68,7 +73,7 @@ ## 赞助 -  DragonOS是一个公益性质的开源项目,但是它的发展离不开资金的支持,如果您愿意的话,可以通过 ** [赞助 - DragonOS](https://dragonos.org/?page_id=37) ** ,从而促进这个项目的发展。所有的赞助者的名单都会被公示。您的每一分赞助,都会为DragonOS的发展作出贡献! +  DragonOS是一个公益性质的开源项目,但是它的发展离不开资金的支持,如果您愿意的话,可以通过 **[赞助 - DragonOS](https://dragonos.org/?page_id=37)** ,从而促进这个项目的发展。所有的赞助者的名单都会被公示。您的每一分赞助,都会为DragonOS的发展作出贡献! ### 赞助的资金都会被用到哪里? diff --git a/README_EN.md b/README_EN.md index dafd3ed1..c4ab5b76 100644 --- a/README_EN.md +++ b/README_EN.md @@ -12,6 +12,7 @@ - Home Page **[DragonOS.org](https://dragonos.org)** - Documentation **[docs.DragonOS.org](https://docs.dragonos.org)** +- **To learn about development dynamics and development tasks, please visit DragonOS's zulip community:** [https://DragonOS.zulipchat.com](https://DragonOS.zulipchat.com) - BBS **[bbs.DragonOS.org](https://bbs.dragonos.org)** - Software mirror website **[mirrors.DragonOS.org](https://mirrors.DragonOS.org)** - QQ group **115763565** @@ -31,7 +32,7 @@ ## How to join DragonOS ? -  If you are willing to join us, you can check the project panel of the GitHub repo, select the recently planned functions, and improve them. +  If you are willing to join us, you can visit DragonOS's zulip community, learn about development dynamics and development tasks: [https://DragonOS.zulipchat.com](https://DragonOS.zulipchat.com)   Or, you can also bring your ideas, discuss with community members, and create some new functions for DragonOS. @@ -41,9 +42,8 @@   Or join our development exchange QQ group: **115763565** -  For the discussion of formal issues, we suggest that they be discussed in the forum [BBS.Dragonos.org](https://bbs.dragonos.org/) In the corresponding section of the, use formal language to post for discussion. Or ask questions under the issue of the warehouse. +  For the discussion of formal issues, we recommend that you use the official language to post on the corresponding section of **[DragonOS's zulip community](https://DragonOS.zulipchat.com)**. Or you can post questions under the issue of this repository. -  While posting, you can forward the post to the communication QQ group, which can make the communication of problems more efficient and facilitate the archiving of problems. ## List of contributors @@ -58,7 +58,7 @@ Maintainer longjin's Email:longjin@DragonOS.org ## Reward -  DragonOS is an open source public welfare project, but its development cannot be separated from the support of funds. If you want, you can visit ** [Sponsor - DragonOS](https://dragonos.org/?page_id=37) ** , so as to promote the development of this project. The list of all sponsors will be published. Every bit of your sponsorship will contribute to the development of DragonOS! +  DragonOS is an open source public welfare project, but its development cannot be separated from the support of funds. If you want, you can visit **[Sponsor - DragonOS](https://dragonos.org/?page_id=37)** , so as to promote the development of this project. The list of all sponsors will be published. Every bit of your sponsorship will contribute to the development of DragonOS! ### Where will the sponsorship funds be used? diff --git a/docs/Multiboot2 Specification version 2.0.pdf b/docs/Multiboot2 Specification version 2.0.pdf deleted file mode 100644 index 97baac9f..00000000 Binary files a/docs/Multiboot2 Specification version 2.0.pdf and /dev/null differ diff --git a/docs/community/contact/index.rst b/docs/community/contact/index.rst index 7ddb6480..2abe7b25 100644 --- a/docs/community/contact/index.rst +++ b/docs/community/contact/index.rst @@ -3,12 +3,50 @@ 与社区建立联系 ==================================== +联系方式 +------------------------- + 社区公共邮箱:contact@DragonOS.org -DragonOS负责人: longjin +DragonOS社区负责人: longjin 工作邮箱: longjin@DragonOS.org 开发交流QQ群: 115763565 DragonOS官网: https://DragonOS.org + +了解开发动态、开发任务,请访问DragonOS的zulip社群: https://DragonOS.zulipchat.com + + +赞助及捐赠 +------------------------- + +DragonOS是一个开源项目,我们欢迎任何形式的赞助和捐赠,您的捐赠将用于DragonOS的开发和维护,以及社区的运营。 + +您可以通过以下方式赞助和捐赠: + +- 访问DragonOS官网 https://DragonOS.org ,点击页面右上角的“赞助”按钮,进行捐赠 +- 联系社区负责人,沟通具体的赞助方式等。 + +财务及捐赠信息公开 +------------------------- + +DragonOS社区的捐赠信息将按年进行公开。赞助商、赞助者信息将在收到赞助后,15天内进行公开。 + +社区管理、财务及法务主体 +------------------------- + +DragonOS社区的管理、财务及法务主体为:灵高计算机系统(广州)有限公司。 + +我们是一家开源公司,我们坚信,开源能为我国将来的IT,打下更好的基础。我们也通过其他业务创收,投入到DragonOS的研发之中。 + +公司负责DragonOS社区的运营、财务、法务事项处理工作。 + +地址:广东省广州市番禺区小谷围街广州大学城华南理工大学大学城校区 + +邮件:contact@DragonOS.org + +官网:https://ringotek.com.cn + + diff --git a/docs/introduction/build_system.md b/docs/introduction/build_system.md index 61019acc..38e91cd5 100644 --- a/docs/introduction/build_system.md +++ b/docs/introduction/build_system.md @@ -233,7 +233,10 @@ make run-docker - Docker编译,并写入磁盘镜像,: `make docker` - Docker编译,写入磁盘镜像,并在QEMU中运行: `make run-docker` - 不编译,直接从已有的磁盘镜像启动: `make qemu` +- 清理编译产生的文件: `make clean` +- 编译文档: `make docs` (需要手动安装sphinx以及docs下的`requirements.txt`中的依赖) +- 清理文档: `make clean-docs` :::{note} 如果您需要在vnc中运行DragonOS,请在上述命令后加上`-vnc`后缀。如:`make run-vnc` -::: \ No newline at end of file +::: diff --git a/docs/introduction/features.md b/docs/introduction/features.md index e61ad879..77071106 100644 --- a/docs/introduction/features.md +++ b/docs/introduction/features.md @@ -12,10 +12,14 @@ ### 内存管理 -- [x] 页分配器 -- [x] slab分配器 +- [x] 页帧分配器 +- [x] 小对象分配器 - [x] VMA - [x] MMIO地址空间自动分配 +- [x] 页面映射器 +- [x] 硬件抽象层 +- [x] 独立的用户地址空间管理机制 +- [x] C接口兼容层 ### 多核 @@ -31,6 +35,7 @@ - [x] exec - [x] 进程睡眠(支持高精度睡眠) - [x] kthread机制 +- [x] 可扩展二进制加载器 #### 同步原语 @@ -43,7 +48,10 @@ ### 调度 - [x] CFS调度器 +- [x] 实时调度器(FIFO、RR) - [x] 单核调度 +- [x] 多核调度 +- [x] 负载均衡 ### IPC @@ -56,7 +64,8 @@ - [x] fat12/16/32 - [x] Devfs - [x] RamFS -- [x] procfs +- [x] Procfs +- [x] Sysfs ### 异常及中断处理 @@ -95,7 +104,7 @@ - [x] ACPI 高级电源配置模块 - [x] IDE硬盘 - [x] AHCI硬盘 -- [x] PCI +- [x] PCI、PCIe总线 - [x] XHCI(usb3.0) - [x] ps/2 键盘 - [x] ps/2 鼠标 @@ -107,6 +116,8 @@ - [x] VirtIO网卡 - [x] x87FPU - [x] TTY终端 +- [x] 浮点处理器 + ## 用户层 @@ -121,9 +132,9 @@ - [x] 基于字符串匹配的解析 - [x] 基本的几个命令 -### 驱动程序 +### Http Server -- [x] ps/2键盘用户态驱动 +- 使用C编写的简单的Http Server,能够运行静态网站。 ## 软件移植 @@ -132,3 +143,4 @@ - [x] gmp 6.2.1 [https://github.com/DragonOS-Community/gmp-6.2.1](https://github.com/DragonOS-Community/gmp-6.2.1) - [x] mpfr 4.1.1 [https://github.com/DragonOS-Community/mpfr](https://github.com/DragonOS-Community/mpfr) - [x] mpc 1.2.1 [https://github.com/DragonOS-Community/mpc](https://github.com/DragonOS-Community/mpc) +- [x] relibc [https://github.com/DragonOS-Community/relibc](https://github.com/DragonOS-Community/relibc) diff --git a/docs/introduction/index.rst b/docs/introduction/index.rst index 763b97d4..1b32565f 100644 --- a/docs/introduction/index.rst +++ b/docs/introduction/index.rst @@ -3,9 +3,11 @@ DragonOS简介 DragonOS龙操作系统(以下简称“DragonOS”)是一个面向服务器领域的,从0开发内核及用户态环境,并提供Linux兼容性的64位操作系统。它使用Rust与C语言进行编写,并正在逐步淘汰原有的C代码,以在将来提供更好的安全性与可靠性。 +**我们致力于打造完全自主可控的数字化未来!** + DragonOS的目标是,构建一个完全独立自主的、开源的、高性能及高可靠性的服务器操作系统,为国家数字基础设施建设提供完全独立自主的底层核心动力。 - 作为一个社区驱动的开源操作系统,为了促进其发展,避免让其遭受一些不遵守开源协议的商业公司的侵权,我们决定使用GPLv2协议开放源代码,以严格的开源协议来保护DragonOS。 + 作为一个社区驱动的开源操作系统,为了促进开源社区建设,并避免让其遭受一些不遵守开源协议的商业公司的侵权,我们决定使用GPLv2协议开放源代码,以严格的开源协议来保护DragonOS。 你可能对DragonOS中已经实现了哪些功能感兴趣,您可以转到这里::ref:`功能特性 <_genreal_features>` diff --git a/docs/kernel/core_api/allocate-memory.md b/docs/kernel/core_api/allocate-memory.md deleted file mode 100644 index 43874c04..00000000 --- a/docs/kernel/core_api/allocate-memory.md +++ /dev/null @@ -1,15 +0,0 @@ -# 内存分配指南 - -DragonOS提供了一些用于内存分配的api。您可以使用*kmalloc*来分配小的内存块,也可以使用*alloc_pages*分配连续的2MB大小的内存页面。 - -## 选择合适的内存分配器 - -在内核中,最直接、最简单的分配内存的方式就是,使用`kmalloc()`函数进行分配。并且,出于安全起见,除非内存在分配后一定会被覆盖,且您能确保内存中的脏数据一定不会对程序造成影响,在其余情况下,我们建议使用`kzalloc()`进行内存分配,它将会在`kmalloc()`的基础上,把申请到的内存进行清零。 - -您可以通过`kmalloc()`函数分配得到32bytes到1MBytes之间的内存对象。并且,这些内存对象具有以下的性质: - -- 内存起始地址及大小按照2次幂对齐。(比如,申请的是80bytes的内存空间,那么获得的内存对象大小为128bytes且内存地址按照128bytes对齐) - -对于需要大量连续内存的分配,可以使用`alloc_pages()`向页面分配器申请连续的内存页。 - -当内存空间不再被使用时,那么必须释放他们。若您使用的是`kmalloc()`分配的内存,那么您需要使用`kfree()`释放它。若是使用`alloc_pages()`分配的内存,则需要使用`free_pages()`来释放它们。 diff --git a/docs/kernel/core_api/index.rst b/docs/kernel/core_api/index.rst index 8152fcbb..92899c3c 100644 --- a/docs/kernel/core_api/index.rst +++ b/docs/kernel/core_api/index.rst @@ -13,16 +13,3 @@ data_structures casting softirq - -内存管理 -=================== - - 这里快速讲解了如何在DragonOS中分配、使用内存。以便您能快速的了解这个模块。 - - 详细的内存管理模块的文档请参见::ref:`memory_management_module` - -.. toctree:: - :maxdepth: 1 - - allocate-memory - mm-api \ No newline at end of file diff --git a/docs/kernel/core_api/mm-api.md b/docs/kernel/core_api/mm-api.md deleted file mode 100644 index a05276c1..00000000 --- a/docs/kernel/core_api/mm-api.md +++ /dev/null @@ -1,267 +0,0 @@ -(_core_mm_api)= - -# 内存管理API - -## SLAB内存池 - -SLAB内存池提供小内存对象的分配功能。 - -### `void *kmalloc(unsigned long size, gfp_t gfp)` - -  获取小块的内存。 - -#### 描述 - -  kmalloc用于获取那些小于2M内存页大小的内存对象。可分配的内存对象大小为32bytes~1MBytes. 且分配的内存块大小、起始地址按照2的n次幂进行对齐。(比如,申请的是80bytes的内存空间,那么获得的内存对象大小为128bytes且内存地址按照128bytes对齐) - -##### 参数 - -**size** - -  内存对象的大小 - -**gfp** - -  标志位 - -### `void *kzalloc(unsigned long size, gfp_t gfp)` - -#### 描述 - -  获取小块的内存,并将其清零。其余功能与kmalloc相同。 - - -##### 参数 - -**size** - -  内存对象的大小 - -**gfp** - -  标志位 - -### `unsigned long kfree(void *address)` - -  释放从slab分配的内存。 - -#### 描述 - -  该函数用于释放通过kmalloc申请的内存。如果`address`为NULL,则函数被调用后,无事发生。 - -  请不要通过这个函数释放那些不是从`kmalloc()`或`kzalloc()`申请的内存,否则将会导致系统崩溃。 - -##### 参数 - -**address** - -  指向内存对象的起始地址的指针 - -## 物理页管理 - -DragonOS支持对物理页的直接操作 - -### `struct Page *alloc_pages(unsigned int zone_select, int num, ul flags)` - -#### 描述 - -  从物理页管理单元中申请一段连续的物理页 - -#### 参数 - -**zone_select** - -  要申请的物理页所位于的内存区域 - -可选值: - -- `ZONE_DMA` DMA映射专用区域 -- `ZONE_NORMAL` 正常的物理内存区域,已在页表高地址处映射 -- `ZONE_UNMAPPED_IN_PGT` 尚未在页表中映射的区域 - -**num** - -  要申请的连续物理页的数目,该值应当小于64 - -**flags** - -  分配的页面要被设置成的属性 - -可选值: - -- `PAGE_PGT_MAPPED` 页面在页表中已被映射 -- `PAGE_KERNEL_INIT` 内核初始化所占用的页 -- `PAGE_DEVICE` 设备MMIO映射的内存 -- `PAGE_KERNEL` 内核层页 -- `PAGE_SHARED` 共享页 - -#### 返回值 - -##### 成功 - -  成功申请则返回指向起始页面的Page结构体的指针 - -##### 失败 - -  当ZONE错误或内存不足时,返回`NULL` - -### `void free_pages(struct Page *page, int number)` - -#### 描述 - -  从物理页管理单元中释放一段连续的物理页。 - -#### 参数 - -**page** - -  要释放的第一个物理页的Page结构体 - -**number** - -  要释放的连续内存页的数量。该值应小于64 - -## 页表管理 - -### `int mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool use4k)` - -#### 描述 - -  将一段物理地址映射到当前页表的指定虚拟地址处 - -#### 参数 - -**virt_addr_start** - -  虚拟地址的起始地址 - -**phys_addr_start** - -  物理地址的起始地址 - -**length** - -  要映射的地址空间的长度 - -**flags** - -  页表项的属性 - -**use4k** - -  使用4级页表,将地址区域映射为若干4K页 - -### `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)` - -#### 描述 - -  将一段物理地址映射到指定页表的指定虚拟地址处 - -#### 参数 - -**proc_page_table_addr** - -  指定的顶层页表的起始地址 - -**is_phys** - -  该顶层页表地址是否为物理地址 - -**virt_addr_start** - -  虚拟地址的起始地址 - -**phys_addr_start** - -  物理地址的起始地址 - -**length** - -  要映射的地址空间的长度 - -**flags** - -  页表项的属性 - -**user** - -  页面是否为用户态可访问 - -**flush** - -  完成映射后,是否刷新TLB - -**use4k** - -  使用4级页表,将地址区域映射为若干4K页 - -#### 返回值 - -- 映射成功:0 -- 映射失败:-EFAULT - -### `void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul length)` - -#### 描述 - -  取消给定页表中的指定地址空间的页表项映射。 - -#### 参数 - -**proc_page_table_addr** - -  指定的顶层页表的基地址 - -**is_phys** - -  该顶层页表地址是否为物理地址 - -**virt_addr_start** - -  虚拟地址的起始地址 - -**length** - -  要取消映射的地址空间的长度 - -### `mm_unmap_addr(virt_addr, length)` - -#### 描述 - -  该宏定义用于取消当前进程的页表中的指定地址空间的页表项映射。 - -#### 参数 - -**virt_addr** - -  虚拟地址的起始地址 - -**length** - -  要取消映射的地址空间的长度 - -## 内存信息获取 - -### `struct mm_stat_t mm_stat()` - -#### 描述 - -  获取计算机目前的内存空间使用情况 - -#### 参数 - -无 - -#### 返回值 - -  返回值是一个`mm_mstat_t`结构体,该结构体定义于`mm/mm.h`中。其中包含了以下信息(单位均为字节): - -| 参数名 | 解释 | -| ---------- | ----------------------- | -| total | 计算机的总内存数量大小 | -| used | 已使用的内存大小 | -| free | 空闲物理页所占的内存大小 | -| shared | 共享的内存大小 | -| cache_used | 位于slab缓冲区中的已使用的内存大小 | -| cache_free | 位于slab缓冲区中的空闲的内存大小 | -| available | 系统总空闲内存大小(包括kmalloc缓冲区) | \ No newline at end of file diff --git a/docs/kernel/memory_management/allocate-memory.md b/docs/kernel/memory_management/allocate-memory.md new file mode 100644 index 00000000..b4ce2b5c --- /dev/null +++ b/docs/kernel/memory_management/allocate-memory.md @@ -0,0 +1,29 @@ +# 内存分配指南 + +  本文将讲述如何在内核中进行内存分配。在开始之前,请您先了解一个基本点:DragonOS的内核使用4KB的页来管理内存,并且具有伙伴分配器和slab分配器。并且对用户空间、内核空间均具有特定的管理机制。 + +## 1. 安全的分配内存 + +  在默认情况下,KernelAllocator被绑定为全局内存分配器,它会根据请求分配的内存大小,自动选择使用slab还是伙伴分配器。因此,在内核中,使用Rust原生的 +内存分配函数,或者是创建一个`Box`对象等等,都是安全的。 + + +## 2. 手动管理页帧 + +:::{warning} +**请格外小心!** 手动管理页帧脱离了Rust的内存安全机制,因此可能会造成内存泄漏或者是内存错误。 +::: + +  在某些情况下,我们需要手动分配页帧。例如,我们需要在内核中创建一个新的页表,或者是在内核中创建一个新的地址空间。这时候,我们需要手动分配页帧。使用`LockedFrameAllocator`的`allocate()`函数,能够分配在物理地址上连续的页帧。请注意,由于底层使用的是buddy分配器,因此页帧数目必须是2的n次幂,且最大大小不超过1GB。 + +  当需要释放页帧的时候,使用`LockedFrameAllocator`的`deallocate()`函数,或者是`deallocate_page_frames()`函数,能够释放在物理地址上连续的页帧。 + +  当您需要映射页帧的时候,可使用`KernelMapper::lock()`函数,获得一个内核映射器对象,然后进行映射。由于KernelMapper是对PageMapper的封装,因此您在获取KernelMapper之后,可以使用PageMapper相关接口对内核空间的映射进行管理。 + +:::{warning} +**千万不要** 使用KernelMapper去映射用户地址空间的内存,这会使得这部分内存脱离用户地址空间的管理,从而导致内存错误。 +::: + +## 3. 为用户程序分配内存 + +  在内核中,您可以使用用户地址空间结构体(`AddressSpace`)的`mmap()`,`map_anonymous()`等函数,为用户程序分配内存。这些函数会自动将用户程序的内存映射到用户地址空间中,并且会自动创建VMA结构体。您可以使用`AddressSpace`的`munmap()`函数,将用户程序的内存从用户地址空间中解除映射,并且销毁VMA结构体。调整权限等操作可以使用`AddressSpace`的`mprotect()`函数。 diff --git a/docs/kernel/memory_management/index.rst b/docs/kernel/memory_management/index.rst index 421ecc00..16cb74db 100644 --- a/docs/kernel/memory_management/index.rst +++ b/docs/kernel/memory_management/index.rst @@ -1,14 +1,14 @@ .. _memory_management_module: ==================================== -内存管理文档 +内存管理 ==================================== - 这里讲解了内存管理模块的一些设计及实现原理。 - - 如果你正在寻找使用内存管理模块的方法,请转到::ref:`内存管理API文档 <_core_mm_api>` + 这里讲解了内存管理模块的一些设计及实现原理,以及相应的接口。 .. toctree:: :maxdepth: 1 + intro + allocate-memory mmio \ No newline at end of file diff --git a/docs/kernel/memory_management/intro.md b/docs/kernel/memory_management/intro.md new file mode 100644 index 00000000..b459652d --- /dev/null +++ b/docs/kernel/memory_management/intro.md @@ -0,0 +1,20 @@ +# 内存管理模块简介 + +## 1. 概述 + +  DragonOS实现了具有优秀架构设计的内存管理模块,对内核空间和用户空间的内存映射、分配、释放、管理等操作进行了封装,使得内核开发者可以更加方便地进行内存管理。 + +  DragonOS的内存管理模块主要由以下类型的组件组成: + +- **硬件抽象层(MemoryManagementArch)** - 提供对具体处理器架构的抽象,使得内存管理模块可以在不同的处理器架构上运行 +- **页面映射器(PageMapper)**- 提供对虚拟地址和物理地址的映射,以及页表的创建、填写、销毁、权限管理等操作。分为两种类型:内核页表映射器(KernelMapper)和用户页表映射器(位于具体的用户地址空间结构中) +- **页面刷新器(PageFlusher)** - 提供对页表的刷新操作(整表刷新、单页刷新、跨核心刷新) +- **页帧分配器(FrameAllocator)** - 提供对页帧的分配、释放、管理等操作。具体来说,包括BumpAllocator、BuddyAllocator +- **小对象分配器** - 提供对小内存对象的分配、释放、管理等操作。指的是内核里面的SlabAllocator (SlabAllocator的实现目前还没有完成) +- **MMIO空间管理器** - 提供对MMIO地址空间的分配、管理操作。(目前这个模块待进一步重构) +- **用户地址空间管理机制** - 提供对用户地址空间的管理。 + - VMA机制 - 提供对用户地址空间的管理,包括VMA的创建、销毁、权限管理等操作 + - 用户映射管理 - 与VMA机制共同作用,管理用户地址空间的映射 +- **系统调用层** - 提供对用户空间的内存管理系统调用,包括mmap、munmap、mprotect、mremap等 +- **C接口兼容层** - 提供对原有的C代码的接口,使得C代码能够正常运行。 + diff --git a/docs/kernel/process_management/index.rst b/docs/kernel/process_management/index.rst index 747f1aee..c25eb3ef 100644 --- a/docs/kernel/process_management/index.rst +++ b/docs/kernel/process_management/index.rst @@ -5,4 +5,5 @@ :maxdepth: 1 kthread - pcb \ No newline at end of file + pcb + load_binary diff --git a/docs/kernel/process_management/load_binary.md b/docs/kernel/process_management/load_binary.md new file mode 100644 index 00000000..27b68ba6 --- /dev/null +++ b/docs/kernel/process_management/load_binary.md @@ -0,0 +1,15 @@ +# 加载程序 + +## 1. 二进制程序装载 + +  在小节,你将了解DragonOS的二进制程序加载器的原理。 + +  DragonOS在装载二进制程序时,执行了“探测-装载”的过程。 + +  在探测阶段,DragonOS会读取文件首部,然后依次调用各个二进制加载器的探测函数,判断该二进制程序是否适用于该加载器。如果适用,则使用这个加载器进行装载。 + +  在装载阶段,DragonOS会使用上述加载器进行装载。装载器会将二进制程序的各个段映射到内存中,并且得到二进制程序的入口地址。 + +:::{note} +目前DragonOS不支持动态链接,因此所有的二进制程序都是静态链接的。并且暂时支持的只有ELF加载器。 +::: diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 361204ee..99ea9e0a 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -22,6 +22,9 @@ smoltcp = { git = "https://github.com/DragonOS-Community/smoltcp.git", rev = "90 num-traits = { git = "https://github.com/DragonOS-Community/num-traits.git", rev="1597c1c", default-features = false } num = { version = "0.4.0", default-features = false } num-derive = "0.3" +# 一个no_std的hashmap、hashset +hashbrown = "0.13.2" +elf = { version = "0.7.2", default-features = false } # 构建时依赖项 [build-dependencies] @@ -34,4 +37,8 @@ version = "1.4.0" features = ["spin_no_std"] +# The release profile, used for `cargo build --release` +[profile.release] +debug = false + diff --git a/kernel/src/Makefile b/kernel/src/Makefile index ef693996..7500550a 100644 --- a/kernel/src/Makefile +++ b/kernel/src/Makefile @@ -17,7 +17,7 @@ export ASFLAGS := --64 LD_LIST := head.o -kernel_subdirs := common driver process debug arch exception mm smp sched syscall ktest libs ipc io time +kernel_subdirs := common driver process debug arch exception smp sched syscall ktest libs ipc io time @@ -58,7 +58,8 @@ all: kernel $(LD) -b elf64-x86-64 -z muldefs -o kernel head.o main.o $(shell find . -name "*.o") ../target/x86_64-unknown-none/release/libdragonos_kernel.a ./debug/kallsyms.o -T link.lds @echo "Generating kernel ELF file..." # 生成内核文件 - $(OBJCOPY) -I elf64-x86-64 -O elf64-x86-64 -R ".comment" -R ".eh_frame" kernel ../../bin/kernel/kernel.elf + $(OBJCOPY) -I elf64-x86-64 -O elf64-x86-64 kernel ../../bin/kernel/kernel.elf +# $(OBJCOPY) -I elf64-x86-64 -O elf64-x86-64 -R ".comment" -R ".eh_frame" kernel ../../bin/kernel/kernel.elf @echo "Kernel Build Done." ECHO: diff --git a/kernel/src/arch/mod.rs b/kernel/src/arch/mod.rs index aa6b0495..fd04262c 100644 --- a/kernel/src/arch/mod.rs +++ b/kernel/src/arch/mod.rs @@ -1,9 +1,9 @@ pub mod x86_64; #[cfg(target_arch = "x86_64")] -pub use self::x86_64::pci::pci::X86_64PciArch as PciArch; -#[cfg(target_arch = "x86_64")] pub use self::x86_64::*; //公开x86_64架构下的函数,使外界接口统一 + use crate::driver::pci::pci::{BusDeviceFunction, PciAddr, PciError, PciRoot, SegmentGroupNumber}; + /// TraitPciArch Pci架构相关函数,任何架构都应独立实现trait里的函数 pub trait TraitPciArch { /// @brief 读取寄存器值,x86_64架构通过读取两个特定io端口实现 diff --git a/kernel/src/arch/x86_64/asm/irqflags.rs b/kernel/src/arch/x86_64/asm/irqflags.rs index 667c8c8c..c9bb481c 100644 --- a/kernel/src/arch/x86_64/asm/irqflags.rs +++ b/kernel/src/arch/x86_64/asm/irqflags.rs @@ -3,8 +3,9 @@ use core::arch::asm; #[inline] pub fn local_irq_save() -> usize { let x: usize; + // x86_64::registers::rflags:: unsafe { - asm!("pushfq ; pop {} ; cli", out(reg) x, options(nostack)); + asm!("pushfq; pop {}; cli", out(reg) x, options(nomem, preserves_flags)); } x } @@ -13,6 +14,6 @@ pub fn local_irq_save() -> usize { // 恢复先前保存的rflags的值x pub fn local_irq_restore(x: usize) { unsafe { - asm!("push {} ; popfq", in(reg) x, options(nostack)); + asm!("push {}; popfq", in(reg) x, options(nomem, preserves_flags)); } } diff --git a/kernel/src/arch/x86_64/context.rs b/kernel/src/arch/x86_64/context.rs index 2c1a9576..b52a0b9b 100644 --- a/kernel/src/arch/x86_64/context.rs +++ b/kernel/src/arch/x86_64/context.rs @@ -16,7 +16,15 @@ pub fn switch_process( fp_state_save(prev); fp_state_restore(next); compiler_fence(core::sync::atomic::Ordering::SeqCst); + let new_address_space = next.address_space().unwrap_or_else(|| { + panic!( + "switch_process: next process:{} address space is null", + next.pid + ) + }); unsafe { + // 加载页表 + new_address_space.read().user_mapper.utable.make_current(); switch_proc(prev, next); } compiler_fence(core::sync::atomic::Ordering::SeqCst); diff --git a/kernel/src/arch/x86_64/fpu.rs b/kernel/src/arch/x86_64/fpu.rs index 32c31127..48e1eabe 100644 --- a/kernel/src/arch/x86_64/fpu.rs +++ b/kernel/src/arch/x86_64/fpu.rs @@ -79,7 +79,7 @@ impl FpState { /// @brief 从用户态进入内核时,保存浮点寄存器,并关闭浮点功能 pub fn fp_state_save(pcb: &mut process_control_block) { // 该过程中不允许中断 - let rflags = local_irq_save(); + let rflags: usize = local_irq_save(); let fp: &mut FpState = if pcb.fp_state == null_mut() { let f = Box::leak(Box::new(FpState::default())); diff --git a/kernel/src/arch/x86_64/interrupt/mod.rs b/kernel/src/arch/x86_64/interrupt/mod.rs index 6a12c1d0..c8292658 100644 --- a/kernel/src/arch/x86_64/interrupt/mod.rs +++ b/kernel/src/arch/x86_64/interrupt/mod.rs @@ -41,14 +41,14 @@ impl InterruptArch for X86_64InterruptArch { fn is_irq_enabled() -> bool { let rflags: u64; unsafe { - asm!("pushfq; pop {}", out(reg) rflags); + asm!("pushfq; pop {}", out(reg) rflags, options(nomem, preserves_flags)); } return rflags & (1 << 9) != 0; } unsafe fn save_and_disable_irq() -> IrqFlagsGuard { compiler_fence(Ordering::SeqCst); - let rflags = local_irq_save() as u64; + let rflags = local_irq_save(); let flags = IrqFlags::new(rflags); let guard = IrqFlagsGuard::new(flags); compiler_fence(Ordering::SeqCst); @@ -57,7 +57,7 @@ impl InterruptArch for X86_64InterruptArch { unsafe fn restore_irq(flags: IrqFlags) { compiler_fence(Ordering::SeqCst); - local_irq_restore(flags.flags() as usize); + local_irq_restore(flags.flags()); compiler_fence(Ordering::SeqCst); } } diff --git a/kernel/src/arch/x86_64/libs/mod.rs b/kernel/src/arch/x86_64/libs/mod.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/kernel/src/arch/x86_64/libs/mod.rs @@ -0,0 +1 @@ + diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index bda00b3f..be8e25ec 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -1,28 +1,627 @@ pub mod barrier; -use crate::include::bindings::bindings::process_control_block; + +use alloc::vec::Vec; +use hashbrown::HashSet; +use x86::time::rdtsc; +use x86_64::registers::model_specific::EferFlags; + +use crate::driver::uart::uart::c_uart_send_str; +use crate::include::bindings::bindings::{ + disable_textui, enable_textui, multiboot2_get_memory, multiboot2_iter, multiboot_mmap_entry_t, + video_reinitialize, +}; +use crate::libs::align::page_align_up; +use crate::libs::printk::PrintkWriter; +use crate::libs::spinlock::SpinLock; + +use crate::mm::allocator::page_frame::{FrameAllocator, PageFrameCount}; +use crate::mm::mmio_buddy::mmio_init; +use crate::{ + arch::MMArch, + mm::allocator::{buddy::BuddyAllocator, bump::BumpAllocator}, +}; + +use crate::mm::kernel_mapper::KernelMapper; +use crate::mm::page::{PageEntry, PageFlags}; +use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, PhysMemoryArea, VirtAddr}; +use crate::syscall::SystemError; +use crate::{kdebug, kinfo}; use core::arch::asm; -use core::ptr::read_volatile; +use core::ffi::c_void; +use core::fmt::{Debug, Write}; +use core::mem::{self}; -use self::barrier::mfence; +use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; -/// @brief 切换进程的页表 -/// -/// @param 下一个进程的pcb。将会把它的页表切换进来。 -/// -/// @return 下一个进程的pcb(把它return的目的主要是为了归还所有权) -#[inline(always)] -#[allow(dead_code)] -pub fn switch_mm( - next_pcb: &'static mut process_control_block, -) -> &'static mut process_control_block { - mfence(); - // kdebug!("to get pml4t"); - let pml4t = unsafe { read_volatile(&next_pcb.mm.as_ref().unwrap().pgd) }; +pub type PageMapper = + crate::mm::page::PageMapper; + +/// @brief 用于存储物理内存区域的数组 +static mut PHYS_MEMORY_AREAS: [PhysMemoryArea; 512] = [PhysMemoryArea { + base: PhysAddr::new(0), + size: 0, +}; 512]; + +/// 初始的CR3寄存器的值,用于内存管理初始化时,创建的第一个内核页表的位置 +static mut INITIAL_CR3_VALUE: PhysAddr = PhysAddr::new(0); + +/// 内核的第一个页表在pml4中的索引 +/// 顶级页表的[256, 512)项是内核的页表 +static KERNEL_PML4E_NO: usize = (X86_64MMArch::PHYS_OFFSET & ((1 << 48) - 1)) >> 39; + +static INNER_ALLOCATOR: SpinLock>> = SpinLock::new(None); + +#[derive(Clone, Copy)] +pub struct X86_64MMBootstrapInfo { + kernel_code_start: usize, + kernel_code_end: usize, + kernel_data_end: usize, + kernel_rodata_end: usize, + start_brk: usize, +} + +impl Debug for X86_64MMBootstrapInfo { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!( + f, + "kernel_code_start: {:x}, kernel_code_end: {:x}, kernel_data_end: {:x}, kernel_rodata_end: {:x}, start_brk: {:x}", + self.kernel_code_start, self.kernel_code_end, self.kernel_data_end, self.kernel_rodata_end, self.start_brk) + } +} + +pub static mut BOOTSTRAP_MM_INFO: Option = None; + +/// @brief X86_64的内存管理架构结构体 +#[derive(Debug, Clone, Copy, Hash)] +pub struct X86_64MMArch; + +/// XD标志位是否被保留 +static XD_RESERVED: AtomicBool = AtomicBool::new(false); + +impl MemoryManagementArch for X86_64MMArch { + /// 4K页 + const PAGE_SHIFT: usize = 12; + + /// 每个页表项占8字节,总共有512个页表项 + const PAGE_ENTRY_SHIFT: usize = 9; + + /// 四级页表(PML4T、PDPT、PDT、PT) + const PAGE_LEVELS: usize = 4; + + /// 页表项的有效位的index。在x86_64中,页表项的第[0, 47]位表示地址和flag, + /// 第[48, 51]位表示保留。因此,有效位的index为52。 + /// 请注意,第63位是XD位,表示是否允许执行。 + const ENTRY_ADDRESS_SHIFT: usize = 52; + + const ENTRY_FLAG_DEFAULT_PAGE: usize = Self::ENTRY_FLAG_PRESENT; + + const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT; + + const ENTRY_FLAG_PRESENT: usize = 1 << 0; + + const ENTRY_FLAG_READONLY: usize = 0; + + const ENTRY_FLAG_READWRITE: usize = 1 << 1; + + const ENTRY_FLAG_USER: usize = 1 << 2; + + const ENTRY_FLAG_WRITE_THROUGH: usize = 1 << 3; + + const ENTRY_FLAG_CACHE_DISABLE: usize = 1 << 4; + + const ENTRY_FLAG_NO_EXEC: usize = 1 << 63; + /// x86_64不存在EXEC标志位,只有NO_EXEC(XD)标志位 + const ENTRY_FLAG_EXEC: usize = 0; + + /// 物理地址与虚拟地址的偏移量 + /// 0xffff_8000_0000_0000 + const PHYS_OFFSET: usize = Self::PAGE_NEGATIVE_MASK + (Self::PAGE_ADDRESS_SIZE >> 1); + + const USER_END_VADDR: VirtAddr = VirtAddr::new(0x0000_7eff_ffff_ffff); + const USER_BRK_START: VirtAddr = VirtAddr::new(0x700000000000); + const USER_STACK_START: VirtAddr = VirtAddr::new(0x6ffff0a00000); + + /// @brief 获取物理内存区域 + unsafe fn init() -> &'static [crate::mm::PhysMemoryArea] { + extern "C" { + fn _text(); + fn _etext(); + fn _edata(); + fn _erodata(); + fn _end(); + } + + Self::init_xd_rsvd(); + + let bootstrap_info = X86_64MMBootstrapInfo { + kernel_code_start: _text as usize, + kernel_code_end: _etext as usize, + kernel_data_end: _edata as usize, + kernel_rodata_end: _erodata as usize, + start_brk: _end as usize, + }; + unsafe { + BOOTSTRAP_MM_INFO = Some(bootstrap_info); + } + + // 初始化物理内存区域(从multiboot2中获取) + let areas_count = + Self::init_memory_area_from_multiboot2().expect("init memory area failed"); + c_uart_send_str(0x3f8, "x86 64 init end\n\0".as_ptr()); + + return &PHYS_MEMORY_AREAS[0..areas_count]; + } + + /// @brief 刷新TLB中,关于指定虚拟地址的条目 + unsafe fn invalidate_page(address: VirtAddr) { + compiler_fence(Ordering::SeqCst); + asm!("invlpg [{0}]", in(reg) address.data(), options(nostack, preserves_flags)); + compiler_fence(Ordering::SeqCst); + } + + /// @brief 刷新TLB中,所有的条目 + unsafe fn invalidate_all() { + compiler_fence(Ordering::SeqCst); + // 通过设置cr3寄存器,来刷新整个TLB + Self::set_table(PageTableKind::User, Self::table(PageTableKind::User)); + compiler_fence(Ordering::SeqCst); + } + + /// @brief 获取顶级页表的物理地址 + unsafe fn table(_table_kind: PageTableKind) -> PhysAddr { + let paddr: usize; + compiler_fence(Ordering::SeqCst); + asm!("mov {}, cr3", out(reg) paddr, options(nomem, nostack, preserves_flags)); + compiler_fence(Ordering::SeqCst); + return PhysAddr::new(paddr); + } + + /// @brief 设置顶级页表的物理地址到处理器中 + unsafe fn set_table(_table_kind: PageTableKind, table: PhysAddr) { + compiler_fence(Ordering::SeqCst); + asm!("mov cr3, {}", in(reg) table.data(), options(nostack, preserves_flags)); + compiler_fence(Ordering::SeqCst); + } + + /// @brief 判断虚拟地址是否合法 + fn virt_is_valid(virt: VirtAddr) -> bool { + return virt.is_canonical(); + } + + /// 获取内存管理初始化时,创建的第一个内核页表的地址 + fn initial_page_table() -> PhysAddr { + unsafe { + return INITIAL_CR3_VALUE; + } + } + + /// @brief 创建新的顶层页表 + /// + /// 该函数会创建页表并复制内核的映射到新的页表中 + /// + /// @return 新的页表 + fn setup_new_usermapper() -> Result { + let new_umapper: crate::mm::page::PageMapper = unsafe { + PageMapper::create(PageTableKind::User, LockedFrameAllocator) + .ok_or(SystemError::ENOMEM)? + }; + + let current_ktable: KernelMapper = KernelMapper::lock(); + let copy_mapping = |pml4_entry_no| unsafe { + let entry: PageEntry = current_ktable + .table() + .entry(pml4_entry_no) + .unwrap_or_else(|| panic!("entry {} not found", pml4_entry_no)); + new_umapper.table().set_entry(pml4_entry_no, entry) + }; + + // 复制内核的映射 + for pml4_entry_no in KERNEL_PML4E_NO..512 { + copy_mapping(pml4_entry_no); + } + + return Ok(crate::mm::ucontext::UserMapper::new(new_umapper)); + } +} + +impl X86_64MMArch { + unsafe fn init_memory_area_from_multiboot2() -> Result { + // 这个数组用来存放内存区域的信息(从C获取) + let mut mb2_mem_info: [multiboot_mmap_entry_t; 512] = mem::zeroed(); + c_uart_send_str(0x3f8, "init_memory_area_from_multiboot2 begin\n\0".as_ptr()); + + let mut mb2_count: u32 = 0; + multiboot2_iter( + Some(multiboot2_get_memory), + &mut mb2_mem_info as *mut [multiboot_mmap_entry_t; 512] as usize as *mut c_void, + &mut mb2_count, + ); + c_uart_send_str(0x3f8, "init_memory_area_from_multiboot2 2\n\0".as_ptr()); + + let mb2_count = mb2_count as usize; + let mut areas_count = 0usize; + let mut total_mem_size = 0usize; + for i in 0..mb2_count { + // Only use the memory area if its type is 1 (RAM) + if mb2_mem_info[i].type_ == 1 { + // Skip the memory area if its len is 0 + if mb2_mem_info[i].len == 0 { + continue; + } + total_mem_size += mb2_mem_info[i].len as usize; + PHYS_MEMORY_AREAS[areas_count].base = PhysAddr::new(mb2_mem_info[i].addr as usize); + PHYS_MEMORY_AREAS[areas_count].size = mb2_mem_info[i].len as usize; + areas_count += 1; + } + } + c_uart_send_str(0x3f8, "init_memory_area_from_multiboot2 end\n\0".as_ptr()); + kinfo!("Total memory size: {} MB, total areas from multiboot2: {mb2_count}, valid areas: {areas_count}", total_mem_size / 1024 / 1024); + + return Ok(areas_count); + } + + fn init_xd_rsvd() { + // 读取ia32-EFER寄存器的值 + let efer: EferFlags = x86_64::registers::model_specific::Efer::read(); + if !efer.contains(EferFlags::NO_EXECUTE_ENABLE) { + // NO_EXECUTE_ENABLE是false,那么就设置xd_reserved为true + kdebug!("NO_EXECUTE_ENABLE is false, set XD_RESERVED to true"); + XD_RESERVED.store(true, Ordering::Relaxed); + } + compiler_fence(Ordering::SeqCst); + } + + /// 判断XD标志位是否被保留 + pub fn is_xd_reserved() -> bool { + return XD_RESERVED.load(Ordering::Relaxed); + } +} + +impl VirtAddr { + /// @brief 判断虚拟地址是否合法 + #[inline(always)] + pub fn is_canonical(self) -> bool { + let x = self.data() & X86_64MMArch::PHYS_OFFSET; + // 如果x为0,说明虚拟地址的高位为0,是合法的用户地址 + // 如果x为PHYS_OFFSET,说明虚拟地址的高位全为1,是合法的内核地址 + return x == 0 || x == X86_64MMArch::PHYS_OFFSET; + } +} + +/// @brief 初始化内存管理模块 +pub fn mm_init() { + c_uart_send_str(0x3f8, "mm_init\n\0".as_ptr()); + PrintkWriter + .write_fmt(format_args!("mm_init() called\n")) + .unwrap(); + // printk_color!(GREEN, BLACK, "mm_init() called\n"); + static _CALL_ONCE: AtomicBool = AtomicBool::new(false); + if _CALL_ONCE + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_err() + { + c_uart_send_str(0x3f8, "mm_init err\n\0".as_ptr()); + panic!("mm_init() can only be called once"); + } + + unsafe { X86_64MMArch::init() }; + kdebug!("bootstrap info: {:?}", unsafe { BOOTSTRAP_MM_INFO }); + kdebug!("phys[0]=virt[0x{:x}]", unsafe { + MMArch::phys_2_virt(PhysAddr::new(0)).unwrap().data() + }); + + // 初始化内存管理器 + unsafe { allocator_init() }; + // enable mmio + mmio_init(); + // 启用printk的alloc选项 + PrintkWriter.enable_alloc(); +} + +unsafe fn allocator_init() { + let virt_offset = BOOTSTRAP_MM_INFO.unwrap().start_brk; + let phy_offset = + unsafe { MMArch::virt_2_phys(VirtAddr::new(page_align_up(virt_offset))) }.unwrap(); + + kdebug!("PhysArea[0..10] = {:?}", &PHYS_MEMORY_AREAS[0..10]); + let mut bump_allocator = + BumpAllocator::::new(&PHYS_MEMORY_AREAS, phy_offset.data()); + kdebug!( + "BumpAllocator created, offset={:?}", + bump_allocator.offset() + ); + + // 暂存初始在head.S中指定的页表的地址,后面再考虑是否需要把它加到buddy的可用空间里面! + // 现在不加的原因是,我担心会有安全漏洞问题:这些初始的页表,位于内核的数据段。如果归还到buddy, + // 可能会产生一定的安全风险(有的代码可能根据虚拟地址来进行安全校验) + let _old_page_table = MMArch::table(PageTableKind::Kernel); + + let new_page_table: PhysAddr; + // 使用bump分配器,把所有的内存页都映射到页表 + { + // 用bump allocator创建新的页表 + let mut mapper: crate::mm::page::PageMapper> = + crate::mm::page::PageMapper::::create( + PageTableKind::Kernel, + &mut bump_allocator, + ) + .expect("Failed to create page mapper"); + new_page_table = mapper.table().phys(); + kdebug!("PageMapper created"); + + // 取消最开始时候,在head.S中指定的映射(暂时不刷新TLB) + { + let table = mapper.table(); + let empty_entry = PageEntry::::new(0); + for i in 0..MMArch::PAGE_ENTRY_NUM { + table + .set_entry(i, empty_entry) + .expect("Failed to empty page table entry"); + } + } + kdebug!("Successfully emptied page table"); + + for area in PHYS_MEMORY_AREAS.iter() { + // kdebug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); + for i in 0..((area.size + MMArch::PAGE_SIZE - 1) / MMArch::PAGE_SIZE) { + let paddr = area.base.add(i * MMArch::PAGE_SIZE); + let vaddr = unsafe { MMArch::phys_2_virt(paddr) }.unwrap(); + let flags = kernel_page_flags::(vaddr); + + let flusher = mapper + .map_phys(vaddr, paddr, flags) + .expect("Failed to map frame"); + // 暂时不刷新TLB + flusher.ignore(); + } + } + + // 添加低地址的映射(在smp完成初始化之前,需要使用低地址的映射.初始化之后需要取消这一段映射) + LowAddressRemapping::remap_at_low_address(&mut mapper); + } unsafe { - asm!("mov cr3, {}", in(reg) pml4t); + INITIAL_CR3_VALUE = new_page_table; } - mfence(); - return next_pcb; + kdebug!( + "After mapping all physical memory, DragonOS used: {} KB", + bump_allocator.offset() / 1024 + ); + + // 初始化buddy_allocator + let buddy_allocator = unsafe { BuddyAllocator::::new(bump_allocator).unwrap() }; + + // 设置全局的页帧分配器 + unsafe { set_inner_allocator(buddy_allocator) }; + kinfo!("Successfully initialized buddy allocator"); + // 关闭显示输出 + unsafe { + disable_textui(); + } + // make the new page table current + { + let mut binding = INNER_ALLOCATOR.lock(); + let mut allocator_guard = binding.as_mut().unwrap(); + kdebug!("To enable new page table."); + compiler_fence(Ordering::SeqCst); + let mapper = crate::mm::page::PageMapper::::new( + PageTableKind::Kernel, + new_page_table, + &mut allocator_guard, + ); + compiler_fence(Ordering::SeqCst); + mapper.make_current(); + compiler_fence(Ordering::SeqCst); + kdebug!("New page table enabled"); + } + kdebug!("Successfully enabled new page table"); + // 重置显示输出目标 + unsafe { + video_reinitialize(false); + } + + // 打开显示输出 + unsafe { + enable_textui(); + } + kdebug!("Text UI enabled"); +} + +#[no_mangle] +pub extern "C" fn rs_test_buddy() { + test_buddy(); +} +pub fn test_buddy() { + // 申请内存然后写入数据然后free掉 + // 总共申请200MB内存 + const TOTAL_SIZE: usize = 200 * 1024 * 1024; + + for i in 0..10 { + kdebug!("Test buddy, round: {i}"); + // 存放申请的内存块 + let mut v: Vec<(PhysAddr, PageFrameCount)> = Vec::with_capacity(60 * 1024); + // 存放已经申请的内存块的地址(用于检查重复) + let mut addr_set: HashSet = HashSet::new(); + + let mut allocated = 0usize; + + let mut free_count = 0usize; + + while allocated < TOTAL_SIZE { + let mut random_size = 0u64; + unsafe { x86::random::rdrand64(&mut random_size) }; + // 一次最多申请4M + random_size = random_size % (1024 * 4096); + if random_size == 0 { + continue; + } + let random_size = + core::cmp::min(page_align_up(random_size as usize), TOTAL_SIZE - allocated); + let random_size = PageFrameCount::from_bytes(random_size.next_power_of_two()).unwrap(); + // 获取帧 + let (paddr, allocated_frame_count) = + unsafe { LockedFrameAllocator.allocate(random_size).unwrap() }; + assert!(allocated_frame_count.data().is_power_of_two()); + assert!(paddr.data() % MMArch::PAGE_SIZE == 0); + unsafe { + assert!(MMArch::phys_2_virt(paddr) + .as_ref() + .unwrap() + .check_aligned(allocated_frame_count.data() * MMArch::PAGE_SIZE)); + } + allocated += allocated_frame_count.data() * MMArch::PAGE_SIZE; + v.push((paddr, allocated_frame_count)); + assert!(addr_set.insert(paddr), "duplicate address: {:?}", paddr); + + // 写入数据 + let vaddr = unsafe { MMArch::phys_2_virt(paddr).unwrap() }; + let slice = unsafe { + core::slice::from_raw_parts_mut( + vaddr.data() as *mut u8, + allocated_frame_count.data() * MMArch::PAGE_SIZE, + ) + }; + for i in 0..slice.len() { + slice[i] = ((i + unsafe { rdtsc() } as usize) % 256) as u8; + } + + // 随机释放一个内存块 + if v.len() > 0 { + let mut random_index = 0u64; + unsafe { x86::random::rdrand64(&mut random_index) }; + // 70%概率释放 + if random_index % 10 > 7 { + continue; + } + random_index = random_index % v.len() as u64; + let random_index = random_index as usize; + let (paddr, allocated_frame_count) = v.remove(random_index); + assert!(addr_set.remove(&paddr)); + unsafe { LockedFrameAllocator.free(paddr, allocated_frame_count) }; + free_count += allocated_frame_count.data() * MMArch::PAGE_SIZE; + } + } + + kdebug!( + "Allocated {} MB memory, release: {} MB, no release: {} bytes", + allocated / 1024 / 1024, + free_count / 1024 / 1024, + (allocated - free_count) + ); + + kdebug!("Now, to release buddy memory"); + // 释放所有的内存 + for (paddr, allocated_frame_count) in v { + unsafe { LockedFrameAllocator.free(paddr, allocated_frame_count) }; + assert!(addr_set.remove(&paddr)); + free_count += allocated_frame_count.data() * MMArch::PAGE_SIZE; + } + + kdebug!("release done!, allocated: {allocated}, free_count: {free_count}"); + } +} +/// 全局的页帧分配器 +#[derive(Debug, Clone, Copy, Hash)] +pub struct LockedFrameAllocator; + +impl FrameAllocator for LockedFrameAllocator { + unsafe fn allocate( + &mut self, + count: crate::mm::allocator::page_frame::PageFrameCount, + ) -> Option<(PhysAddr, PageFrameCount)> { + if let Some(ref mut allocator) = *INNER_ALLOCATOR.lock_irqsave() { + return allocator.allocate(count); + } else { + return None; + } + } + + unsafe fn free( + &mut self, + address: crate::mm::PhysAddr, + count: crate::mm::allocator::page_frame::PageFrameCount, + ) { + assert!(count.data().is_power_of_two()); + if let Some(ref mut allocator) = *INNER_ALLOCATOR.lock_irqsave() { + return allocator.free(address, count); + } + } + + unsafe fn usage(&self) -> crate::mm::allocator::page_frame::PageFrameUsage { + todo!() + } +} + +/// 获取内核地址默认的页面标志 +pub unsafe fn kernel_page_flags(virt: VirtAddr) -> PageFlags { + let info: X86_64MMBootstrapInfo = BOOTSTRAP_MM_INFO.clone().unwrap(); + + if virt.data() >= info.kernel_code_start && virt.data() < info.kernel_code_end { + // Remap kernel code execute + return PageFlags::new().set_execute(true).set_write(true); + } else if virt.data() >= info.kernel_data_end && virt.data() < info.kernel_rodata_end { + // Remap kernel rodata read only + return PageFlags::new().set_execute(true); + } else { + return PageFlags::new().set_write(true).set_execute(true); + } +} + +unsafe fn set_inner_allocator(allocator: BuddyAllocator) { + static FLAG: AtomicBool = AtomicBool::new(false); + if FLAG + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_err() + { + panic!("Cannot set inner allocator twice!"); + } + *INNER_ALLOCATOR.lock() = Some(allocator); +} + +/// 低地址重映射的管理器 +/// +/// 低地址重映射的管理器,在smp初始化完成之前,需要使用低地址的映射,因此需要在smp初始化完成之后,取消这一段映射 +pub struct LowAddressRemapping; + +impl LowAddressRemapping { + // 映射32M + const REMAP_SIZE: usize = 32 * 1024 * 1024; + + pub unsafe fn remap_at_low_address( + mapper: &mut crate::mm::page::PageMapper>, + ) { + for i in 0..(Self::REMAP_SIZE / MMArch::PAGE_SIZE) { + let paddr = PhysAddr::new(i * MMArch::PAGE_SIZE); + let vaddr = VirtAddr::new(i * MMArch::PAGE_SIZE); + let flags = kernel_page_flags::(vaddr); + + let flusher = mapper + .map_phys(vaddr, paddr, flags) + .expect("Failed to map frame"); + // 暂时不刷新TLB + flusher.ignore(); + } + } + + /// 取消低地址的映射 + pub unsafe fn unmap_at_low_address(flush: bool) { + let mut mapper = KernelMapper::lock(); + assert!(mapper.as_mut().is_some()); + for i in 0..(Self::REMAP_SIZE / MMArch::PAGE_SIZE) { + let vaddr = VirtAddr::new(i * MMArch::PAGE_SIZE); + let flusher = mapper + .as_mut() + .unwrap() + .unmap(vaddr, true) + .expect("Failed to unmap frame"); + if flush == false { + flusher.ignore(); + } + } + } +} +#[no_mangle] +pub extern "C" fn rs_mm_init() { + mm_init(); } diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 5b325ac9..2bb4724b 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -4,6 +4,7 @@ pub mod context; pub mod cpu; pub mod fpu; pub mod interrupt; +pub mod libs; pub mod mm; pub mod msi; pub mod pci; @@ -11,4 +12,9 @@ pub mod rand; pub mod sched; pub mod syscall; +pub use self::pci::pci::X86_64PciArch as PciArch; + +/// 导出内存管理的Arch结构体 +pub use self::mm::X86_64MMArch as MMArch; + pub use interrupt::X86_64InterruptArch as CurrentIrqArch; diff --git a/kernel/src/arch/x86_64/msi.rs b/kernel/src/arch/x86_64/msi.rs index 394206f1..fd72007e 100644 --- a/kernel/src/arch/x86_64/msi.rs +++ b/kernel/src/arch/x86_64/msi.rs @@ -12,7 +12,7 @@ pub fn ia64_pci_get_arch_msi_message_address(processor: u16) -> u32 { /// @return MSI Message Address pub fn ia64_pci_get_arch_msi_message_data( vector: u16, - processor: u16, + _processor: u16, trigger: TriggerMode, ) -> u32 { match trigger { diff --git a/kernel/src/arch/x86_64/syscall.rs b/kernel/src/arch/x86_64/syscall.rs index 8e3d48d9..24ce3754 100644 --- a/kernel/src/arch/x86_64/syscall.rs +++ b/kernel/src/arch/x86_64/syscall.rs @@ -1,24 +1,27 @@ -use core::ffi::c_void; +use core::{ffi::c_void, panic}; + +use alloc::{string::String, vec::Vec}; use crate::{ + arch::{asm::current::current_pcb, CurrentIrqArch}, + exception::InterruptArch, + filesystem::vfs::MAX_PATHLEN, include::bindings::bindings::{ - pt_regs, set_system_trap_gate, verify_area, CLONE_FS, CLONE_SIGNAL, CLONE_VM, PAGE_4K_SIZE, + pt_regs, set_system_trap_gate, CLONE_FS, CLONE_SIGNAL, CLONE_VM, USER_CS, USER_DS, }, ipc::signal::sys_rt_sigreturn, - kinfo, - syscall::{Syscall, SystemError, SYS_EXECVE, SYS_FORK, SYS_RT_SIGRETURN, SYS_VFORK}, + mm::{ucontext::AddressSpace, verify_area, VirtAddr}, + process::exec::{load_binary_file, ExecParam, ExecParamFlags}, + syscall::{ + user_access::{check_and_clone_cstr, check_and_clone_cstr_array}, + Syscall, SystemError, SYS_EXECVE, SYS_FORK, SYS_RT_SIGRETURN, SYS_VFORK, + }, }; use super::{asm::ptrace::user_mode, mm::barrier::mfence}; extern "C" { fn do_fork(regs: *mut pt_regs, clone_flags: u64, stack_start: u64, stack_size: u64) -> u64; - fn c_sys_execve( - path: *const u8, - argv: *const *const u8, - envp: *const *const u8, - regs: &mut pt_regs, - ) -> u64; fn syscall_int(); } @@ -71,23 +74,34 @@ pub extern "C" fn syscall_handler(regs: &mut pt_regs) -> () { // 权限校验 if from_user - && (unsafe { !verify_area(path_ptr as u64, PAGE_4K_SIZE as u64) } - || unsafe { !verify_area(argv_ptr as u64, PAGE_4K_SIZE as u64) }) - || unsafe { !verify_area(env_ptr as u64, PAGE_4K_SIZE as u64) } + && (verify_area(VirtAddr::new(path_ptr), MAX_PATHLEN).is_err() + || verify_area(VirtAddr::new(argv_ptr), MAX_PATHLEN).is_err() + || verify_area(VirtAddr::new(env_ptr), MAX_PATHLEN).is_err()) { syscall_return!(SystemError::EFAULT.to_posix_errno() as u64, regs); } else { - syscall_return!( - unsafe { - c_sys_execve( + unsafe { + // kdebug!("syscall: execve\n"); + syscall_return!( + rs_do_execve( path_ptr as *const u8, argv_ptr as *const *const u8, env_ptr as *const *const u8, - regs, - ) - }, - regs - ); + regs + ), + regs + ); + // let path = String::from("/bin/about.elf"); + // let argv = vec![String::from("/bin/about.elf")]; + // let envp = vec![String::from("PATH=/bin")]; + // let r = tmp_rs_execve(path, argv, envp, regs); + // kdebug!("syscall: execve r: {:?}\n", r); + + // syscall_return!( + // r.map(|_| 0).unwrap_or_else(|e| e.to_posix_errno() as usize), + // regs + // ) + } } } @@ -104,7 +118,147 @@ pub extern "C" fn syscall_handler(regs: &mut pt_regs) -> () { /// 系统调用初始化 pub fn arch_syscall_init() -> Result<(), SystemError> { - kinfo!("arch_syscall_init\n"); + // kinfo!("arch_syscall_init\n"); unsafe { set_system_trap_gate(0x80, 0, syscall_int as *mut c_void) }; // 系统调用门 return Ok(()); } + +#[no_mangle] +pub unsafe extern "C" fn rs_do_execve( + path: *const u8, + argv: *const *const u8, + envp: *const *const u8, + regs: &mut pt_regs, +) -> usize { + if path.is_null() { + return SystemError::EINVAL.to_posix_errno() as usize; + } + + let x = || { + let path: String = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let argv: Vec = check_and_clone_cstr_array(argv)?; + let envp: Vec = check_and_clone_cstr_array(envp)?; + Ok((path, argv, envp)) + }; + let r: Result<(String, Vec, Vec), SystemError> = x(); + if let Err(e) = r { + panic!("Failed to execve: {:?}", e); + } + let (path, argv, envp) = r.unwrap(); + + return tmp_rs_execve(path, argv, envp, regs) + .map(|_| 0) + .unwrap_or_else(|e| { + panic!( + "Failed to execve, pid: {} error: {:?}", + current_pcb().pid, + e + ) + }); +} + +/// 执行第一个用户进程的函数(只应该被调用一次) +/// +/// 当进程管理重构完成后,这个函数应该被删除。调整为别的函数。 +#[no_mangle] +pub extern "C" fn rs_exec_init_process(regs: &mut pt_regs) -> usize { + let path = String::from("/bin/shell.elf"); + let argv = vec![String::from("/bin/shell.elf")]; + let envp = vec![String::from("PATH=/bin")]; + let r = tmp_rs_execve(path, argv, envp, regs); + // kdebug!("rs_exec_init_process: r: {:?}\n", r); + return r.map(|_| 0).unwrap_or_else(|e| e.to_posix_errno() as usize); +} + +/// 临时的execve系统调用实现,以后要把它改为普通的系统调用。 +/// +/// 现在放在这里的原因是,还没有重构中断管理模块,未实现TrapFrame这个抽象, +/// 导致我们必须手动设置中断返回时,各个寄存器的值,这个过程很繁琐,所以暂时放在这里。 +fn tmp_rs_execve( + path: String, + argv: Vec, + envp: Vec, + regs: &mut pt_regs, +) -> Result<(), SystemError> { + // kdebug!( + // "tmp_rs_execve: path: {:?}, argv: {:?}, envp: {:?}\n", + // path, + // argv, + // envp + // ); + // 关中断,防止在设置地址空间的时候,发生中断,然后进调度器,出现错误。 + let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; + // 暂存原本的用户地址空间的引用(因为如果在切换页表之前释放了它,可能会造成内存use after free) + let old_address_space = current_pcb().address_space(); + // 在pcb中原来的用户地址空间 + unsafe { + current_pcb().drop_address_space(); + } + // 创建新的地址空间并设置为当前地址空间 + let address_space = AddressSpace::new(true).expect("Failed to create new address space"); + unsafe { + current_pcb().set_address_space(address_space.clone()); + } + assert!( + AddressSpace::is_current(&address_space), + "Failed to set address space" + ); + // kdebug!("Switch to new address space"); + + // 切换到新的用户地址空间 + unsafe { address_space.read().user_mapper.utable.make_current() }; + + drop(old_address_space); + drop(irq_guard); + // kdebug!("to load binary file"); + let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC); + // 加载可执行文件 + let load_result = load_binary_file(&mut param) + .unwrap_or_else(|e| panic!("Failed to load binary file: {:?}, path: {:?}", e, path)); + // kdebug!("load binary file done"); + + param.init_info_mut().args = argv; + param.init_info_mut().envs = envp; + + // 把proc_init_info写到用户栈上 + + let (user_sp, argv_ptr) = unsafe { + param + .init_info() + .push_at( + address_space + .write() + .user_stack_mut() + .expect("No user stack found"), + ) + .expect("Failed to push proc_init_info to user stack") + }; + + // kdebug!("write proc_init_info to user stack done"); + + // (兼容旧版libc)把argv的指针写到寄存器内 + // TODO: 改写旧版libc,不再需要这个兼容 + regs.rdi = param.init_info().args.len() as u64; + regs.rsi = argv_ptr.data() as u64; + + // 设置系统调用返回时的寄存器状态 + // TODO: 中断管理重构后,这里的寄存器状态设置要删掉!!!改为对trap frame的设置。要增加架构抽象。 + regs.rsp = user_sp.data() as u64; + regs.rbp = user_sp.data() as u64; + regs.rip = load_result.entry_point().data() as u64; + + regs.cs = USER_CS as u64 | 3; + regs.ds = USER_DS as u64 | 3; + regs.ss = USER_DS as u64 | 3; + regs.es = 0; + regs.rflags = 0x200; + regs.rax = 1; + + // kdebug!("regs: {:?}\n", regs); + + // kdebug!( + // "tmp_rs_execve: done, load_result.entry_point()={:?}", + // load_result.entry_point() + // ); + return Ok(()); +} diff --git a/kernel/src/common/atomic.h b/kernel/src/common/atomic.h index e295ce7c..d9fc2ef6 100644 --- a/kernel/src/common/atomic.h +++ b/kernel/src/common/atomic.h @@ -9,6 +9,7 @@ * */ #pragma once +#include #define atomic_read(atomic) ((atomic)->value) // 读取原子变量 #define atomic_set(atomic,val) (((atomic)->value) = (val)) // 设置原子变量的初始值 @@ -97,3 +98,10 @@ inline void atomic_clear_mask(atomic_t *ato, long mask) : "r"(mask) : "memory"); } + +// cmpxchgq 比较并交换 +inline long atomic_cmpxchg(atomic_t *ato, long oldval, long newval) +{ + bool success = arch_try_cmpxchg(&ato->value, &oldval, &newval); + return success ? oldval : newval; +} \ No newline at end of file diff --git a/kernel/src/driver/acpi/acpi.c b/kernel/src/driver/acpi/acpi.c index e7f69f77..45f296b1 100644 --- a/kernel/src/driver/acpi/acpi.c +++ b/kernel/src/driver/acpi/acpi.c @@ -44,7 +44,8 @@ void acpi_iter_SDT(bool (*_fun)(const struct acpi_system_description_table_heade ul *ent = &(xsdt->Entry); for (int i = 0; i < acpi_XSDT_Entry_num; ++i) { - mm_map_phys_addr(acpi_description_header_base + PAGE_2M_SIZE * i, (*(ent + i)) & PAGE_2M_MASK, PAGE_2M_SIZE, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false); + // mm_map_phys_addr(acpi_description_header_base + PAGE_2M_SIZE * i, (*(ent + i)) & PAGE_2M_MASK, PAGE_2M_SIZE, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false); + rs_map_phys(acpi_description_header_base + PAGE_2M_SIZE * i, (*(ent + i)) & PAGE_2M_MASK, PAGE_2M_SIZE, PAGE_KERNEL_PAGE); sdt_header = (struct acpi_system_description_table_header_t *)((ul)(acpi_description_header_base + PAGE_2M_SIZE * i)); if (_fun(sdt_header, _data) == true) @@ -173,13 +174,14 @@ void acpi_init() ul rsdt_phys_base = rsdpv2->rsdp1.RsdtAddress & PAGE_2M_MASK; acpi_RSDT_offset = rsdpv2->rsdp1.RsdtAddress - rsdt_phys_base; - //申请mmio空间 + // 申请mmio空间 uint64_t size = 0; mmio_create(PAGE_2M_SIZE, VM_IO | VM_DONTCOPY, &acpi_rsdt_virt_addr_base, &size); - //映射rsdt表 + // 映射rsdt表 paddr = (uint64_t)rsdt_phys_base; - mm_map(&initial_mm, acpi_rsdt_virt_addr_base, PAGE_2M_SIZE, paddr); + // mm_map(&initial_mm, acpi_rsdt_virt_addr_base, PAGE_2M_SIZE, paddr); + rs_map_phys(acpi_rsdt_virt_addr_base, paddr, PAGE_2M_SIZE, PAGE_KERNEL_PAGE); // rsdt表虚拟地址 rsdt = (struct acpi_RSDT_Structure_t *)(acpi_rsdt_virt_addr_base + acpi_RSDT_offset); @@ -192,7 +194,7 @@ void acpi_init() printk_color(ORANGE, BLACK, "RSDT Length=%dbytes.\n", rsdt->header.Length); printk_color(ORANGE, BLACK, "RSDT Entry num=%d\n", acpi_RSDT_Entry_num); - //申请mmio空间 + // 申请mmio空间 mmio_create(PAGE_2M_SIZE, VM_IO | VM_DONTCOPY, &acpi_description_header_base, &size); // 映射所有的Entry的物理地址 @@ -201,23 +203,27 @@ void acpi_init() acpi_RSDT_entry_phys_base = MASK_HIGH_32bit(acpi_RSDT_entry_phys_base); paddr = (uint64_t)acpi_RSDT_entry_phys_base; - mm_map(&initial_mm, acpi_description_header_base, PAGE_2M_SIZE, paddr); + // mm_map(&initial_mm, acpi_description_header_base, PAGE_2M_SIZE, paddr); + rs_map_phys(acpi_description_header_base, paddr, PAGE_2M_SIZE, PAGE_KERNEL_PAGE); } else if (rsdpv1->RsdtAddress != (uint)0x00UL) { // rsdt表物理地址 ul rsdt_phys_base = rsdpv1->RsdtAddress & PAGE_2M_MASK; acpi_RSDT_offset = rsdpv1->RsdtAddress - rsdt_phys_base; - + kdebug("rsdpv1->RsdtAddress=%#018lx", rsdpv1->RsdtAddress); - //申请mmio空间 + // 申请mmio空间 uint64_t size = 0; mmio_create(PAGE_2M_SIZE, VM_IO | VM_DONTCOPY, &acpi_rsdt_virt_addr_base, &size); + // acpi_rsdt_virt_addr_base = 0xffffb00000000000UL; + kdebug("ACPI: mmio created. acpi_rsdt_virt_addr_base = %#018lx,size= %#010lx", acpi_rsdt_virt_addr_base, size); // kdebug("acpi_rsdt_virt_addr_base = %#018lx,size= %#010lx", acpi_rsdt_virt_addr_base, size); - //映射rsdt表 + // 映射rsdt表 paddr = (uint64_t)rsdt_phys_base; - mm_map(&initial_mm, acpi_rsdt_virt_addr_base, PAGE_2M_SIZE, paddr); + // mm_map(&initial_mm, acpi_rsdt_virt_addr_base, PAGE_2M_SIZE, paddr); + rs_map_phys(acpi_rsdt_virt_addr_base, paddr, PAGE_2M_SIZE, PAGE_KERNEL_PAGE); // rsdt表虚拟地址 rsdt = (struct acpi_RSDT_Structure_t *)(acpi_rsdt_virt_addr_base + acpi_RSDT_offset); kdebug("RSDT mapped!"); @@ -231,7 +237,7 @@ void acpi_init() printk_color(ORANGE, BLACK, "RSDT Length=%dbytes.\n", rsdt->header.Length); printk_color(ORANGE, BLACK, "RSDT Entry num=%d\n", acpi_RSDT_Entry_num); - //申请mmio空间 + // 申请mmio空间 mmio_create(PAGE_2M_SIZE, VM_IO | VM_DONTCOPY, &acpi_description_header_base, &size); // 映射所有的Entry的物理地址 @@ -240,9 +246,9 @@ void acpi_init() acpi_RSDT_entry_phys_base = MASK_HIGH_32bit(acpi_RSDT_entry_phys_base); paddr = (uint64_t)acpi_RSDT_entry_phys_base; - mm_map(&initial_mm, acpi_description_header_base, PAGE_2M_SIZE, paddr); - // kinfo("entry mapped!"); - + // mm_map(&initial_mm, acpi_description_header_base, PAGE_2M_SIZE, paddr); + rs_map_phys(acpi_description_header_base, paddr, PAGE_2M_SIZE, PAGE_KERNEL_PAGE); + kinfo("entry mapped!"); } else { diff --git a/kernel/src/driver/base/char/mod.rs b/kernel/src/driver/base/char/mod.rs index 7637069d..2b81d8ea 100644 --- a/kernel/src/driver/base/char/mod.rs +++ b/kernel/src/driver/base/char/mod.rs @@ -71,7 +71,7 @@ impl CharDeviceStruct { /// name: 字符设备名 /// char: 字符设备实例 /// @return: 实例 - /// + /// #[allow(dead_code)] pub fn new(dev_t: DeviceNumber, minorct: usize, name: &'static str) -> Self { Self { @@ -84,7 +84,7 @@ impl CharDeviceStruct { /// @brief: 获取起始次设备号 /// @parameter: None /// @return: 起始设备号 - /// + /// #[allow(dead_code)] pub fn device_number(&self) -> DeviceNumber { self.dev_t @@ -93,7 +93,7 @@ impl CharDeviceStruct { /// @brief: 获取起始次设备号 /// @parameter: None /// @return: 起始设备号 - /// + /// #[allow(dead_code)] pub fn base_minor(&self) -> usize { self.dev_t.minor() @@ -122,7 +122,7 @@ impl CharDevOps { /// @brief: 动态获取主设备号 /// @parameter: None - /// @return: 如果成功,返回主设备号,否则,返回错误码 + /// @return: 如果成功,返回主设备号,否则,返回错误码 #[allow(dead_code)] fn find_dynamic_major() -> Result { let chardevs = CHARDEVS.0.lock(); @@ -171,7 +171,7 @@ impl CharDevOps { /// @parameter: baseminor: 主设备号 /// count: 次设备号数量 /// name: 字符设备名 - /// @return: 如果注册成功,返回,否则,返回false + /// @return: 如果注册成功,返回,否则,返回false #[allow(dead_code)] pub fn alloc_chardev_region( baseminor: usize, diff --git a/kernel/src/driver/base/device/mod.rs b/kernel/src/driver/base/device/mod.rs index ccce9758..2baa5197 100644 --- a/kernel/src/driver/base/device/mod.rs +++ b/kernel/src/driver/base/device/mod.rs @@ -1,3 +1,5 @@ +use alloc::{collections::BTreeMap, string::String, sync::Arc}; + use crate::{ filesystem::{ sysfs::{ @@ -9,7 +11,6 @@ use crate::{ libs::spinlock::SpinLock, syscall::SystemError, }; -use alloc::{collections::BTreeMap, string::String, sync::Arc}; use core::{any::Any, fmt::Debug}; pub mod bus; diff --git a/kernel/src/driver/disk/ahci/ahci.c b/kernel/src/driver/disk/ahci/ahci.c index 4da4d54b..b58daebf 100644 --- a/kernel/src/driver/disk/ahci/ahci.c +++ b/kernel/src/driver/disk/ahci/ahci.c @@ -26,7 +26,7 @@ void ahci_cpp_init(uint32_t *count_ahci_devices, struct pci_device_structure_hea // 映射ABAR uint32_t bar5 = gen_devs[0]->BAR5; - mm_map_phys_addr(AHCI_MAPPING_BASE, (ul)(bar5)&PAGE_2M_MASK, PAGE_2M_SIZE, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false); + rs_map_phys(AHCI_MAPPING_BASE, (ul)(bar5)&PAGE_2M_MASK, PAGE_2M_SIZE, PAGE_KERNEL_PAGE); kinfo("ABAR mapped!"); } diff --git a/kernel/src/driver/interrupt/apic/apic.c b/kernel/src/driver/interrupt/apic/apic.c index f585ae47..31a043b5 100644 --- a/kernel/src/driver/interrupt/apic/apic.c +++ b/kernel/src/driver/interrupt/apic/apic.c @@ -86,8 +86,8 @@ void apic_io_apic_init() // kdebug("(ul)apic_ioapic_map.virtual_index_addr=%#018lx", (ul)apic_ioapic_map.virtual_index_addr); // 填写页表,完成地址映射 - mm_map_phys_addr((ul)apic_ioapic_map.virtual_index_addr, apic_ioapic_map.addr_phys, PAGE_2M_SIZE, - PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false); + rs_map_phys((ul)apic_ioapic_map.virtual_index_addr, apic_ioapic_map.addr_phys, PAGE_2M_SIZE, + PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD); // 设置IO APIC ID 为0x0f000000 *apic_ioapic_map.virtual_index_addr = 0x00; @@ -278,8 +278,8 @@ void apic_local_apic_init() uint64_t ia32_apic_base = rdmsr(0x1b); // kdebug("apic base=%#018lx", (ia32_apic_base & 0x1FFFFFFFFFF000)); // 映射Local APIC 寄存器地址 - mm_map_phys_addr(APIC_LOCAL_APIC_VIRT_BASE_ADDR, (ia32_apic_base & 0x1FFFFFFFFFFFFF), PAGE_2M_SIZE, - PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false); + // todo: + rs_map_phys(APIC_LOCAL_APIC_VIRT_BASE_ADDR, (ia32_apic_base & 0x1FFFFFFFFFF000), PAGE_2M_SIZE, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD); uint a, b, c, d; cpu_cpuid(1, 0, &a, &b, &c, &d); @@ -357,6 +357,7 @@ void apic_local_apic_init() */ int apic_init() { + kinfo("Initializing APIC..."); // 初始化中断门, 中断使用rsp0防止在软中断时发生嵌套,然后处理器重新加载导致数据被抹掉 for (int i = 32; i <= 55; ++i) set_intr_gate(i, 0, interrupt_table[i - 32]); @@ -396,6 +397,7 @@ int apic_init() RCBA_vaddr = 0; kwarn("Cannot get RCBA address. RCBA_phys=%#010lx", RCBA_phys); } + kinfo("APIC initialized."); sti(); return 0; } diff --git a/kernel/src/driver/pci/msi.c b/kernel/src/driver/pci/msi.c index 6f249eeb..e59ffc8a 100644 --- a/kernel/src/driver/pci/msi.c +++ b/kernel/src/driver/pci/msi.c @@ -96,7 +96,7 @@ static __always_inline int __msix_map_table(struct pci_device_structure_header_t // pci_dev->msix_mmio_vaddr, bar, pci_dev->msix_offset, pci_dev->msix_table_size, pci_dev->msix_mmio_size); // 将msix table映射到页表 - mm_map(&initial_mm, pci_dev->msix_mmio_vaddr, pci_dev->msix_mmio_size, bar); + rs_map_phys(pci_dev->msix_mmio_vaddr, bar, pci_dev->msix_mmio_size, PAGE_KERNEL_PAGE); return 0; } diff --git a/kernel/src/driver/pci/pci.rs b/kernel/src/driver/pci/pci.rs index 5c089fe2..f4adc08f 100644 --- a/kernel/src/driver/pci/pci.rs +++ b/kernel/src/driver/pci/pci.rs @@ -3,15 +3,17 @@ use super::pci_irq::{IrqType, PciIrqError}; use crate::arch::{PciArch, TraitPciArch}; -use crate::include::bindings::bindings::{ - initial_mm, mm_map, mm_struct, PAGE_2M_SIZE, VM_DONTCOPY, VM_IO, -}; +use crate::include::bindings::bindings::{PAGE_2M_SIZE, VM_DONTCOPY, VM_IO}; use crate::libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -use crate::mm::mmio_buddy::MMIO_POOL; +use crate::mm::kernel_mapper::KernelMapper; +use crate::mm::mmio_buddy::mmio_pool; +use crate::mm::page::PageFlags; +use crate::mm::{PhysAddr, VirtAddr}; use crate::{kdebug, kerror, kinfo, kwarn}; use alloc::vec::Vec; use alloc::{boxed::Box, collections::LinkedList}; use bitflags::bitflags; + use core::{ convert::TryFrom, fmt::{self, Debug, Display, Formatter}, @@ -632,20 +634,27 @@ impl PciRoot { let virtsize_ptr = &mut virtsize as *mut u64; let size = bus_number_double * PAGE_2M_SIZE; unsafe { - let initial_mm_ptr = &mut initial_mm as *mut mm_struct; - if let Err(_) = - MMIO_POOL.create_mmio(size, (VM_IO | VM_DONTCOPY) as u64, vaddr_ptr, virtsize_ptr) - { + if let Err(_) = mmio_pool().create_mmio( + size as usize, + (VM_IO | VM_DONTCOPY) as u64, + vaddr_ptr, + virtsize_ptr, + ) { kerror!("Create mmio failed when initing ecam"); return Err(PciError::CreateMmioError); }; + //kdebug!("virtaddress={:#x},virtsize={:#x}",virtaddress,virtsize); - mm_map( - initial_mm_ptr, - virtaddress, - size as u64, - self.physical_address_base, - ); + let vaddr = VirtAddr::new(virtaddress as usize); + let paddr = PhysAddr::new(self.physical_address_base as usize); + // kdebug!("pci root: map: vaddr={vaddr:?}, paddr={paddr:?}, size={size}"); + let page_flags = PageFlags::mmio_flags(); + let mut kernel_mapper = KernelMapper::lock(); + // todo: 添加错误处理代码。因为内核映射器可能是只读的,所以可能会出错 + assert!(kernel_mapper + .map_phys_with_size(vaddr, paddr, size as usize, page_flags, true) + .is_ok()); + drop(kernel_mapper); } self.mmio_base = Some(virtaddress as *mut u32); Ok(0) @@ -1302,8 +1311,9 @@ impl Display for BarInfo { } } } -//todo 增加对桥的bar的支持 +// todo 增加对桥的bar的支持 pub trait PciDeviceBar {} + ///一个普通PCI设备(非桥)有6个BAR寄存器,PciStandardDeviceBar存储其全部信息 #[derive(Clone, Debug, Eq, PartialEq)] pub struct PciStandardDeviceBar { @@ -1409,10 +1419,11 @@ pub fn pci_bar_init( let vaddr_ptr = &mut virtaddress as *mut u64; let mut virtsize: u64 = 0; let virtsize_ptr = &mut virtsize as *mut u64; - let initial_mm_ptr = &mut initial_mm as *mut mm_struct; - //kdebug!("size want={:#x}", size); - if let Err(_) = MMIO_POOL.create_mmio( - size, + + let size_want = size as usize; + + if let Err(_) = mmio_pool().create_mmio( + size_want, (VM_IO | VM_DONTCOPY) as u64, vaddr_ptr, virtsize_ptr, @@ -1421,7 +1432,20 @@ pub fn pci_bar_init( return Err(PciError::CreateMmioError); }; //kdebug!("virtaddress={:#x},virtsize={:#x}",virtaddress,virtsize); - mm_map(initial_mm_ptr, virtaddress, size as u64, address); + let vaddr = VirtAddr::new(virtaddress as usize); + let paddr = PhysAddr::new(address as usize); + let page_flags = PageFlags::new() + .set_write(true) + .set_execute(true) + .set_page_cache_disable(true) + .set_page_write_through(true); + kdebug!("Pci bar init: vaddr={vaddr:?}, paddr={paddr:?}, size_want={size_want}, page_flags={page_flags:?}"); + let mut kernel_mapper = KernelMapper::lock(); + // todo: 添加错误处理代码。因为内核映射器可能是只读的,所以可能会出错 + assert!(kernel_mapper + .map_phys_with_size(vaddr, paddr, size_want, page_flags, true) + .is_ok()); + drop(kernel_mapper); } bar_info = BarInfo::Memory { address_type, diff --git a/kernel/src/driver/pci/pci_irq.rs b/kernel/src/driver/pci/pci_irq.rs index 24e95afe..670f1289 100644 --- a/kernel/src/driver/pci/pci_irq.rs +++ b/kernel/src/driver/pci/pci_irq.rs @@ -6,16 +6,14 @@ use core::ptr::NonNull; use alloc::ffi::CString; use alloc::vec::Vec; -use super::pci::{HeaderType, PciDeviceStructure, PciDeviceStructureGeneralDevice, PciError}; +use super::pci::{PciDeviceStructure, PciDeviceStructureGeneralDevice, PciError}; use crate::arch::msi::{ia64_pci_get_arch_msi_message_address, ia64_pci_get_arch_msi_message_data}; use crate::arch::{PciArch, TraitPciArch}; use crate::include::bindings::bindings::{ c_irq_install, c_irq_uninstall, pt_regs, ul, EAGAIN, EINVAL, }; -use crate::libs::volatile::{ - volread, volwrite, ReadOnly, Volatile, VolatileReadable, VolatileWritable, WriteOnly, -}; -use crate::{kdebug, kerror, kinfo, kwarn}; +use crate::libs::volatile::{volread, volwrite, Volatile, VolatileReadable, VolatileWritable}; + /// MSIX表的一项 #[repr(C)] struct MsixEntry { @@ -268,7 +266,7 @@ pub trait PciInterrupt: PciDeviceStructure { return Err(PciError::PciIrqError(PciIrqError::PciDeviceNotSupportIrq)); } /// @brief 获取指定数量的中断号 todo 需要中断重构支持 - fn irq_alloc(num: u16) -> Option> { + fn irq_alloc(_num: u16) -> Option> { None } /// @brief 进行PCI设备中断的安装 diff --git a/kernel/src/driver/uart/uart.rs b/kernel/src/driver/uart/uart.rs index 15fefbca..101f4944 100644 --- a/kernel/src/driver/uart/uart.rs +++ b/kernel/src/driver/uart/uart.rs @@ -2,7 +2,7 @@ use super::super::base::device::Device; use crate::{ driver::base::{ char::CharDevice, - device::{driver::Driver, DeviceState, DeviceType, IdTable, KObject}, + device::{driver::Driver, DeviceState, DeviceType, IdTable, KObject}, platform::{ self, platform_device::PlatformDevice, platform_driver::PlatformDriver, CompatibleTable, }, diff --git a/kernel/src/driver/video/video.c b/kernel/src/driver/video/video.c index 00365236..6bbfc313 100644 --- a/kernel/src/driver/video/video.c +++ b/kernel/src/driver/video/video.c @@ -34,16 +34,10 @@ void init_frame_buffer() { kinfo("Re-mapping VBE frame buffer..."); - uint64_t global_CR3 = (uint64_t)get_CR3(); - - struct multiboot_tag_framebuffer_info_t info; - int reserved; - video_frame_buffer_info.vaddr = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + FRAME_BUFFER_MAPPING_OFFSET; - mm_map_proc_page_table(global_CR3, true, video_frame_buffer_info.vaddr, __fb_info.framebuffer_addr, - video_frame_buffer_info.size, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false, true, false); - flush_tlb(); + rs_map_phys(video_frame_buffer_info.vaddr, __fb_info.framebuffer_addr, video_frame_buffer_info.size, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD); + kinfo("VBE frame buffer successfully Re-mapped!"); } @@ -119,7 +113,6 @@ int video_reinitialize(bool level) // 这个函数会在main.c调用, 保证 vid // 启用屏幕刷新软中断 rs_register_softirq_video(); rs_raise_softirq(VIDEO_REFRESH_SIRQ); - } return 0; } @@ -189,8 +182,10 @@ int video_init() video_frame_buffer_info.width * video_frame_buffer_info.height * ((video_frame_buffer_info.bit_depth + 7) / 8); // 先临时映射到该地址,稍后再重新映射 video_frame_buffer_info.vaddr = 0xffff800003000000; - mm_map_phys_addr(video_frame_buffer_info.vaddr, __fb_info.framebuffer_addr, video_frame_buffer_info.size, - PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false); + char init_text1[] = "Video driver to map.\n"; + for (int i = 0; i < sizeof(init_text1) - 1; ++i) + c_uart_send(COM1, init_text1[i]); + rs_pseudo_map_phys(video_frame_buffer_info.vaddr, __fb_info.framebuffer_addr, video_frame_buffer_info.size); io_mfence(); char init_text2[] = "Video driver initialized.\n"; diff --git a/kernel/src/driver/virtio/virtio_impl.rs b/kernel/src/driver/virtio/virtio_impl.rs index 1ad3f9dc..645979b6 100644 --- a/kernel/src/driver/virtio/virtio_impl.rs +++ b/kernel/src/driver/virtio/virtio_impl.rs @@ -1,59 +1,101 @@ -/// 为virtio-drivers库提供的操作系统接口 -use crate::include::bindings::bindings::{ - alloc_pages, free_pages, memory_management_struct, Page, PAGE_2M_SHIFT, PAGE_2M_SIZE, - PAGE_OFFSET, PAGE_SHARED, ZONE_NORMAL, +use crate::arch::mm::kernel_page_flags; + +use crate::arch::MMArch; + +use crate::mm::kernel_mapper::KernelMapper; +use crate::mm::page::PageFlags; +use crate::mm::{ + allocator::page_frame::{ + allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, + }, + MemoryManagementArch, PhysAddr, VirtAddr, }; -use crate::mm::virt_2_phys; -use core::mem::size_of; use core::ptr::NonNull; -use virtio_drivers::{BufferDirection, Hal, PhysAddr, PAGE_SIZE}; +use virtio_drivers::{BufferDirection, Hal, PAGE_SIZE}; + pub struct HalImpl; unsafe impl Hal for HalImpl { /// @brief 申请用于DMA的内存页 /// @param pages 页数(4k一页) /// @return PhysAddr 获得的内存页的初始物理地址 - fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull) { - let page_num = (pages * PAGE_SIZE - 1 + PAGE_2M_SIZE as usize) / PAGE_2M_SIZE as usize; + fn dma_alloc( + pages: usize, + _direction: BufferDirection, + ) -> (virtio_drivers::PhysAddr, NonNull) { + let page_num = PageFrameCount::new( + ((pages * PAGE_SIZE + MMArch::PAGE_SIZE - 1) / MMArch::PAGE_SIZE).next_power_of_two(), + ); unsafe { - let pa = alloc_pages(ZONE_NORMAL, page_num as i32, PAGE_SHARED as u64); - let page = *pa; - //kdebug!("alloc pages num:{},Phyaddr={:#x}",pages,page.addr_phys); - ( - page.addr_phys as PhysAddr, - NonNull::new((page.addr_phys as PhysAddr + PAGE_OFFSET as usize) as _).unwrap(), - ) + let (paddr, count) = + allocate_page_frames(page_num).expect("VirtIO Impl: alloc page failed"); + let virt = MMArch::phys_2_virt(paddr).unwrap(); + // 清空这块区域,防止出现脏数据 + core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); + + let dma_flags: PageFlags = PageFlags::mmio_flags(); + + let mut kernel_mapper = KernelMapper::lock(); + let kernel_mapper = kernel_mapper.as_mut().unwrap(); + let flusher = kernel_mapper + .remap(virt, dma_flags) + .expect("VirtIO Impl: remap failed"); + flusher.flush(); + return ( + paddr.data(), + NonNull::new(MMArch::phys_2_virt(paddr).unwrap().data() as _).unwrap(), + ); } } /// @brief 释放用于DMA的内存页 /// @param paddr 起始物理地址 pages 页数(4k一页) /// @return i32 0表示成功 - unsafe fn dma_dealloc(paddr: PhysAddr, _vaddr: NonNull, pages: usize) -> i32 { - let page_num = (pages * PAGE_SIZE - 1 + PAGE_2M_SIZE as usize) / PAGE_2M_SIZE as usize; + unsafe fn dma_dealloc( + paddr: virtio_drivers::PhysAddr, + vaddr: NonNull, + pages: usize, + ) -> i32 { + let page_count = PageFrameCount::new( + ((pages * PAGE_SIZE + MMArch::PAGE_SIZE - 1) / MMArch::PAGE_SIZE).next_power_of_two(), + ); + + // 恢复页面属性 + let vaddr = VirtAddr::new(vaddr.as_ptr() as *mut u8 as usize); + let mut kernel_mapper = KernelMapper::lock(); + let kernel_mapper = kernel_mapper.as_mut().unwrap(); + let flusher = kernel_mapper + .remap(vaddr, kernel_page_flags(vaddr)) + .expect("VirtIO Impl: remap failed"); + flusher.flush(); + unsafe { - let pa = (memory_management_struct.pages_struct as usize - + (paddr >> PAGE_2M_SHIFT) * size_of::()) as *mut Page; - //kdebug!("free pages num:{},Phyaddr={}",page_num,paddr); - free_pages(pa, page_num as i32); + deallocate_page_frames(PhysPageFrame::new(PhysAddr::new(paddr)), page_count); } return 0; } /// @brief mmio物理地址转换为虚拟地址,不需要使用 /// @param paddr 起始物理地址 /// @return NonNull 虚拟地址的指针 - unsafe fn mmio_phys_to_virt(_paddr: PhysAddr, _size: usize) -> NonNull { + unsafe fn mmio_phys_to_virt(_paddr: virtio_drivers::PhysAddr, _size: usize) -> NonNull { NonNull::new((0) as _).unwrap() } /// @brief 与真实物理设备共享 /// @param buffer 要共享的buffer _direction:设备到driver或driver到设备 /// @return buffer在内存中的物理地址 - unsafe fn share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr { - let vaddr = buffer.as_ptr() as *mut u8 as usize; + unsafe fn share( + buffer: NonNull<[u8]>, + _direction: BufferDirection, + ) -> virtio_drivers::PhysAddr { + let vaddr = VirtAddr::new(buffer.as_ptr() as *mut u8 as usize); //kdebug!("virt:{:x}", vaddr); // Nothing to do, as the host already has access to all memory. - virt_2_phys(vaddr) + return MMArch::virt_2_phys(vaddr).unwrap().data(); } /// @brief 停止共享(让主机可以访问全部内存的话什么都不用做) - unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) { + unsafe fn unshare( + _paddr: virtio_drivers::PhysAddr, + _buffer: NonNull<[u8]>, + _direction: BufferDirection, + ) { // Nothing to do, as the host already has access to all memory and we didn't copy the buffer // anywhere else. } diff --git a/kernel/src/exception/mod.rs b/kernel/src/exception/mod.rs index d4240a4e..5ab53bc4 100644 --- a/kernel/src/exception/mod.rs +++ b/kernel/src/exception/mod.rs @@ -19,15 +19,15 @@ pub trait InterruptArch: Send + Sync { #[derive(Debug, Clone, Copy)] pub struct IrqFlags { - flags: u64, + flags: usize, } impl IrqFlags { - pub fn new(flags: u64) -> Self { + pub fn new(flags: usize) -> Self { IrqFlags { flags } } - pub fn flags(&self) -> u64 { + pub fn flags(&self) -> usize { self.flags } } diff --git a/kernel/src/exception/softirq.rs b/kernel/src/exception/softirq.rs index 3cc6eff9..8eeb158f 100644 --- a/kernel/src/exception/softirq.rs +++ b/kernel/src/exception/softirq.rs @@ -46,6 +46,7 @@ pub fn softirq_init() -> Result<(), SystemError> { cpu_pending[i as usize] = VecStatus::default(); } } + kinfo!("Softirq initialized."); return Ok(()); } diff --git a/kernel/src/filesystem/procfs/mod.rs b/kernel/src/filesystem/procfs/mod.rs index b46f954c..c1146cfb 100644 --- a/kernel/src/filesystem/procfs/mod.rs +++ b/kernel/src/filesystem/procfs/mod.rs @@ -10,6 +10,7 @@ use alloc::{ }; use crate::{ + arch::asm::current::current_pcb, filesystem::vfs::{ core::{generate_inode_id, ROOT_INODE}, FileType, @@ -168,13 +169,15 @@ impl ProcFSInode { .to_owned(), ); - // 当前进程运行过程中占用内存的峰值 - let hiwater_vm: u64 = - unsafe { *(*pcb.mm).vmas }.vm_end - unsafe { *(*pcb.mm).vmas }.vm_start; + let binding = current_pcb().address_space().unwrap(); + let address_space_guard = binding.read(); + // todo: 当前进程运行过程中占用内存的峰值 + let hiwater_vm: u64 = 0; + // 进程代码段的大小 + let text = (address_space_guard.end_code - address_space_guard.start_code) / 1024; // 进程数据段的大小 - let text: u64 = unsafe { *pcb.mm }.code_addr_end - unsafe { *pcb.mm }.code_addr_start; - // 进程代码的大小 - let data: u64 = unsafe { *pcb.mm }.data_addr_end - unsafe { *pcb.mm }.data_addr_start; + let data = (address_space_guard.end_data - address_space_guard.start_data) / 1024; + drop(address_space_guard); pdata.append( &mut format!("\nVmPeak:\t{} kB", hiwater_vm) diff --git a/kernel/src/filesystem/sysfs/bus.rs b/kernel/src/filesystem/sysfs/bus.rs index 1676a66e..acd797f0 100644 --- a/kernel/src/filesystem/sysfs/bus.rs +++ b/kernel/src/filesystem/sysfs/bus.rs @@ -1,9 +1,5 @@ use super::{LockedSysFSInode, SYS_BUS_INODE}; -use crate::{ - filesystem::vfs::IndexNode, - kdebug, - syscall::SystemError, -}; +use crate::{filesystem::vfs::IndexNode, kdebug, syscall::SystemError}; use alloc::sync::Arc; /// @brief: 注册bus,在sys/bus下生成文件夹 @@ -58,20 +54,15 @@ pub fn sys_bus_init( /// @parameter bus_name: 总线名 /// name: 设备名 /// @return: 操作成功,返回device inode,操作失败,返回错误码 -pub fn bus_driver_register( - bus_name: &str, - name: &str, -) -> Result, SystemError> { +pub fn bus_driver_register(bus_name: &str, name: &str) -> Result, SystemError> { match SYS_BUS_INODE().find(bus_name) { Ok(platform) => match platform.find("drivers") { - Ok(device) => { - device - .as_any_ref() - .downcast_ref::() - .ok_or(SystemError::E2BIG) - .unwrap() - .add_dir(name) - } + Ok(device) => device + .as_any_ref() + .downcast_ref::() + .ok_or(SystemError::E2BIG) + .unwrap() + .add_dir(name), Err(_) => return Err(SystemError::EXDEV), }, Err(_) => return Err(SystemError::EXDEV), @@ -82,20 +73,15 @@ pub fn bus_driver_register( /// @parameter bus_name: 总线名 /// name: 驱动名 /// @return: 操作成功,返回drivers inode,操作失败,返回错误码 -pub fn bus_device_register( - bus_name: &str, - name: &str, -) -> Result, SystemError> { +pub fn bus_device_register(bus_name: &str, name: &str) -> Result, SystemError> { match SYS_BUS_INODE().find(bus_name) { Ok(platform) => match platform.find("devices") { - Ok(device) => { - device - .as_any_ref() - .downcast_ref::() - .ok_or(SystemError::E2BIG) - .unwrap() - .add_dir(name) - } + Ok(device) => device + .as_any_ref() + .downcast_ref::() + .ok_or(SystemError::E2BIG) + .unwrap() + .add_dir(name), Err(_) => return Err(SystemError::EXDEV), }, Err(_) => return Err(SystemError::EXDEV), diff --git a/kernel/src/filesystem/vfs/VFS.h b/kernel/src/filesystem/vfs/VFS.h index 4675d9b2..d7f78dd4 100644 --- a/kernel/src/filesystem/vfs/VFS.h +++ b/kernel/src/filesystem/vfs/VFS.h @@ -22,7 +22,7 @@ #define VFS_DPT_MBR 0 // MBR分区表 #define VFS_DPT_GPT 1 // GPT分区表 -#define VFS_MAX_PATHLEN 1024 +#define VFS_MAX_PATHLEN 4096 /** * @brief inode的属性 diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 02d3eff8..c216360c 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -173,7 +173,7 @@ impl File { /// /// @param origin 调整的起始位置 pub fn lseek(&mut self, origin: SeekFrom) -> Result { - let file_type = self.inode.metadata().unwrap().file_type; + let file_type = self.inode.metadata()?.file_type; match file_type { FileType::Pipe | FileType::CharDevice => { return Err(SystemError::ESPIPE); diff --git a/kernel/src/head.S b/kernel/src/head.S index 985f8cf1..19677460 100644 --- a/kernel/src/head.S +++ b/kernel/src/head.S @@ -255,7 +255,7 @@ ENTRY(_start64) movq $0x1b, %rcx // 根据IA32_APIC_BASE.BSP[8]标志位判断处理器是否为apu rdmsr bt $8, %rax - jnc load_cr3 + jnc load_apu_cr3 // 2. 设置临时页表 // 最高级 @@ -263,7 +263,11 @@ ENTRY(_start64) mov $__PDPTE, %ebx or $0x3, %ebx mov %ebx, 0(%eax) - mov %ebx, 256(%eax) + + mov $__PML4E, %eax + // 加256个表项, 映射高地址 + add $2048, %eax + mov %ebx, 0(%eax) // 次级 mov $__PDPTE, %eax @@ -271,6 +275,28 @@ ENTRY(_start64) or $0x3, %ebx mov %ebx, 0(%eax) + // 次低级 + mov $__PDE, %eax + mov $50, %ecx + mov $__PT_S, %ebx + or $0x3, %ebx +.fill_pde_64: + mov %ebx, 0(%eax) + add $0x1000, %ebx + add $8, %eax + loop .fill_pde_64 + + // 最低级 + // 循环 512*50=25600 次,填满50页 + mov $25600, %ecx + mov $__PT_S, %eax + mov $0x3, %ebx +.fill_pt_64: + mov %ebx, 0(%eax) + add $0x1000, %ebx + add $8, %eax + loop .fill_pt_64 + // ==== 加载CR3寄存器 @@ -279,7 +305,20 @@ load_cr3: movq $__PML4E, %rax //设置页目录基地址 movq %rax, %cr3 - + jmp to_switch_seg + +load_apu_cr3: + // 由于内存管理模块重置了页表,因此ap核心初始化的时候,需要使用新的内核页表。 + // 这个页表的值由smp模块设置到__APU_START_CR3变量中 + + // 加载__APU_START_CR3中的值 + movq $__APU_START_CR3, %rax + movq 0(%rax), %rax + movq %rax, %cr3 + jmp to_switch_seg + +to_switch_seg: + movq switch_seg(%rip), %rax // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器 // 实在是太妙了!Amazing! @@ -481,73 +520,23 @@ ENTRY(head_stack_start) // 初始化页表 .align 0x1000 //设置为4k对齐 -//.org 0x1000 //设置页表位置为内核执行头程序的0x1000处 - __PML4E: - .quad 0x103007 // 用户访问,可读写,已存在, 地址在31~12位 - .fill 255,8,0 - .quad 0x103003 - .fill 255,8,0 - -.org 0x2000 - + .skip 0x1000 __PDPTE: + .skip 0x1000 - .quad 0x104003 // 用户访问,可读写,已存在 - .fill 511,8,0 - -.org 0x3000 - +// 三级页表 __PDE: + .skip 0x1000 - .quad 0x000083 // 用户访问,可读写,已存在 - .quad 0x200083 - .quad 0x400083 - .quad 0x600083 - .quad 0x800083 - .quad 0xa00083 - .quad 0xc00083 - .quad 0xe00083 - .quad 0x1000083 - .quad 0x1200083 - .quad 0x1400083 - .quad 0x1600083 - .quad 0x1800083 - .quad 0x1a00083 - .quad 0x1c00083 - .quad 0x1e00083 - .quad 0x2000083 - .quad 0x2200083 - .quad 0x2400083 - .quad 0x2600083 - .quad 0x2800083 - .quad 0x2a00083 - .quad 0x2c00083 - .quad 0x2e00083 - .quad 0x3000083 - .quad 0x3200083 - .quad 0x3400083 - .quad 0x3600083 +// 预留50个四级页表,总共表示100M的内存空间。这50个页表占用200KB的空间 +__PT_S: + .skip 0x32000 - .quad 0xe0000083 /*虚拟地址0x 3000000 初始情况下,帧缓冲区映射到这里*/ - .quad 0xe0200083 - .quad 0xe0400083 - .quad 0xe0600083 /*0x1000000*/ - .quad 0xe0800083 - .quad 0xe0a00083 - .quad 0xe0c00083 - .quad 0xe0e00083 - .quad 0xe1000083 - .quad 0xe1200083 - .quad 0xe1400083 - .quad 0xe1600083 - .quad 0xe1800083 - .quad 0xe1a00083 - .quad 0xe1c00083 - .quad 0xe1e00083 - .fill 468,8,0 - +.global __APU_START_CR3 +__APU_START_CR3: + .quad 0 // GDT表 diff --git a/kernel/src/include/bindings/wrapper.h b/kernel/src/include/bindings/wrapper.h index b7a0d376..f6cf2d59 100644 --- a/kernel/src/include/bindings/wrapper.h +++ b/kernel/src/include/bindings/wrapper.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -49,4 +50,4 @@ #include { + // 页存放entry的数量 + entry_num: usize, + // 下一个页面的地址 + next_page: PhysAddr, + phantom: PhantomData, +} + +impl Clone for PageList { + fn clone(&self) -> Self { + Self { + entry_num: self.entry_num, + next_page: self.next_page, + phantom: PhantomData, + } + } +} + +impl PageList { + #[allow(dead_code)] + fn empty() -> Self { + Self { + entry_num: 0, + next_page: PhysAddr::new(0), + phantom: PhantomData, + } + } + fn new(entry_num: usize, next_page: PhysAddr) -> Self { + Self { + entry_num, + next_page, + phantom: PhantomData, + } + } +} + +/// @brief: 用来表示 buddy 算法中的一个 buddy 块,整体存放在area的头部 +// 这种方式会出现对齐问题 +// #[repr(packed)] +#[repr(C)] +#[derive(Debug)] +pub struct BuddyAllocator { + // 存放每个阶的空闲“链表”的头部地址 + free_area: [PhysAddr; (MAX_ORDER - MIN_ORDER) as usize], + phantom: PhantomData, +} + +impl BuddyAllocator { + const BUDDY_ENTRIES: usize = + // 定义一个变量记录buddy表的大小 + (A::PAGE_SIZE - mem::size_of::>()) / mem::size_of::(); + + pub unsafe fn new(mut bump_allocator: BumpAllocator) -> Option { + let initial_free_pages = bump_allocator.usage().free(); + kdebug!("Free pages before init buddy: {:?}", initial_free_pages); + kdebug!("Buddy entries: {}", Self::BUDDY_ENTRIES); + // 最高阶的链表页数 + let max_order_linked_list_page_num = max( + 1, + (((initial_free_pages.data() * A::PAGE_SIZE) >> (MAX_ORDER - 1)) + Self::BUDDY_ENTRIES + - 1) + / Self::BUDDY_ENTRIES, + ); + + let mut free_area: [PhysAddr; (MAX_ORDER - MIN_ORDER) as usize] = + [PhysAddr::new(0); (MAX_ORDER - MIN_ORDER) as usize]; + + // Buddy初始占用的空间从bump分配 + for f in free_area.iter_mut() { + let curr_page = bump_allocator.allocate_one(); + // 保存每个阶的空闲链表的头部地址 + *f = curr_page.unwrap(); + // 清空当前页 + core::ptr::write_bytes(MMArch::phys_2_virt(*f)?.data() as *mut u8, 0, A::PAGE_SIZE); + + let page_list: PageList = PageList::new(0, PhysAddr::new(0)); + Self::write_page(*f, page_list); + } + + // 分配最高阶的链表页 + for _ in 1..max_order_linked_list_page_num { + let curr_page = bump_allocator.allocate_one().unwrap(); + // 清空当前页 + core::ptr::write_bytes( + MMArch::phys_2_virt(curr_page)?.data() as *mut u8, + 0, + A::PAGE_SIZE, + ); + + let page_list: PageList = + PageList::new(0, free_area[Self::order2index((MAX_ORDER - 1) as u8)]); + Self::write_page(curr_page, page_list); + free_area[Self::order2index((MAX_ORDER - 1) as u8)] = curr_page; + } + + let initial_bump_offset = bump_allocator.offset(); + let pages_to_buddy = bump_allocator.usage().free(); + kdebug!("pages_to_buddy {:?}", pages_to_buddy); + // kdebug!("initial_bump_offset {:#x}", initial_bump_offset); + let mut paddr = initial_bump_offset; + let mut remain_pages = pages_to_buddy; + // 设置entry,这里假设了bump_allocator当前offset之后,所有的area的地址是连续的. + // TODO: 这里需要修改,按照area来处理 + for i in MIN_ORDER..MAX_ORDER { + // kdebug!("i {i}, remain pages={}", remain_pages.data()); + if remain_pages.data() < (1 << (i - MIN_ORDER)) { + break; + } + + assert!(paddr & ((1 << i) - 1) == 0); + + if likely(i != MAX_ORDER - 1) { + // 要填写entry + if paddr & (1 << i) != 0 { + let page_list_paddr: PhysAddr = free_area[Self::order2index(i as u8)]; + let mut page_list: PageList = Self::read_page(page_list_paddr); + + A::write( + Self::entry_virt_addr(page_list_paddr, page_list.entry_num), + paddr, + ); + page_list.entry_num += 1; + Self::write_page(page_list_paddr, page_list); + + paddr += 1 << i; + remain_pages -= 1 << (i - MIN_ORDER); + }; + } else { + // 往最大的阶数的链表中添加entry(注意要考虑到最大阶数的链表可能有多页) + // 断言剩余页面数量是MAX_ORDER-1阶的整数倍 + + let mut entries = (remain_pages.data() * A::PAGE_SIZE) >> i; + let mut page_list_paddr: PhysAddr = free_area[Self::order2index(i as u8)]; + let block_size = 1usize << i; + + if entries > Self::BUDDY_ENTRIES { + // 在第一页填写一些entries + let num = entries % Self::BUDDY_ENTRIES; + entries -= num; + + let mut page_list: PageList = Self::read_page(page_list_paddr); + for _j in 0..num { + A::write( + Self::entry_virt_addr(page_list_paddr, page_list.entry_num), + paddr, + ); + page_list.entry_num += 1; + paddr += block_size; + remain_pages -= 1 << (i - MIN_ORDER); + } + page_list_paddr = page_list.next_page; + Self::write_page(page_list_paddr, page_list); + assert!(!page_list_paddr.is_null()); + } + + while entries > 0 { + let mut page_list: PageList = Self::read_page(page_list_paddr); + + for _ in 0..Self::BUDDY_ENTRIES { + A::write( + Self::entry_virt_addr(page_list_paddr, page_list.entry_num), + paddr, + ); + page_list.entry_num += 1; + paddr += block_size; + remain_pages -= 1 << (i - MIN_ORDER); + entries -= 1; + if entries == 0 { + break; + } + } + page_list_paddr = page_list.next_page; + Self::write_page(page_list_paddr, page_list); + + if likely(entries > 0) { + assert!(!page_list_paddr.is_null()); + } + } + } + } + + let mut remain_bytes = remain_pages.data() * A::PAGE_SIZE; + + assert!(remain_bytes < (1 << MAX_ORDER - 1)); + + for i in (MIN_ORDER..MAX_ORDER).rev() { + if remain_bytes & (1 << i) != 0 { + let page_list_paddr: PhysAddr = free_area[Self::order2index(i as u8)]; + let mut page_list: PageList = Self::read_page(page_list_paddr); + + A::write( + Self::entry_virt_addr(page_list_paddr, page_list.entry_num), + paddr, + ); + page_list.entry_num += 1; + Self::write_page(page_list_paddr, page_list); + + paddr += 1 << i; + remain_bytes -= 1 << i; + } + } + + assert!(remain_bytes == 0); + assert!(paddr == initial_bump_offset + pages_to_buddy.data() * A::PAGE_SIZE); + + // Self::print_free_area(free_area); + let allocator = Self { + free_area, + phantom: PhantomData, + }; + + Some(allocator) + } + /// 获取第j个entry的虚拟地址, + /// j从0开始计数 + pub fn entry_virt_addr(base_addr: PhysAddr, j: usize) -> VirtAddr { + let entry_virt_addr = unsafe { A::phys_2_virt(Self::entry_addr(base_addr, j)) }; + return entry_virt_addr.unwrap(); + } + pub fn entry_addr(base_addr: PhysAddr, j: usize) -> PhysAddr { + let entry_addr = base_addr + mem::size_of::>() + j * mem::size_of::(); + return entry_addr; + } + pub fn read_page(addr: PhysAddr) -> T { + let page_list = unsafe { A::read(A::phys_2_virt(addr).unwrap()) }; + return page_list; + } + + pub fn write_page(curr_page: PhysAddr, page_list: PageList) { + // 把物理地址转换为虚拟地址 + let virt_addr = unsafe { A::phys_2_virt(curr_page) }; + let virt_addr = virt_addr.unwrap(); + unsafe { A::write(virt_addr, page_list) }; + } + + /// 从order转换为free_area的下标 + /// + /// # 参数 + /// + /// - `order` - order + /// + /// # 返回值 + /// + /// free_area的下标 + #[inline] + fn order2index(order: u8) -> usize { + (order as usize - MIN_ORDER) as usize + } + + /// 从空闲链表的开头,取出1个指定阶数的伙伴块,如果没有,则返回None + /// + /// ## 参数 + /// + /// - `order` - 伙伴块的阶数 + fn pop_front(&mut self, order: u8) -> Option { + let mut alloc_in_specific_order = |spec_order: u8| { + // 先尝试在order阶的“空闲链表”的开头位置分配一个伙伴块 + let mut page_list_addr = self.free_area[Self::order2index(spec_order)]; + let mut page_list: PageList = Self::read_page(page_list_addr); + + // kdebug!("page_list={page_list:?}"); + + // 循环删除头部的空闲链表页 + while page_list.entry_num == 0 { + let next_page_list_addr = page_list.next_page; + // 找完了,都是空的 + if next_page_list_addr.is_null() { + return None; + } + + if !next_page_list_addr.is_null() { + // 此时page_list已经没有空闲伙伴块了,又因为非唯一页,需要删除该page_list + self.free_area[Self::order2index(spec_order)] = next_page_list_addr; + drop(page_list); + // kdebug!("FREE: page_list_addr={:b}", page_list_addr.data()); + unsafe { + self.buddy_free(page_list_addr, MMArch::PAGE_SHIFT as u8); + } + } + // 由于buddy_free可能导致首部的链表页发生变化,因此需要重新读取 + let next_page_list_addr = self.free_area[Self::order2index(spec_order)]; + assert!(!next_page_list_addr.is_null()); + page_list = Self::read_page(next_page_list_addr); + page_list_addr = next_page_list_addr; + } + + // 有空闲页面,直接分配 + if page_list.entry_num > 0 { + let entry: PhysAddr = unsafe { + A::read(Self::entry_virt_addr( + page_list_addr, + page_list.entry_num - 1, + )) + }; + if entry.is_null() { + kerror!( + "entry is null, entry={:?}, order={}, entry_num = {}", + entry, + spec_order, + page_list.entry_num - 1 + ); + } + // kdebug!("entry={entry:?}"); + // 更新page_list的entry_num + page_list.entry_num -= 1; + let tmp_current_entry_num = page_list.entry_num; + if page_list.entry_num == 0 { + if !page_list.next_page.is_null() { + // 此时page_list已经没有空闲伙伴块了,又因为非唯一页,需要删除该page_list + self.free_area[Self::order2index(spec_order)] = page_list.next_page; + drop(page_list); + unsafe { self.buddy_free(page_list_addr, MMArch::PAGE_SHIFT as u8) }; + } else { + Self::write_page(page_list_addr, page_list); + } + } else { + // 若entry_num不为0,说明该page_list还有空闲伙伴块,需要更新该page_list + // 把更新后的page_list写回 + Self::write_page(page_list_addr, page_list.clone()); + } + + // 检测entry 是否对齐 + if !entry.check_aligned(1 << spec_order) { + panic!("entry={:?} is not aligned, spec_order={spec_order}, page_list.entry_num={}", entry,tmp_current_entry_num); + } + return Some(entry); + } + return None; + }; + + let result: Option = alloc_in_specific_order(order as u8); + // kdebug!("result={:?}", result); + if result.is_some() { + return result; + } + // 尝试从更大的链表中分裂 + + let mut current_order = (order + 1) as usize; + let mut x: Option = None; + while current_order < MAX_ORDER { + x = alloc_in_specific_order(current_order as u8); + // kdebug!("current_order={:?}", current_order); + if x.is_some() { + break; + } + current_order += 1; + } + + // kdebug!("x={:?}", x); + // 如果找到一个大的块,就进行分裂 + if x.is_some() { + // 分裂到order阶 + while current_order > order as usize { + current_order -= 1; + // 把后面那半块放回空闲链表 + + let buddy = *x.as_ref().unwrap() + (1 << current_order); + // kdebug!("x={:?}, buddy={:?}", x, buddy); + // kdebug!("current_order={:?}, buddy={:?}", current_order, buddy); + unsafe { self.buddy_free(buddy, current_order as u8) }; + } + return x; + } + + return None; + } + + /// 从伙伴系统中分配count个页面 + /// + /// ## 参数 + /// + /// - `count`:需要分配的页面数 + /// + /// ## 返回值 + /// + /// 返回分配的页面的物理地址和页面数 + fn buddy_alloc(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { + assert!(count.data().is_power_of_two()); + // 计算需要分配的阶数 + let mut order = log2(count.data() as usize); + if count.data() & ((1 << order) - 1) != 0 { + order += 1; + } + let order = (order + MIN_ORDER) as u8; + if order as usize >= MAX_ORDER { + return None; + } + + // kdebug!("buddy_alloc: order = {}", order); + // 获取该阶数的一个空闲页面 + let free_addr = self.pop_front(order); + // kdebug!( + // "buddy_alloc: order = {}, free_addr = {:?}", + // order, + // free_addr + // ); + return free_addr + .map(|addr| (addr, PageFrameCount::new(1 << (order as usize - MIN_ORDER)))); + } + + /// 释放一个块 + /// + /// ## 参数 + /// + /// - `base` - 块的起始地址 + /// - `order` - 块的阶数 + unsafe fn buddy_free(&mut self, mut base: PhysAddr, order: u8) { + // kdebug!("buddy_free: base = {:?}, order = {}", base, order); + let mut order = order as usize; + + while order < MAX_ORDER { + // 检测地址是否合法 + if base.data() & ((1 << (order)) - 1) != 0 { + panic!( + "buddy_free: base is not aligned, base = {:#x}, order = {}", + base.data(), + order + ); + } + + // 在链表中寻找伙伴块 + // 伙伴块的地址是base ^ (1 << order) + let buddy_addr = PhysAddr::new(base.data() ^ (1 << order)); + + let first_page_list_paddr = self.free_area[Self::order2index(order as u8)]; + let mut page_list_paddr = first_page_list_paddr; + let mut page_list: PageList = Self::read_page(page_list_paddr); + let first_page_list = page_list.clone(); + + let mut buddy_entry_virt_vaddr = None; + let mut buddy_entry_page_list_paddr = None; + // 除非order是最大的,否则尝试查找伙伴块 + if likely(order != MAX_ORDER - 1) { + 'outer: loop { + for i in 0..page_list.entry_num { + let entry_virt_addr = Self::entry_virt_addr(page_list_paddr, i); + let entry: PhysAddr = unsafe { A::read(entry_virt_addr) }; + if entry == buddy_addr { + // 找到了伙伴块,记录该entry相关信息,然后退出查找 + buddy_entry_virt_vaddr = Some(entry_virt_addr); + buddy_entry_page_list_paddr = Some(page_list_paddr); + break 'outer; + } + } + if page_list.next_page.is_null() { + break; + } + page_list_paddr = page_list.next_page; + page_list = Self::read_page(page_list_paddr); + } + } + + // 如果没有找到伙伴块 + if buddy_entry_virt_vaddr.is_none() { + assert!( + page_list.entry_num <= Self::BUDDY_ENTRIES, + "buddy_free: page_list.entry_num > Self::BUDDY_ENTRIES" + ); + + // 当前第一个page_list没有空间了 + if first_page_list.entry_num == Self::BUDDY_ENTRIES { + // 如果当前order是最小的,那么就把这个块当作新的page_list使用 + let new_page_list_addr = if order == MIN_ORDER { + base + } else { + // 否则分配新的page_list + // 请注意,分配之后,有可能当前的entry_num会减1(伙伴块分裂),造成出现整个链表为null的entry数量为Self::BUDDY_ENTRIES+1的情况 + // 但是不影响,我们在后面插入链表项的时候,会处理这种情况,检查链表中的第2个页是否有空位 + self.buddy_alloc(PageFrameCount::new(1)) + .expect("buddy_alloc failed: no enough memory") + .0 + }; + + // 清空这个页面 + core::ptr::write_bytes( + A::phys_2_virt(new_page_list_addr) + .expect( + "Buddy free: failed to get virt address of [new_page_list_addr]", + ) + .as_ptr::(), + 0, + 1 << order, + ); + assert!( + first_page_list_paddr == self.free_area[Self::order2index(order as u8)] + ); + // 初始化新的page_list + let new_page_list = PageList::new(0, first_page_list_paddr); + Self::write_page(new_page_list_addr, new_page_list); + self.free_area[Self::order2index(order as u8)] = new_page_list_addr; + } + + // 由于上面可能更新了第一个链表页,因此需要重新获取这个值 + let first_page_list_paddr = self.free_area[Self::order2index(order as u8)]; + let first_page_list: PageList = Self::read_page(first_page_list_paddr); + + // 检查第二个page_list是否有空位 + let second_page_list = if first_page_list.next_page.is_null() { + None + } else { + Some(Self::read_page::>(first_page_list.next_page)) + }; + + let (paddr, mut page_list) = if let Some(second) = second_page_list { + // 第二个page_list有空位 + // 应当符合之前的假设:还有1个空位 + assert!(second.entry_num == Self::BUDDY_ENTRIES - 1); + + (first_page_list.next_page, second) + } else { + // 在第一个page list中分配 + (first_page_list_paddr, first_page_list) + }; + + // kdebug!("to write entry, page_list_base={paddr:?}, page_list.entry_num={}, value={base:?}", page_list.entry_num); + assert!(page_list.entry_num < Self::BUDDY_ENTRIES); + // 把要归还的块,写入到链表项中 + unsafe { A::write(Self::entry_virt_addr(paddr, page_list.entry_num), base) } + page_list.entry_num += 1; + Self::write_page(paddr, page_list); + return; + } else { + // 如果找到了伙伴块,合并,向上递归 + + // 伙伴块所在的表项的虚拟地址 + let buddy_entry_virt_addr = buddy_entry_virt_vaddr.unwrap(); + // 伙伴块所在的page_list的物理地址 + let buddy_entry_page_list_paddr = buddy_entry_page_list_paddr.unwrap(); + + let mut page_list_paddr = self.free_area[Self::order2index(order as u8)]; + let mut page_list = Self::read_page::>(page_list_paddr); + // 找第一个有空闲块的链表页。跳过空闲链表页。不进行回收的原因是担心出现死循环 + while page_list.entry_num == 0 { + if page_list.next_page.is_null() { + panic!( + "buddy_free: page_list.entry_num == 0 && page_list.next_page.is_null()" + ); + } + page_list_paddr = page_list.next_page; + page_list = Self::read_page(page_list_paddr); + } + + // 如果伙伴块不在第一个链表页,则把第一个链表中的某个空闲块替换到伙伴块的位置 + if page_list_paddr != buddy_entry_page_list_paddr { + let entry: PhysAddr = unsafe { + A::read(Self::entry_virt_addr( + page_list_paddr, + page_list.entry_num - 1, + )) + }; + // 把这个空闲块写入到伙伴块的位置 + unsafe { + A::write(buddy_entry_virt_addr, entry); + } + // 设置刚才那个entry为空 + unsafe { + A::write( + Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1), + PhysAddr::new(0), + ); + } + // 更新当前链表页的统计数据 + page_list.entry_num -= 1; + Self::write_page(page_list_paddr, page_list); + } else { + // 伙伴块所在的链表页就是第一个链表页 + let last_entry: PhysAddr = unsafe { + A::read(Self::entry_virt_addr( + page_list_paddr, + page_list.entry_num - 1, + )) + }; + + // 如果最后一个空闲块不是伙伴块,则把最后一个空闲块移动到伙伴块的位置 + // 否则后面的操作也将删除这个伙伴块 + if last_entry != buddy_addr { + unsafe { + A::write(buddy_entry_virt_addr, last_entry); + A::write( + Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1), + PhysAddr::new(0), + ); + } + } else { + unsafe { + A::write( + Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1), + PhysAddr::new(0), + ); + } + } + // 更新当前链表页的统计数据 + page_list.entry_num -= 1; + Self::write_page(page_list_paddr, page_list); + } + } + base = min(base, buddy_addr); + order += 1; + } + // 走到这一步,order应该为MAX_ORDER-1 + assert!(order == MAX_ORDER - 1); + } +} + +impl FrameAllocator for BuddyAllocator { + unsafe fn allocate(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { + return self.buddy_alloc(count); + } + + /// 释放一个块 + /// + /// ## 参数 + /// + /// - `base` - 块的起始地址 + /// - `count` - 块的页数(必须是2的幂) + /// + /// ## Panic + /// + /// 如果count不是2的幂,会panic + unsafe fn free(&mut self, base: PhysAddr, count: PageFrameCount) { + // 要求count是2的幂 + if unlikely(!count.data().is_power_of_two()) { + kwarn!("buddy free: count is not power of two"); + } + let mut order = log2(count.data() as usize); + if count.data() & ((1 << order) - 1) != 0 { + order += 1; + } + let order = (order + MIN_ORDER) as u8; + // kdebug!("free: base={:?}, count={:?}", base, count); + self.buddy_free(base, order); + } + + unsafe fn usage(&self) -> PageFrameUsage { + todo!("BuddyAllocator::usage") + } +} + +/// 一个用于计算整数的对数的函数,会向下取整。(由于内核不能进行浮点运算,因此需要这个函数) +fn log2(x: usize) -> usize { + let leading_zeros = x.leading_zeros() as usize; + let log2x = 63 - leading_zeros; + return log2x; +} diff --git a/kernel/src/mm/allocator/bump.rs b/kernel/src/mm/allocator/bump.rs new file mode 100644 index 00000000..02401749 --- /dev/null +++ b/kernel/src/mm/allocator/bump.rs @@ -0,0 +1,112 @@ +/// @Auther: Kong +/// @Date: 2023-03-27 06:54:08 +/// @FilePath: /DragonOS/kernel/src/mm/allocator/bump.rs +/// @Description: bump allocator线性分配器 +use super::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}; +use crate::mm::{MemoryManagementArch, PhysAddr, PhysMemoryArea}; +use core::marker::PhantomData; + +/// 线性分配器 +pub struct BumpAllocator { + // 表示可用物理内存区域的数组。每个 PhysMemoryArea 结构体描述一个物理内存区域的起始地址和大小。 + areas: &'static [PhysMemoryArea], + // 表示当前分配的物理内存的偏移量. + offset: usize, + // 一个占位类型,用于标记 A 类型在结构体中的存在。但是,它并不会占用任何内存空间,因为它的大小为 0。 + phantom: PhantomData, +} + +/// 为BumpAllocator实现FrameAllocator +impl BumpAllocator { + /// @brief: 创建一个线性分配器 + /// @param Fareas 当前的内存区域 + /// @param offset 当前的偏移量 + /// @return 分配器本身 + pub fn new(areas: &'static [PhysMemoryArea], offset: usize) -> Self { + Self { + areas, + offset, + phantom: PhantomData, + } + } + // @brief 获取页帧使用情况 + pub fn areas(&self) -> &'static [PhysMemoryArea] { + return self.areas; + } + // @brief 获取当前分配的物理内存的偏移量 + pub fn offset(&self) -> usize { + return self.offset; + } +} + +impl FrameAllocator for BumpAllocator { + /// @brief: 分配count个物理页帧 + /// @param mut self + /// @param count 分配的页帧数量 + /// @return 分配后的物理地址 + unsafe fn allocate(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { + let mut offset = self.offset(); + // 遍历所有的物理内存区域 + for area in self.areas().iter() { + // 将area的base地址与PAGE_SIZE对齐,对齐时向上取整 + // let area_base = (area.base.data() + MMA::PAGE_SHIFT) & !(MMA::PAGE_SHIFT); + let area_base = (area.base.data() + (MMA::PAGE_SIZE - 1)) & !(MMA::PAGE_SIZE - 1); + // 将area的末尾地址与PAGE_SIZE对齐,对齐时向下取整 + // let area_end = (area.base.data() + area.size) & !(MMA::PAGE_SHIFT); + let area_end = (area.base.data() + area.size) & !(MMA::PAGE_SIZE - 1); + + // 如果offset大于area_end,说明当前的物理内存区域已经分配完了,需要跳到下一个物理内存区域 + if offset >= area_end { + continue; + } + + // 如果offset小于area_base ,说明当前的物理内存区域还没有分配过页帧,将offset设置为area_base + if offset < area_base { + offset = area_base; + } else if offset < area_end { + // 将offset对齐到PAGE_SIZE + offset = (offset + (MMA::PAGE_SIZE - 1)) & !(MMA::PAGE_SIZE - 1); + } + // 如果当前offset到area_end的距离大于等于count.data() * PAGE_SIZE,说明当前的物理内存区域足以分配count个页帧 + if offset + count.data() * MMA::PAGE_SIZE <= area_end { + let res_page_phys = offset; + // 将offset增加至分配后的内存 + self.offset = offset + count.data() * MMA::PAGE_SIZE; + + return Some((PhysAddr(res_page_phys), count)); + } + } + return None; + } + + unsafe fn free(&mut self, _address: PhysAddr, _count: PageFrameCount) { + // TODO: 支持释放页帧 + unimplemented!("BumpAllocator::free not implemented"); + } + /// @brief: 获取内存区域页帧的使用情况 + /// @param self + /// @return 页帧的使用情况 + unsafe fn usage(&self) -> PageFrameUsage { + let mut total = 0; + let mut used = 0; + for area in self.areas().iter() { + // 将area的base地址与PAGE_SIZE对齐,对其时向上取整 + let area_base = (area.base.data() + MMA::PAGE_SHIFT) & !(MMA::PAGE_SHIFT); + // 将area的末尾地址与PAGE_SIZE对齐,对其时向下取整 + let area_end = (area.base.data() + area.size) & !(MMA::PAGE_SHIFT); + + total += (area_end - area_base) >> MMA::PAGE_SHIFT; + // 如果offset大于area_end,说明当前物理区域被分配完,都需要加到used中 + if self.offset >= area_end { + used += (area_end - area_base) >> MMA::PAGE_SHIFT; + } else if self.offset < area_base { + // 如果offset小于area_base,说明当前物理区域还没有分配过页帧,都不需要加到used中 + continue; + } else { + used += (self.offset - area_base) >> MMA::PAGE_SHIFT; + } + } + let frame = PageFrameUsage::new(PageFrameCount::new(used), PageFrameCount::new(total)); + return frame; + } +} diff --git a/kernel/src/mm/allocator/kernel_allocator.rs b/kernel/src/mm/allocator/kernel_allocator.rs new file mode 100644 index 00000000..93d66396 --- /dev/null +++ b/kernel/src/mm/allocator/kernel_allocator.rs @@ -0,0 +1,101 @@ +use crate::{ + arch::mm::LockedFrameAllocator, + libs::align::page_align_up, + mm::{MMArch, MemoryManagementArch, VirtAddr}, +}; + +use core::{ + alloc::{AllocError, GlobalAlloc, Layout}, + intrinsics::unlikely, + ptr::NonNull, +}; + +use super::page_frame::{FrameAllocator, PageFrameCount}; + +/// 类kmalloc的分配器应当实现的trait +pub trait LocalAlloc { + unsafe fn local_alloc(&self, layout: Layout) -> *mut u8; + unsafe fn local_alloc_zeroed(&self, layout: Layout) -> *mut u8; + unsafe fn local_dealloc(&self, ptr: *mut u8, layout: Layout); +} + +pub struct KernelAllocator; + +impl KernelAllocator { + unsafe fn alloc_in_buddy(&self, layout: Layout) -> Result, AllocError> { + // 计算需要申请的页数,向上取整 + let count = (page_align_up(layout.size()) / MMArch::PAGE_SIZE).next_power_of_two(); + let page_frame_count = PageFrameCount::new(count); + let (phy_addr, allocated_frame_count) = LockedFrameAllocator + .allocate(page_frame_count) + .ok_or(AllocError)?; + + let virt_addr = unsafe { MMArch::phys_2_virt(phy_addr).ok_or(AllocError)? }; + if unlikely(virt_addr.is_null()) { + return Err(AllocError); + } + + let slice = unsafe { + core::slice::from_raw_parts_mut( + virt_addr.data() as *mut u8, + allocated_frame_count.data() * MMArch::PAGE_SIZE, + ) + }; + return Ok(NonNull::from(slice)); + } + + unsafe fn free_in_buddy(&self, ptr: *mut u8, layout: Layout) { + // 由于buddy分配的页数量是2的幂,因此释放的时候也需要按照2的幂向上取整。 + let count = (page_align_up(layout.size()) / MMArch::PAGE_SIZE).next_power_of_two(); + let page_frame_count = PageFrameCount::new(count); + let phy_addr = MMArch::virt_2_phys(VirtAddr::new(ptr as usize)).unwrap(); + LockedFrameAllocator.free(phy_addr, page_frame_count); + } +} + +/// 为内核SLAB分配器实现LocalAlloc的trait +impl LocalAlloc for KernelAllocator { + unsafe fn local_alloc(&self, layout: Layout) -> *mut u8 { + return self + .alloc_in_buddy(layout) + .map(|x| x.as_mut_ptr() as *mut u8) + .unwrap_or(core::ptr::null_mut() as *mut u8); + } + + unsafe fn local_alloc_zeroed(&self, layout: Layout) -> *mut u8 { + return self + .alloc_in_buddy(layout) + .map(|x| { + let ptr: *mut u8 = x.as_mut_ptr(); + core::ptr::write_bytes(ptr, 0, x.len()); + ptr + }) + .unwrap_or(core::ptr::null_mut() as *mut u8); + } + + unsafe fn local_dealloc(&self, ptr: *mut u8, layout: Layout) { + self.free_in_buddy(ptr, layout); + } +} + +/// 为内核slab分配器实现GlobalAlloc特性 +unsafe impl GlobalAlloc for KernelAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + return self.local_alloc(layout); + // self.local_alloc_zeroed(layout, 0) + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + self.local_alloc_zeroed(layout) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + self.local_dealloc(ptr, layout); + } +} + +/// 内存分配错误处理函数 +#[alloc_error_handler] +pub fn global_alloc_err_handler(layout: Layout) -> ! { + panic!("global_alloc_error, layout: {:?}", layout); +} diff --git a/kernel/src/mm/allocator/mod.rs b/kernel/src/mm/allocator/mod.rs new file mode 100644 index 00000000..88259f4a --- /dev/null +++ b/kernel/src/mm/allocator/mod.rs @@ -0,0 +1,5 @@ +pub mod buddy; +pub mod bump; +pub mod kernel_allocator; +pub mod page_frame; +pub mod slab; diff --git a/kernel/src/mm/allocator/page_frame.rs b/kernel/src/mm/allocator/page_frame.rs new file mode 100644 index 00000000..9be4538e --- /dev/null +++ b/kernel/src/mm/allocator/page_frame.rs @@ -0,0 +1,338 @@ +use core::{ + intrinsics::unlikely, + ops::{Add, AddAssign, Mul, Sub, SubAssign}, +}; + +use crate::{ + arch::{mm::LockedFrameAllocator, MMArch}, + mm::{MemoryManagementArch, PhysAddr, VirtAddr}, +}; + +/// @brief 物理页帧的表示 +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PhysPageFrame { + /// 物理页页号 + number: usize, +} + +#[allow(dead_code)] +impl PhysPageFrame { + pub fn new(paddr: PhysAddr) -> Self { + return Self { + number: paddr.data() / MMArch::PAGE_SIZE, + }; + } + + /// @brief 获取当前页对应的物理地址 + pub fn phys_address(&self) -> PhysAddr { + return PhysAddr::new(self.number * MMArch::PAGE_SIZE); + } + + pub fn next_by(&self, n: usize) -> Self { + return Self { + number: self.number + n, + }; + } + + pub fn next(&self) -> Self { + return self.next_by(1); + } + + /// 构造物理页帧的迭代器,范围为[start, end) + pub fn iter_range(start: Self, end: Self) -> PhysPageFrameIter { + return PhysPageFrameIter::new(start, end); + } +} + +/// @brief 物理页帧的迭代器 +#[derive(Debug)] +pub struct PhysPageFrameIter { + current: PhysPageFrame, + /// 结束的物理页帧(不包含) + end: PhysPageFrame, +} + +impl PhysPageFrameIter { + pub fn new(start: PhysPageFrame, end: PhysPageFrame) -> Self { + return Self { + current: start, + end, + }; + } +} + +impl Iterator for PhysPageFrameIter { + type Item = PhysPageFrame; + + fn next(&mut self) -> Option { + if unlikely(self.current == self.end) { + return None; + } + let current = self.current.next(); + return Some(current); + } +} + +/// 虚拟页帧的表示 +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct VirtPageFrame { + /// 虚拟页页号 + number: usize, +} + +impl VirtPageFrame { + pub fn new(vaddr: VirtAddr) -> Self { + return Self { + number: vaddr.data() / MMArch::PAGE_SIZE, + }; + } + + /// 获取当前虚拟页对应的虚拟地址 + pub fn virt_address(&self) -> VirtAddr { + return VirtAddr::new(self.number * MMArch::PAGE_SIZE); + } + + pub fn next_by(&self, n: usize) -> Self { + return Self { + number: self.number + n, + }; + } + + pub fn next(&self) -> Self { + return self.next_by(1); + } + + /// 构造虚拟页帧的迭代器,范围为[start, end) + pub fn iter_range(start: Self, end: Self) -> VirtPageFrameIter { + return VirtPageFrameIter { + current: start, + end, + }; + } + + pub fn add(&self, n: PageFrameCount) -> Self { + return Self { + number: self.number + n.data(), + }; + } +} + +/// 虚拟页帧的迭代器 +#[derive(Debug)] +pub struct VirtPageFrameIter { + current: VirtPageFrame, + /// 结束的虚拟页帧(不包含) + end: VirtPageFrame, +} + +impl VirtPageFrameIter { + /// @brief 构造虚拟页帧的迭代器,范围为[start, end) + pub fn new(start: VirtPageFrame, end: VirtPageFrame) -> Self { + return Self { + current: start, + end, + }; + } +} + +impl Iterator for VirtPageFrameIter { + type Item = VirtPageFrame; + + fn next(&mut self) -> Option { + if unlikely(self.current == self.end) { + return None; + } + let current: VirtPageFrame = self.current; + self.current = self.current.next_by(1); + return Some(current); + } +} + +/// 页帧使用的数量 +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct PageFrameCount(usize); + +impl PageFrameCount { + // @brief 初始化PageFrameCount + pub const fn new(count: usize) -> Self { + return Self(count); + } + // @brief 获取页帧数量 + pub fn data(&self) -> usize { + return self.0; + } + + /// 计算这一段页帧占用的字节数 + pub fn bytes(&self) -> usize { + return self.0 * MMArch::PAGE_SIZE; + } + + /// 将字节数转换为页帧数量 + /// + /// 如果字节数不是页帧大小的整数倍,则返回None. 否则返回页帧数量 + pub fn from_bytes(bytes: usize) -> Option { + if bytes & MMArch::PAGE_OFFSET_MASK != 0 { + return None; + } else { + return Some(Self(bytes / MMArch::PAGE_SIZE)); + } + } +} + +impl Add for PageFrameCount { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + return Self(self.0 + rhs.0); + } +} + +impl AddAssign for PageFrameCount { + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } +} + +impl Sub for PageFrameCount { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + return Self(self.0 - rhs.0); + } +} + +impl SubAssign for PageFrameCount { + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + } +} + +impl Mul for PageFrameCount { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + return Self(self.0 * rhs.0); + } +} + +impl Add for PageFrameCount { + type Output = Self; + + fn add(self, rhs: usize) -> Self::Output { + return Self(self.0 + rhs); + } +} + +impl AddAssign for PageFrameCount { + fn add_assign(&mut self, rhs: usize) { + self.0 += rhs; + } +} + +impl Sub for PageFrameCount { + type Output = Self; + + fn sub(self, rhs: usize) -> Self::Output { + return Self(self.0 - rhs); + } +} + +impl SubAssign for PageFrameCount { + fn sub_assign(&mut self, rhs: usize) { + self.0 -= rhs; + } +} + +impl Mul for PageFrameCount { + type Output = Self; + + fn mul(self, rhs: usize) -> Self::Output { + return Self(self.0 * rhs); + } +} + +// 页帧使用情况 +#[derive(Debug)] +pub struct PageFrameUsage { + used: PageFrameCount, + total: PageFrameCount, +} + +#[allow(dead_code)] +impl PageFrameUsage { + /// @brief: 初始化FrameUsage + /// @param PageFrameCount used 已使用的页帧数量 + /// @param PageFrameCount total 总的页帧数量 + pub fn new(used: PageFrameCount, total: PageFrameCount) -> Self { + return Self { used, total }; + } + // @brief 获取已使用的页帧数量 + pub fn used(&self) -> PageFrameCount { + return self.used; + } + // @brief 获取空闲的页帧数量 + pub fn free(&self) -> PageFrameCount { + return PageFrameCount(self.total.0 - self.used.0); + } + // @brief 获取总的页帧数量 + pub fn total(&self) -> PageFrameCount { + return self.total; + } +} + +/// 能够分配页帧的分配器需要实现的trait +pub trait FrameAllocator { + // @brief 分配count个页帧 + unsafe fn allocate(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)>; + + // @brief 通过地址释放count个页帧 + unsafe fn free(&mut self, address: PhysAddr, count: PageFrameCount); + // @brief 分配一个页帧 + unsafe fn allocate_one(&mut self) -> Option { + return self.allocate(PageFrameCount::new(1)).map(|(addr, _)| addr); + } + // @brief 通过地址释放一个页帧 + unsafe fn free_one(&mut self, address: PhysAddr) { + return self.free(address, PageFrameCount::new(1)); + } + // @brief 获取页帧使用情况 + unsafe fn usage(&self) -> PageFrameUsage; +} + +/// @brief 通过一个 &mut T 的引用来对一个实现了 FrameAllocator trait 的类型进行调用,使代码更加灵活 +impl FrameAllocator for &mut T { + unsafe fn allocate(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { + return T::allocate(self, count); + } + unsafe fn free(&mut self, address: PhysAddr, count: PageFrameCount) { + return T::free(self, address, count); + } + unsafe fn allocate_one(&mut self) -> Option { + return T::allocate_one(self); + } + unsafe fn free_one(&mut self, address: PhysAddr) { + return T::free_one(self, address); + } + unsafe fn usage(&self) -> PageFrameUsage { + return T::usage(self); + } +} + +/// @brief 从全局的页帧分配器中分配连续count个页帧 +/// +/// @param count 请求分配的页帧数量 +pub unsafe fn allocate_page_frames(count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { + let frame = unsafe { LockedFrameAllocator.allocate(count)? }; + return Some(frame); +} + +/// @brief 向全局页帧分配器释放连续count个页帧 +/// +/// @param frame 要释放的第一个页帧 +/// @param count 要释放的页帧数量 (必须是2的n次幂) +pub unsafe fn deallocate_page_frames(frame: PhysPageFrame, count: PageFrameCount) { + unsafe { + LockedFrameAllocator.free(frame.phys_address(), count); + } +} diff --git a/kernel/src/mm/allocator/slab.rs b/kernel/src/mm/allocator/slab.rs new file mode 100644 index 00000000..7b461b0b --- /dev/null +++ b/kernel/src/mm/allocator/slab.rs @@ -0,0 +1,123 @@ +//! 当前slab分配器暂时不使用,等待后续完善后合并主线 +#![allow(dead_code)] + +use core::alloc::Layout; + +// 定义Slab,用来存放空闲块 +pub struct Slab { + block_size: usize, + free_block_list: FreeBlockList, +} + +impl Slab { + /// @brief: 初始化一个slab + /// @param {usize} start_addr + /// @param {usize} slab_size + /// @param {usize} block_size + pub unsafe fn new(start_addr: usize, slab_size: usize, block_size: usize) -> Slab { + let blocks_num = slab_size / block_size; + return Slab { + block_size: block_size, + free_block_list: FreeBlockList::new(start_addr, block_size, blocks_num), + }; + } + + /// @brief: 获取slab中可用的block数 + pub fn used_blocks(&self) -> usize { + return self.free_block_list.len(); + } + + /// @brief: 扩大free_block_list + /// @param {*} mut + /// @param {usize} start_addr + /// @param {usize} slab_size + pub fn grow(&mut self, start_addr: usize, slab_size: usize) { + let num_of_blocks = slab_size / self.block_size; + let mut block_list = + unsafe { FreeBlockList::new(start_addr, self.block_size, num_of_blocks) }; + // 将新链表接到原链表的后面 + while let Some(block) = block_list.pop() { + self.free_block_list.push(block); + } + } + /// @brief: 从slab中分配一个block + /// @return 分配的内存地址 + pub fn allocate(&mut self, _layout: Layout) -> Option<*mut u8> { + match self.free_block_list.pop() { + Some(block) => return Some(block.addr() as *mut u8), + None => return None, + } + } + /// @brief: 将block归还给slab + pub fn free(&mut self, ptr: *mut u8) { + let ptr = ptr as *mut FreeBlock; + unsafe { + self.free_block_list.push(&mut *ptr); + } + } +} +/// slab中的空闲块 +struct FreeBlockList { + len: usize, + head: Option<&'static mut FreeBlock>, +} + +impl FreeBlockList { + unsafe fn new(start_addr: usize, block_size: usize, num_of_blocks: usize) -> FreeBlockList { + let mut new_list = FreeBlockList::new_empty(); + for i in (0..num_of_blocks).rev() { + // 从后往前分配,避免内存碎片 + let new_block = (start_addr + i * block_size) as *mut FreeBlock; + new_list.push(&mut *new_block); + } + return new_list; + } + + fn new_empty() -> FreeBlockList { + return FreeBlockList { len: 0, head: None }; + } + + fn len(&self) -> usize { + return self.len; + } + + /// @brief: 将空闲块从链表中弹出 + fn pop(&mut self) -> Option<&'static mut FreeBlock> { + // 从链表中弹出一个空闲块 + let block = self.head.take().map(|node| { + self.head = node.next.take(); + self.len -= 1; + node + }); + return block; + } + + /// @brief: 将空闲块压入链表 + fn push(&mut self, free_block: &'static mut FreeBlock) { + free_block.next = self.head.take(); + self.len += 1; + self.head = Some(free_block); + } + + fn is_empty(&self) -> bool { + return self.head.is_none(); + } +} + +impl Drop for FreeBlockList { + fn drop(&mut self) { + while let Some(_) = self.pop() {} + } +} + +struct FreeBlock { + next: Option<&'static mut FreeBlock>, +} + +impl FreeBlock { + /// @brief: 获取FreeBlock的地址 + /// @return {*} + fn addr(&self) -> usize { + return self as *const _ as usize; + } +} diff --git a/kernel/src/mm/c_adapter.rs b/kernel/src/mm/c_adapter.rs new file mode 100644 index 00000000..de98900b --- /dev/null +++ b/kernel/src/mm/c_adapter.rs @@ -0,0 +1,135 @@ +//! 这是暴露给C的接口,用于在C语言中使用Rust的内存分配器。 + +use core::intrinsics::unlikely; + +use alloc::vec::Vec; +use hashbrown::HashMap; + +use crate::{ + arch::mm::LowAddressRemapping, + include::bindings::bindings::{gfp_t, PAGE_U_S}, + kerror, + libs::{align::page_align_up, spinlock::SpinLock}, + mm::MMArch, + syscall::SystemError, +}; + +use super::{ + allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, no_init::pseudo_map_phys, + page::PageFlags, MemoryManagementArch, PhysAddr, VirtAddr, +}; + +lazy_static! { + // 用于记录内核分配给C的空间信息 + static ref C_ALLOCATION_MAP: SpinLock> = SpinLock::new(HashMap::new()); +} + +/// [EXTERN TO C] Use pseudo mapper to map physical memory to virtual memory. +#[no_mangle] +pub unsafe extern "C" fn rs_pseudo_map_phys(vaddr: usize, paddr: usize, size: usize) { + let vaddr = VirtAddr::new(vaddr); + let paddr = PhysAddr::new(paddr); + let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); + pseudo_map_phys(vaddr, paddr, count); +} + +/// [EXTERN TO C] Use kernel mapper to map physical memory to virtual memory. +#[no_mangle] +pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, flags: usize) { + let mut vaddr = VirtAddr::new(vaddr); + let mut paddr = PhysAddr::new(paddr); + let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); + // kdebug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); + + let mut page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); + if flags & PAGE_U_S as usize != 0 { + page_flags = page_flags.set_user(true); + } + + let mut kernel_mapper = KernelMapper::lock(); + let mut kernel_mapper = kernel_mapper.as_mut(); + assert!(kernel_mapper.is_some()); + for _i in 0..count.data() { + let flusher = kernel_mapper + .as_mut() + .unwrap() + .map_phys(vaddr, paddr, page_flags) + .unwrap(); + + flusher.flush(); + + vaddr += MMArch::PAGE_SIZE; + paddr += MMArch::PAGE_SIZE; + } +} + +#[no_mangle] +pub unsafe extern "C" fn kzalloc(size: usize, _gfp: gfp_t) -> usize { + // kdebug!("kzalloc: size: {size}"); + return do_kmalloc(size, true); +} + +#[no_mangle] +pub unsafe extern "C" fn kmalloc(size: usize, _gfp: gfp_t) -> usize { + // kdebug!("kmalloc: size: {size}"); + // 由于C代码不规范,因此都全部清空 + return do_kmalloc(size, true); +} + +fn do_kmalloc(size: usize, zero: bool) -> usize { + let space: Vec = if zero { + vec![0u8; size] + } else { + let mut v = Vec::with_capacity(size); + unsafe { + v.set_len(size); + } + v + }; + + assert!(space.len() == size); + let (ptr, len, cap) = space.into_raw_parts(); + if !ptr.is_null() { + let vaddr = VirtAddr::new(ptr as usize); + let len = len as usize; + let cap = cap as usize; + let mut guard = C_ALLOCATION_MAP.lock(); + if unlikely(guard.contains_key(&vaddr)) { + drop(guard); + unsafe { + drop(Vec::from_raw_parts(vaddr.data() as *mut u8, len, cap)); + } + panic!( + "do_kmalloc: vaddr {:?} already exists in C Allocation Map, query size: {size}, zero: {zero}", + vaddr + ); + } + // 插入到C Allocation Map中 + guard.insert(vaddr, (vaddr, len, cap)); + return vaddr.data(); + } else { + return SystemError::ENOMEM.to_posix_errno() as i64 as usize; + } +} + +#[no_mangle] +pub unsafe extern "C" fn kfree(vaddr: usize) -> usize { + let vaddr = VirtAddr::new(vaddr); + let mut guard = C_ALLOCATION_MAP.lock(); + let p = guard.remove(&vaddr); + drop(guard); + + if p.is_none() { + kerror!("kfree: vaddr {:?} not found in C Allocation Map", vaddr); + return SystemError::EINVAL.to_posix_errno() as i64 as usize; + } + let (vaddr, len, cap) = p.unwrap(); + drop(Vec::from_raw_parts(vaddr.data() as *mut u8, len, cap)); + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_unmap_at_low_addr() -> usize { + LowAddressRemapping::unmap_at_low_address(true); + return 0; +} diff --git a/kernel/src/mm/internal.h b/kernel/src/mm/internal.h deleted file mode 100644 index f50902d2..00000000 --- a/kernel/src/mm/internal.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "mm.h" - - -// 当vma被成功合并后的返回值 -#define __VMA_MERGED 1 - -/** - * @brief 将vma结构体插入mm_struct的链表之中 - * - * @param mm 内存空间分布结构体 - * @param vma 待插入的VMA结构体 - * @param prev 链表的前一个结点 - */ -void __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev); - -/** - * @brief 将vma给定结构体从vma链表的结点之中删除 - * - * @param mm 内存空间分布结构体 - * @param vma 待插入的VMA结构体 - */ -void __vma_unlink_list(struct mm_struct *mm, struct vm_area_struct *vma); - -/** - * @brief 获取指定虚拟地址处映射的物理地址 - * - * @param mm 内存空间分布结构体 - * @param vaddr 虚拟地址 - * @return uint64_t 已映射的物理地址 - */ -uint64_t __mm_get_paddr(struct mm_struct *mm, uint64_t vaddr); - -/** - * @brief 创建anon_vma,并将其与页面结构体进行绑定 - * 若提供的页面结构体指针为NULL,则只创建,不绑定 - * - * @param page 页面结构体的指针 - * @param lock_page 是否将页面结构体加锁 - * @return struct anon_vma_t* 创建好的anon_vma - */ -struct anon_vma_t *__anon_vma_create_alloc(struct Page *page, bool lock_page); - -/** - * @brief 释放anon vma结构体 - * - * @param anon_vma 待释放的anon_vma结构体 - * @return int 返回码 - */ -int __anon_vma_free(struct anon_vma_t *anon_vma); - -/** - * @brief 将指定的vma加入到anon_vma的管理范围之中 - * - * @param anon_vma 页面的anon_vma - * @param vma 待加入的vma - * @return int 返回码 - */ -int __anon_vma_add(struct anon_vma_t *anon_vma, struct vm_area_struct *vma); - -/** - * @brief 从anon_vma的管理范围中删除指定的vma - * (在进入这个函数之前,应该要对anon_vma加锁) - * @param vma 将要取消对应的anon_vma管理的vma结构体 - * @return int 返回码 - */ -int __anon_vma_del(struct vm_area_struct *vma); - -/** - * @brief 创建mmio对应的页结构体 - * - * @param paddr 物理地址 - * @return struct Page* 创建成功的page - */ -struct Page* __create_mmio_page_struct(uint64_t paddr); - -// 判断给定的两个值是否跨越了2M边界 -#define CROSS_2M_BOUND(val1, val2) ((val1 & PAGE_2M_MASK) != (val2 & PAGE_2M_MASK)) \ No newline at end of file diff --git a/kernel/src/mm/kernel_mapper.rs b/kernel/src/mm/kernel_mapper.rs new file mode 100644 index 00000000..2aa525a7 --- /dev/null +++ b/kernel/src/mm/kernel_mapper.rs @@ -0,0 +1,145 @@ +use super::{page::PageFlags, PageTableKind, PhysAddr, VirtAddr}; +use crate::{ + arch::{ + asm::irqflags::{local_irq_restore, local_irq_save}, + mm::{LockedFrameAllocator, PageMapper}, + }, + libs::align::page_align_up, + mm::allocator::page_frame::PageFrameCount, + mm::{MMArch, MemoryManagementArch}, + smp::core::smp_get_processor_id, + syscall::SystemError, +}; +use core::{ + ops::Deref, + sync::atomic::{compiler_fence, AtomicUsize, Ordering}, +}; + +/// 标志当前没有处理器持有内核映射器的锁 +/// 之所以需要这个标志,是因为AtomicUsize::new(0)会把0当作一个处理器的id +const KERNEL_MAPPER_NO_PROCESSOR: usize = !0; +/// 当前持有内核映射器锁的处理器 +static KERNEL_MAPPER_LOCK_OWNER: AtomicUsize = AtomicUsize::new(KERNEL_MAPPER_NO_PROCESSOR); +/// 内核映射器的锁计数器 +static KERNEL_MAPPER_LOCK_COUNT: AtomicUsize = AtomicUsize::new(0); + +pub struct KernelMapper { + /// 内核空间映射器 + mapper: PageMapper, + /// 标记当前映射器是否为只读 + readonly: bool, +} + +impl KernelMapper { + fn lock_cpu(cpuid: usize, mapper: PageMapper) -> Self { + loop { + match KERNEL_MAPPER_LOCK_OWNER.compare_exchange_weak( + KERNEL_MAPPER_NO_PROCESSOR, + cpuid, + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(_) => break, + // 当前处理器已经持有了锁 + Err(id) if id == cpuid => break, + // either CAS failed, or some other hardware thread holds the lock + Err(_) => core::hint::spin_loop(), + } + } + + let prev_count = KERNEL_MAPPER_LOCK_COUNT.fetch_add(1, Ordering::Relaxed); + compiler_fence(Ordering::Acquire); + + // 本地核心已经持有过锁,因此标记当前加锁获得的映射器为只读 + let readonly = prev_count > 0; + + return Self { mapper, readonly }; + } + + /// @brief 锁定内核映射器, 并返回一个内核映射器对象 + #[inline(always)] + pub fn lock() -> Self { + let cpuid = smp_get_processor_id() as usize; + let mapper = unsafe { PageMapper::current(PageTableKind::Kernel, LockedFrameAllocator) }; + return Self::lock_cpu(cpuid, mapper); + } + + /// @brief 获取内核映射器的page mapper的可变引用。如果当前映射器为只读,则返回 None + #[inline(always)] + pub fn as_mut(&mut self) -> Option<&mut PageMapper> { + if self.readonly { + return None; + } else { + return Some(&mut self.mapper); + } + } + + /// @brief 获取内核映射器的page mapper的不可变引用 + #[inline(always)] + pub fn as_ref(&self) -> &PageMapper { + return &self.mapper; + } + + /// 映射一段物理地址到指定的虚拟地址。 + /// + /// ## 参数 + /// + /// - `vaddr`: 要映射的虚拟地址 + /// - `paddr`: 要映射的物理地址 + /// - `size`: 要映射的大小(字节,必须是页大小的整数倍,否则会向上取整) + /// - `flags`: 页面标志 + /// - `flush`: 是否刷新TLB + /// + /// ## 返回 + /// + /// - 成功:返回Ok(()) + /// - 失败: 如果当前映射器为只读,则返回EAGAIN_OR_EWOULDBLOCK + pub unsafe fn map_phys_with_size( + &mut self, + mut vaddr: VirtAddr, + mut paddr: PhysAddr, + size: usize, + flags: PageFlags, + flush: bool, + ) -> Result<(), SystemError> { + if self.readonly { + return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); + } + + let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); + // kdebug!("kernel mapper: map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); + + for _ in 0..count.data() { + let flusher = self.mapper.map_phys(vaddr, paddr, flags).unwrap(); + + if flush { + flusher.flush(); + } + + vaddr += MMArch::PAGE_SIZE; + paddr += MMArch::PAGE_SIZE; + } + return Ok(()); + } +} + +impl Drop for KernelMapper { + fn drop(&mut self) { + // 为了防止fetch_sub和store之间,由于中断,导致store错误清除了owner,导致错误,因此需要关中断。 + let flags = local_irq_save(); + let prev_count = KERNEL_MAPPER_LOCK_COUNT.fetch_sub(1, Ordering::Relaxed); + if prev_count == 1 { + KERNEL_MAPPER_LOCK_OWNER.store(KERNEL_MAPPER_NO_PROCESSOR, Ordering::Release); + } + local_irq_restore(flags); + compiler_fence(Ordering::Release); + } +} + +impl Deref for KernelMapper { + type Target = PageMapper; + + fn deref(&self) -> &Self::Target { + return self.as_ref(); + } +} diff --git a/kernel/src/mm/mm-stat.c b/kernel/src/mm/mm-stat.c deleted file mode 100644 index 43856b65..00000000 --- a/kernel/src/mm/mm-stat.c +++ /dev/null @@ -1,196 +0,0 @@ -/** - * @file mm-stat.c - * @author longjin(longjin@RinGoTek.cn) - * @brief 查询内存信息 - * @version 0.1 - * @date 2022-08-06 - * - * @copyright Copyright (c) 2022 - * - */ - -#include "mm.h" -#include "slab.h" -#include -#include - -extern const struct slab kmalloc_cache_group[16]; - -static int __empty_2m_pages(int zone); -static int __count_in_using_2m_pages(int zone); -static uint64_t __count_kmalloc_free(); -static uint64_t __count_kmalloc_using(); -static uint64_t __count_kmalloc_total(); -uint64_t sys_mm_stat(struct pt_regs *regs); - -/** - * @brief 获取指定zone中的空闲2M页的数量 - * - * @param zone 内存zone号 - * @return int 空闲2M页数量 - */ -static int __count_empty_2m_pages(int zone) -{ - int zone_start = 0, zone_end = 0; - - uint64_t attr = 0; - switch (zone) - { - case ZONE_DMA: - // DMA区域 - zone_start = 0; - zone_end = ZONE_DMA_INDEX; - attr |= PAGE_PGT_MAPPED; - break; - case ZONE_NORMAL: - zone_start = ZONE_DMA_INDEX; - zone_end = ZONE_NORMAL_INDEX; - attr |= PAGE_PGT_MAPPED; - break; - case ZONE_UNMAPPED_IN_PGT: - zone_start = ZONE_NORMAL_INDEX; - zone_end = ZONE_UNMAPPED_INDEX; - attr = 0; - break; - default: - kerror("In __count_empty_2m_pages: param: zone invalid."); - // 返回错误码 - return -EINVAL; - break; - } - - uint64_t result = 0; - for (int i = zone_start; i <= zone_end; ++i) - { - result += (memory_management_struct.zones_struct + i)->count_pages_free; - } - return result; -} - -/** - * @brief 获取指定zone中的正在使用的2M页的数量 - * - * @param zone 内存zone号 - * @return int 空闲2M页数量 - */ -static int __count_in_using_2m_pages(int zone) -{ - int zone_start = 0, zone_end = 0; - - uint64_t attr = 0; - switch (zone) - { - case ZONE_DMA: - // DMA区域 - zone_start = 0; - zone_end = ZONE_DMA_INDEX; - attr |= PAGE_PGT_MAPPED; - break; - case ZONE_NORMAL: - zone_start = ZONE_DMA_INDEX; - zone_end = ZONE_NORMAL_INDEX; - attr |= PAGE_PGT_MAPPED; - break; - case ZONE_UNMAPPED_IN_PGT: - zone_start = ZONE_NORMAL_INDEX; - zone_end = ZONE_UNMAPPED_INDEX; - attr = 0; - break; - default: - kerror("In __count_in_using_2m_pages: param: zone invalid."); - // 返回错误码 - return -EINVAL; - break; - } - - uint64_t result = 0; - for (int i = zone_start; i <= zone_end; ++i) - { - result += (memory_management_struct.zones_struct + i)->count_pages_using; - } - return result; -} - -/** - * @brief 计算kmalloc缓冲区中的空闲内存 - * - * @return uint64_t 空闲内存(字节) - */ -static uint64_t __count_kmalloc_free() -{ - uint64_t result = 0; - for (int i = 0; i < sizeof(kmalloc_cache_group) / sizeof(struct slab); ++i) - { - result += kmalloc_cache_group[i].size * kmalloc_cache_group[i].count_total_free; - } - return result; -} - -/** - * @brief 计算kmalloc缓冲区中已使用的内存 - * - * @return uint64_t 已使用的内存(字节) - */ -static uint64_t __count_kmalloc_using() -{ - uint64_t result = 0; - for (int i = 0; i < sizeof(kmalloc_cache_group) / sizeof(struct slab); ++i) - { - result += kmalloc_cache_group[i].size * kmalloc_cache_group[i].count_total_using; - } - return result; -} - -/** - * @brief 计算kmalloc缓冲区中总共占用的内存 - * - * @return uint64_t 缓冲区占用的内存(字节) - */ -static uint64_t __count_kmalloc_total() -{ - uint64_t result = 0; - for (int i = 0; i < sizeof(kmalloc_cache_group) / sizeof(struct slab); ++i) - { - result += kmalloc_cache_group[i].size * - (kmalloc_cache_group[i].count_total_free + kmalloc_cache_group[i].count_total_using); - } - return result; -} - -/** - * @brief 获取系统当前的内存信息(未上锁,不一定精准) - * - * @return struct mm_stat_t 内存信息结构体 - */ -struct mm_stat_t mm_stat() -{ - struct mm_stat_t tmp = {0}; - // 统计物理页的信息 - tmp.used = __count_in_using_2m_pages(ZONE_NORMAL) * PAGE_2M_SIZE; - tmp.free = __count_empty_2m_pages(ZONE_NORMAL) * PAGE_2M_SIZE; - tmp.total = tmp.used + tmp.free; - tmp.shared = 0; - // 统计kmalloc slab中的信息 - tmp.cache_free = __count_kmalloc_free(); - tmp.cache_used = __count_kmalloc_using(); - tmp.available = tmp.free + tmp.cache_free; - return tmp; -} - -/** - * @brief 获取内存信息的系统调用 - * - * @param r8 返回的内存信息结构体的地址 - * @return uint64_t - */ -uint64_t sys_do_mstat(struct mm_stat_t *dst, bool from_user) -{ - if (dst == NULL) - return -EFAULT; - struct mm_stat_t stat = mm_stat(); - if (from_user) - copy_to_user((void *)dst, &stat, sizeof(struct mm_stat_t)); - else - memcpy((void *)dst, &stat, sizeof(struct mm_stat_t)); - return 0; -} \ No newline at end of file diff --git a/kernel/src/mm/mm-types.h b/kernel/src/mm/mm-types.h index 96891944..6799086b 100644 --- a/kernel/src/mm/mm-types.h +++ b/kernel/src/mm/mm-types.h @@ -4,179 +4,4 @@ #include #include -struct mm_struct; -struct anon_vma_t; typedef uint64_t vm_flags_t; - -/** - * @brief 内存页表结构体 - * - */ -typedef struct -{ - unsigned long pml4t; -} pml4t_t; - -typedef struct -{ - unsigned long pdpt; -} pdpt_t; - -typedef struct -{ - unsigned long pdt; -} pdt_t; - -typedef struct -{ - unsigned long pt; -} pt_t; - -// Address Range Descriptor Structure 地址范围描述符 -struct ARDS -{ - 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, rodata_end; // 内核程序数据段结束地址、 内核程序只读段结束地址 - uint64_t start_brk; // 堆地址的起始位置 - - 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; - - struct anon_vma_t *anon_vma; // 本页对应的anon_vma - - spinlock_t op_lock; // 页面操作锁 - -}; - -/** - * @brief 虚拟内存区域(VMA)结构体 - * - */ -struct vm_area_struct -{ - struct vm_area_struct *vm_prev, *vm_next; - - // 虚拟内存区域的范围是一个左闭右开的区间:[vm_start, vm_end) - uint64_t vm_start; // 区域的起始地址 - uint64_t vm_end; // 区域的结束地址 - struct mm_struct *vm_mm; // 虚拟内存区域对应的mm结构体 - vm_flags_t vm_flags; // 虚拟内存区域的标志位, 具体可选值请见mm.h - - - struct List anon_vma_list; // anon_vma的链表结点 - struct anon_vma_t * anon_vma; // 属于的anon_vma - - struct vm_operations_t *vm_ops; // 操作方法 - atomic_t ref_count; // 引用计数 - pgoff_t page_offset; // 起始地址在当前VMA所占的2M物理页中的偏移量 - void *private_data; -}; - -/** - * @brief 内存空间分布结构体 - * 包含了进程内存空间分布的信息 - */ -struct mm_struct -{ - pml4t_t *pgd; // 内存页表指针 - struct vm_area_struct *vmas; // VMA列表 - // 代码段空间 - uint64_t code_addr_start, code_addr_end; - // 数据段空间 - uint64_t data_addr_start, data_addr_end; - // 只读数据段空间 - uint64_t rodata_addr_start, rodata_addr_end; - // BSS段的空间 - uint64_t bss_start, bss_end; - // 动态内存分配区(堆区域) - uint64_t brk_start, brk_end; - // 应用层栈基地址 - uint64_t stack_start; -}; - -/** - * @brief 匿名vma对象的结构体 - * - * anon_vma与每个内存页结构体进行一对一绑定 - * anon_vma也连接着一切使用到该内存页的vma,当发生页面换出时,应当更新与该page相关的所有vma在页表中的映射信息。 - */ -struct anon_vma_t -{ - // anon vma的操作信号量 - semaphore_t sem; - - /** - * 记录当前有多少个vma与该anon_vma关联,当vma被释放时, - * 应当检查这个值。当该值为0时,应当释放anon_vma结构体 - */ - atomic_t ref_count; - - // todo: 把下面的循环链表更换成红黑树 - // 与当前anon_vma相关的vma的列表 - struct List vma_list; - // 当前anon vma对应的page - struct Page* page; -}; \ No newline at end of file diff --git a/kernel/src/mm/mm.c b/kernel/src/mm/mm.c deleted file mode 100644 index b4452266..00000000 --- a/kernel/src/mm/mm.c +++ /dev/null @@ -1,686 +0,0 @@ -#include "mm.h" -#include "mm-types.h" -#include "mmio.h" -#include "slab.h" -#include -#include -#include -#include -#include -#include -#include - -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}; - -/** - * @brief 从页表中获取pdt页表项的内容 - * - * @param proc_page_table_addr 页表的地址 - * @param is_phys 页表地址是否为物理地址 - * @param virt_addr_start 要清除的虚拟地址的起始地址 - * @param length 要清除的区域的长度 - * @param clear 是否清除标志位 - */ -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..."); - // 设置内核程序不同部分的起止地址 - 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.rodata_end = (ul)&_erodata; - memory_management_struct.start_brk = (ul)&_end; - - struct multiboot_mmap_entry_t mb2_mem_info[512]; - int count; - - multiboot2_iter(multiboot2_get_memory, mb2_mem_info, &count); - io_mfence(); - for (int i = 0; i < count; ++i) - { - io_mfence(); - // 可用的内存 - if (mb2_mem_info->type == 1) - mm_Total_Memory += mb2_mem_info->len; - - // kdebug("[i=%d] mb2_mem_info[i].type=%d, mb2_mem_info[i].addr=%#018lx", i, mb2_mem_info[i].type, mb2_mem_info[i].addr); - // 保存信息到mms - memory_management_struct.e820[i].BaseAddr = mb2_mem_info[i].addr; - memory_management_struct.e820[i].Length = mb2_mem_info[i].len; - memory_management_struct.e820[i].type = mb2_mem_info[i].type; - memory_management_struct.len_e820 = i; - - // 脏数据 - if (mb2_mem_info[i].type > 4 || mb2_mem_info[i].len == 0 || mb2_mem_info[i].type < 1) - break; - } - printk("[ INFO ] Total amounts of RAM : %ld bytes\n", mm_Total_Memory); - - // 计算有效内存页数 - io_mfence(); - for (int i = 0; i < memory_management_struct.len_e820; ++i) - { - if (memory_management_struct.e820[i].type != 1) - continue; - io_mfence(); - // 将内存段的起始物理地址按照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; - io_mfence(); - mm_total_2M_pages += ((addr_end - addr_start) >> PAGE_2M_SHIFT); - } - kinfo("Total amounts of 2M pages : %ld.", mm_total_2M_pages); - - // 物理地址空间的最大地址(包含了物理内存、内存空洞、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对齐的上边界(防止修改了别的数据) - io_mfence(); - memory_management_struct.bmp = (unsigned long *)((memory_management_struct.start_brk + 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变量组成 - io_mfence(); - - // 初始化bitmap, 先将整个bmp空间全部置位。稍后再将可用物理内存页复位。 - memset(memory_management_struct.bmp, 0xff, memory_management_struct.bmp_len); - io_mfence(); - // 初始化内存页结构 - // 将页结构映射于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 - - io_mfence(); - // 初始化内存区域 - 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); - io_mfence(); - // 由于暂时无法计算zone结构体的数量,因此先将其设为0 - memory_management_struct.count_zones = 0; - io_mfence(); - // zones-struct 成员变量暂时按照5个来计算 - memory_management_struct.zones_struct_len = (10 * sizeof(struct Zone) + sizeof(ul) - 1) & (~(sizeof(ul) - 1)); - io_mfence(); - 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) - { - io_mfence(); - 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]。 - io_mfence(); - memory_management_struct.pages_struct->zone = memory_management_struct.zones_struct; - memory_management_struct.pages_struct->addr_phys = 0UL; - set_page_attr(memory_management_struct.pages_struct, PAGE_PGT_MAPPED | PAGE_KERNEL_INIT | PAGE_KERNEL); - memory_management_struct.pages_struct->ref_counts = 1; - memory_management_struct.pages_struct->age = 0; - // 将第0页的标志位给置上 - //*(memory_management_struct.bmp) |= 1UL; - - // 计算zone结构体的总长度(按照64位对齐) - memory_management_struct.zones_struct_len = (memory_management_struct.count_zones * sizeof(struct Zone) + sizeof(ul) - 1) & (~(sizeof(ul) - 1)); - - ZONE_DMA_INDEX = 0; - ZONE_NORMAL_INDEX = memory_management_struct.count_zones ; - ZONE_UNMAPPED_INDEX = 0; - - //kdebug("ZONE_DMA_INDEX=%d\tZONE_NORMAL_INDEX=%d\tZONE_UNMAPPED_INDEX=%d", ZONE_DMA_INDEX, ZONE_NORMAL_INDEX, ZONE_UNMAPPED_INDEX); - // 设置内存页管理结构的地址,预留了一段空间,防止内存越界。 - 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)); - - // 初始化内存管理单元结构所占的物理页的结构体 - ul mms_max_page = (virt_2_phys(memory_management_struct.end_of_struct) >> PAGE_2M_SHIFT); // 内存管理单元所占据的序号最大的物理页 - // kdebug("mms_max_page=%ld", mms_max_page); - - struct Page *tmp_page = NULL; - ul page_num; - // 第0个page已经在上方配置 - for (ul j = 1; j <= mms_max_page; ++j) - { - barrier(); - tmp_page = memory_management_struct.pages_struct + j; - page_init(tmp_page, PAGE_PGT_MAPPED | PAGE_KERNEL | PAGE_KERNEL_INIT); - barrier(); - page_num = tmp_page->addr_phys >> PAGE_2M_SHIFT; - *(memory_management_struct.bmp + (page_num >> 6)) |= (1UL << (page_num % 64)); - ++tmp_page->zone->count_pages_using; - --tmp_page->zone->count_pages_free; - } - - kinfo("Memory management unit initialize complete!"); - - flush_tlb(); - // todo: 在这里增加代码,暂时停止视频输出,否则可能会导致图像数据写入slab的区域,从而造成异常 - // 初始化slab内存池 - slab_init(); - page_table_init(); - - initial_mm.pgd = (pml4t_t *)get_CR3(); - - initial_mm.code_addr_start = memory_management_struct.kernel_code_start; - initial_mm.code_addr_end = memory_management_struct.kernel_code_end; - - initial_mm.data_addr_start = (ul)&_data; - initial_mm.data_addr_end = memory_management_struct.kernel_data_end; - - initial_mm.rodata_addr_start = (ul)&_rodata; - initial_mm.rodata_addr_end = (ul)&_erodata; - initial_mm.bss_start = (uint64_t)&_bss; - initial_mm.bss_end = (uint64_t)&_ebss; - - initial_mm.brk_start = memory_management_struct.start_brk; - initial_mm.brk_end = current_pcb->addr_limit; - - initial_mm.stack_start = _stack_start; - initial_mm.vmas = NULL; - - - - mmio_init(); -} - -/** - * @brief 初始化内存页 - * - * @param page 内存页结构体 - * @param flags 标志位 - * 本函数只负责初始化内存页,允许对同一页面进行多次初始化 - * 而维护计数器及置位bmp标志位的功能,应当在分配页面的时候手动完成 - * @return unsigned long - */ -unsigned long page_init(struct Page *page, ul flags) -{ - page->attr |= flags; - // 若页面的引用计数为0或是共享页,增加引用计数 - if ((!page->ref_counts) || (page->attr & PAGE_SHARED)) - { - ++page->ref_counts; - barrier(); - if (page->zone) - ++page->zone->total_pages_link; - } - page->anon_vma = NULL; - spin_init(&(page->op_lock)); - return 0; -} - -/** - * @brief 从已初始化的页结构中搜索符合申请条件的、连续num个struct page - * - * @param zone_select 选择内存区域, 可选项:dma, mapped in pgt(normal), 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; - if (num >= 64 && num <= 0) - { - kerror("alloc_pages(): num is invalid."); - return NULL; - } - - ul attr = flags; - switch (zone_select) - { - case ZONE_DMA: - // DMA区域 - zone_start = 0; - zone_end = ZONE_DMA_INDEX; - attr |= PAGE_PGT_MAPPED; - break; - case ZONE_NORMAL: - zone_start = ZONE_DMA_INDEX; - zone_end = ZONE_NORMAL_INDEX; - attr |= PAGE_PGT_MAPPED; - break; - case ZONE_UNMAPPED_IN_PGT: - zone_start = ZONE_NORMAL_INDEX; - zone_end = ZONE_UNMAPPED_INDEX; - attr = 0; - break; - - default: - kerror("In alloc_pages: param: zone_select incorrect."); - // 返回空 - 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 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; - ul tmp_num = ((1UL << num) - 1); - for (ul k = shift; k < 64; ++k) - { - // 寻找连续num个空页 - if (!((k ? ((*p >> k) | (*(p + 1) << (64 - k))) : *p) & tmp_num)) - - { - ul start_page_num = j + k - shift; // 计算得到要开始获取的内存页的页号 - for (ul l = 0; l < num; ++l) - { - struct Page *x = memory_management_struct.pages_struct + start_page_num + l; - - // 分配页面,手动配置属性及计数器 - // 置位bmp - *(memory_management_struct.bmp + ((x->addr_phys >> PAGE_2M_SHIFT) >> 6)) |= (1UL << (x->addr_phys >> PAGE_2M_SHIFT) % 64); - ++(z->count_pages_using); - --(z->count_pages_free); - page_init(x, attr); - } - // 成功分配了页面,返回第一个页面的指针 - // kwarn("start page num=%d\n", start_page_num); - return (struct Page *)(memory_management_struct.pages_struct + start_page_num); - } - } - } - } - kBUG("Cannot alloc page, ZONE=%d\tnums=%d, mm_total_2M_pages=%d", zone_select, num, mm_total_2M_pages); - return NULL; -} - -/** - * @brief 清除页面的引用计数, 计数为0时清空除页表已映射以外的所有属性 - * - * @param p 物理页结构体 - * @return unsigned long - */ -unsigned long page_clean(struct Page *p) -{ - --p->ref_counts; - --p->zone->total_pages_link; - - // 若引用计数为空,则清空除PAGE_PGT_MAPPED以外的所有属性 - if (!p->ref_counts) - { - p->attr &= PAGE_PGT_MAPPED; - } - return 0; -} - -/** - * @brief Get the page's attr - * - * @param page 内存页结构体 - * @return ul 属性 - */ -ul get_page_attr(struct Page *page) -{ - if (page == NULL) - { - kBUG("get_page_attr(): page == NULL"); - return EPAGE_NULL; - } - else - return page->attr; -} - -/** - * @brief Set the page's attr - * - * @param page 内存页结构体 - * @param flags 属性 - * @return ul 错误码 - */ -ul set_page_attr(struct Page *page, ul flags) -{ - if (page == NULL) - { - kBUG("get_page_attr(): page == NULL"); - return EPAGE_NULL; - } - else - { - page->attr = flags; - return 0; - } -} -/** - * @brief 释放连续number个内存页 - * - * @param page 第一个要被释放的页面的结构体 - * @param number 要释放的内存页数量 number<64 - */ - -void free_pages(struct Page *page, int number) -{ - if (page == NULL) - { - kerror("free_pages() page is invalid."); - return; - } - - if (number >= 64 || number <= 0) - { - kerror("free_pages(): number %d is invalid.", number); - return; - } - - ul page_num; - for (int i = 0; i < number; ++i, ++page) - { - page_num = page->addr_phys >> PAGE_2M_SHIFT; - // 复位bmp - *(memory_management_struct.bmp + (page_num >> 6)) &= ~(1UL << (page_num % 64)); - // 更新计数器 - --page->zone->count_pages_using; - ++page->zone->count_pages_free; - page->attr = 0; - } - - return; -} - -/** - * @brief 重新初始化页表的函数 - * 将所有物理页映射到线性地址空间 - */ -void page_table_init() -{ - kinfo("Re-Initializing page table..."); - ul *global_CR3 = get_CR3(); - - int js = 0; - ul *tmp_addr; - for (int i = 0; i < memory_management_struct.count_zones; ++i) - { - struct Zone *z = memory_management_struct.zones_struct + i; - struct Page *p = z->pages_group; - - if (i == ZONE_UNMAPPED_INDEX && ZONE_UNMAPPED_INDEX != 0) - break; - - for (int j = 0; j < z->count_pages; ++j) - { - 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; - } - } - - - barrier(); - // ========= 在IDLE进程的顶层页表中添加对内核地址空间的映射 ===================== - - // 由于IDLE进程的顶层页表的高地址部分会被后续进程所复制,为了使所有进程能够共享相同的内核空间, - // 因此需要先在IDLE进程的顶层页表内映射二级页表 - - uint64_t *idle_pml4t_vaddr = (uint64_t *)phys_2_virt((uint64_t)get_CR3() & (~0xfffUL)); - - for (int i = 256; i < 512; ++i) - { - uint64_t *tmp = idle_pml4t_vaddr + i; - barrier(); - if (*tmp == 0) - { - void *pdpt = kmalloc(PAGE_4K_SIZE, 0); - barrier(); - memset(pdpt, 0, PAGE_4K_SIZE); - barrier(); - set_pml4t(tmp, mk_pml4t(virt_2_phys(pdpt), PAGE_KERNEL_PGT)); - } - } - barrier(); - flush_tlb(); - kinfo("Page table Initialized. Affects:%d", js); -} - -/** - * @brief 从页表中获取pdt页表项的内容 - * - * @param proc_page_table_addr 页表的地址 - * @param is_phys 页表地址是否为物理地址 - * @param virt_addr_start 要清除的虚拟地址的起始地址 - * @param length 要清除的区域的长度 - * @param clear 是否清除标志位 - */ -uint64_t mm_get_PDE(ul proc_page_table_addr, bool is_phys, ul virt_addr, bool clear) -{ - ul *tmp; - if (is_phys) - tmp = phys_2_virt((ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr >> PAGE_GDT_SHIFT) & 0x1ff)); - else - tmp = (ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr >> PAGE_GDT_SHIFT) & 0x1ff); - - // pml4页表项为0 - if (*tmp == 0) - return 0; - - tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr >> PAGE_1G_SHIFT) & 0x1ff)); - - // pdpt页表项为0 - if (*tmp == 0) - return 0; - - // 读取pdt页表项 - tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr) >> PAGE_2M_SHIFT) & 0x1ff))); - - if (clear) // 清除页表项的标志位 - return *tmp & (~0x1fff); - else - return *tmp; -} - -/** - * @brief 从mms中寻找Page结构体 - * - * @param phys_addr - * @return struct Page* - */ -static struct Page *mm_find_page(uint64_t phys_addr, uint32_t zone_select) -{ - uint32_t zone_start, zone_end; - 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_UNMAPPED_INDEX; - break; - - default: - kerror("In mm_find_page: param: zone_select incorrect."); - // 返回空 - return NULL; - break; - } - - for (int i = zone_start; i <= zone_end; ++i) - { - if ((memory_management_struct.zones_struct + i)->count_pages_using == 0) - 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 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 (ul k = shift; k < 64; ++k) - { - if ((*p >> k) & 1) // 若当前页已分配 - { - uint64_t page_num = j + k - shift; - struct Page *x = memory_management_struct.pages_struct + page_num; - - if (x->addr_phys == phys_addr) // 找到对应的页 - return x; - } - } - } - } - return NULL; -} - -/** - * @brief 调整堆区域的大小(暂时只能增加堆区域) - * - * @todo 缩小堆区域 - * @param old_brk_end_addr 原本的堆内存区域的结束地址 - * @param offset 新的地址相对于原地址的偏移量 - * @return uint64_t - */ -uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset) -{ - - uint64_t end_addr = PAGE_2M_ALIGN(old_brk_end_addr + offset); - if (offset >= 0) - { - for (uint64_t i = old_brk_end_addr; i < end_addr; i += PAGE_2M_SIZE) - { - struct vm_area_struct *vma = NULL; - mm_create_vma(current_pcb->mm, i, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); - mm_map(current_pcb->mm, i, PAGE_2M_SIZE, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys); - // mm_map_vma(vma, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, 0, PAGE_2M_SIZE); - } - current_pcb->mm->brk_end = end_addr; - } - else - { - - // 释放堆内存 - for (uint64_t i = end_addr; i < old_brk_end_addr; i += PAGE_2M_SIZE) - { - uint64_t phys = mm_get_PDE((uint64_t)phys_2_virt((uint64_t)current_pcb->mm->pgd), false, i, true); - - // 找到对应的页 - struct Page *p = mm_find_page(phys, ZONE_NORMAL); - if (p == NULL) - { - kerror("cannot find page addr=%#018lx", phys); - return end_addr; - } - - free_pages(p, 1); - } - - mm_unmap_proc_table((uint64_t)phys_2_virt((uint64_t)current_pcb->mm->pgd), false, end_addr, PAGE_2M_ALIGN(ABS(offset))); - // 在页表中取消映射 - } - return end_addr; -} - -/** - * @brief 创建mmio对应的页结构体 - * - * @param paddr 物理地址 - * @return struct Page* 创建成功的page - */ -struct Page *__create_mmio_page_struct(uint64_t paddr) -{ - struct Page *p = (struct Page *)kzalloc(sizeof(struct Page), 0); - if (p == NULL) - return NULL; - p->addr_phys = paddr; - page_init(p, PAGE_DEVICE); - return p; -} \ No newline at end of file diff --git a/kernel/src/mm/mm.h b/kernel/src/mm/mm.h index 1ae69383..8dec99b0 100644 --- a/kernel/src/mm/mm.h +++ b/kernel/src/mm/mm.h @@ -6,9 +6,9 @@ #include #include -// 每个页表的项数 -// 64位下,每个页表4k,每条页表项8B,故一个页表有512条 -#define PTRS_PER_PGT 512 +extern void rs_pseudo_map_phys(uint64_t virt_addr, uint64_t phys_addr, uint64_t size); +extern void rs_map_phys(uint64_t virt_addr, uint64_t phys_addr, uint64_t size, uint64_t flags); +extern uint64_t rs_unmap_at_low_addr(); // 内核层的起始地址 #define PAGE_OFFSET 0xffff800000000000UL @@ -39,9 +39,6 @@ // 虚拟地址与物理地址转换 #define virt_2_phys(addr) ((unsigned long)(addr)-PAGE_OFFSET) #define phys_2_virt(addr) ((unsigned long *)((unsigned long)(addr) + PAGE_OFFSET)) -// 获取对应的页结构体 -#define Virt_To_2M_Page(kaddr) (memory_management_struct.pages_struct + (virt_2_phys(kaddr) >> PAGE_2M_SHIFT)) -#define Phy_to_2M_Page(kaddr) (memory_management_struct.pages_struct + ((unsigned long)(kaddr) >> PAGE_2M_SHIFT)) // 在这个地址以上的虚拟空间,用来进行特殊的映射 #define SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE 0xffffa00000000000UL @@ -49,7 +46,6 @@ #define IO_APIC_MAPPING_OFFSET 0xfec00000UL #define LOCAL_APIC_MAPPING_OFFSET 0xfee00000UL #define AHCI_MAPPING_OFFSET 0xff200000UL // AHCI 映射偏移量,之后使用了4M的地址 -#define XHCI_MAPPING_OFFSET 0x100000000 // XHCI控制器映射偏移量(后方请预留1GB的虚拟空间来映射不同的controller) // ===== 内存区域属性 ===== // DMA区域 @@ -138,59 +134,21 @@ #define PAGE_USER_4K_PAGE (PAGE_U_S | PAGE_R_W | PAGE_PRESENT) -// ===== 错误码定义 ==== -// 物理页结构体为空 -#define EPAGE_NULL 1 - /** * @brief 刷新TLB的宏定义 * 由于任何写入cr3的操作都会刷新TLB,因此这个宏定义可以刷新TLB */ -#define flush_tlb() \ - do \ - { \ - ul tmp; \ - io_mfence(); \ - __asm__ __volatile__("movq %%cr3, %0\n\t" \ - "movq %0, %%cr3\n\t" \ - : "=r"(tmp)::"memory"); \ - \ +#define flush_tlb() \ + do \ + { \ + ul tmp; \ + io_mfence(); \ + __asm__ __volatile__("movq %%cr3, %0\n\t" \ + "movq %0, %%cr3\n\t" \ + : "=r"(tmp)::"memory"); \ + \ } while (0); -/** - * @brief 系统内存信息结构体(单位:字节) - * - */ -struct mm_stat_t -{ - uint64_t total; // 计算机的总内存数量大小 - uint64_t used; // 已使用的内存大小 - uint64_t free; // 空闲物理页所占的内存大小 - uint64_t shared; // 共享的内存大小 - uint64_t cache_used; // 位于slab缓冲区中的已使用的内存大小 - uint64_t cache_free; // 位于slab缓冲区中的空闲的内存大小 - uint64_t available; // 系统总空闲内存大小(包括kmalloc缓冲区) -}; - -/** - * @brief 虚拟内存区域的操作方法的结构体 - * - */ -struct vm_operations_t -{ - /** - * @brief vm area 被打开时的回调函数 - * - */ - void (*open)(struct vm_area_struct *area); - /** - * @brief vm area将要被移除的时候,将会调用该回调函数 - * - */ - void (*close)(struct vm_area_struct *area); -}; - -extern struct memory_desc memory_management_struct; // 导出内核程序的几个段的起止地址 extern char _text; @@ -203,26 +161,6 @@ extern char _bss; extern char _ebss; extern char _end; -// 每个区域的索引 - -int ZONE_DMA_INDEX = 0; -int ZONE_NORMAL_INDEX = 0; -int ZONE_UNMAPPED_INDEX = 0; - -// 初始化内存管理单元 -void mm_init(); - -/** - * @brief 初始化内存页 - * - * @param page 内存页结构体 - * @param flags 标志位 - * 本函数只负责初始化内存页,允许对同一页面进行多次初始化 - * 而维护计数器及置位bmp标志位的功能,应当在分配页面的时候手动完成 - * @return unsigned long - */ -unsigned long page_init(struct Page *page, ul flags); - /** * @brief 读取CR3寄存器的值(存储了页目录的基地址) * @@ -231,70 +169,11 @@ unsigned long page_init(struct Page *page, ul flags); unsigned long *get_CR3() { ul *tmp; - __asm__ __volatile__("movq %%cr3, %0\n\t" : "=r"(tmp)::"memory"); + __asm__ __volatile__("movq %%cr3, %0\n\t" + : "=r"(tmp)::"memory"); return tmp; } -/** - * @brief 从已初始化的页结构中搜索符合申请条件的、连续num个struct page - * - * @param zone_select 选择内存区域, 可选项:dma, mapped in pgt(normal), 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); - -/** - * @brief 清除页面的引用计数, 计数为0时清空除页表已映射以外的所有属性 - * - * @param p 物理页结构体 - * @return unsigned long - */ -unsigned long page_clean(struct Page *page); - -/** - * @brief 释放连续number个内存页 - * - * @param page 第一个要被释放的页面的结构体 - * @param number 要释放的内存页数量 number<64 - */ -void free_pages(struct Page *page, int number); - -/** - * @brief Get the page's attr - * - * @param page 内存页结构体 - * @return ul 属性 - */ -ul get_page_attr(struct Page *page); - -/** - * @brief Set the page's attr - * - * @param page 内存页结构体 - * @param flags 属性 - * @return ul 错误码 - */ -ul set_page_attr(struct Page *page, ul flags); - -#define mk_pml4t(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr)) -/** - * @brief 设置pml4页表的页表项 - * @param pml4tptr pml4页表项的地址 - * @param pml4val pml4页表项的值 - */ -#define set_pml4t(pml4tptr, pml4tval) (*(pml4tptr) = (pml4tval)) - -#define mk_pdpt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr)) -#define set_pdpt(pdptptr, pdptval) (*(pdptptr) = (pdptval)) - -#define mk_pdt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr)) -#define set_pdt(pdtptr, pdtval) (*(pdtptr) = (pdtval)) - -#define mk_pt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr)) -#define set_pt(ptptr, ptval) (*(ptptr) = (ptval)) - /* * vm_area_struct中的vm_flags的可选值 * 对应的结构体请见mm-types.h @@ -312,233 +191,3 @@ ul set_page_attr(struct Page *page, ul flags); /* VMA basic access permission flags */ #define VM_ACCESS_FLAGS (VM_READ | VM_WRITE | VM_EXEC) - -/** - * @brief 初始化虚拟内存区域结构体 - * - * @param vma - * @param mm - */ -static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm) -{ - memset(vma, 0, sizeof(struct vm_area_struct)); - vma->vm_mm = mm; - vma->vm_prev = vma->vm_next = NULL; - vma->vm_ops = NULL; - list_init(&vma->anon_vma_list); -} - -/** - * @brief 判断给定的vma是否为当前进程所属的vma - * - * @param vma 给定的vma结构体 - * @return true - * @return false - */ -static inline bool vma_is_foreign(struct vm_area_struct *vma) -{ - if (current_pcb->mm == NULL) - return true; - if (current_pcb->mm != vma->vm_mm) - return true; - return false; -} - -static inline bool vma_is_accessible(struct vm_area_struct *vma) -{ - return vma->vm_flags & VM_ACCESS_FLAGS; -} - -/** - * @brief 获取一块新的vma结构体,并将其与指定的mm进行绑定 - * - * @param mm 与VMA绑定的内存空间分布结构体 - * @return struct vm_area_struct* 新的VMA - */ -struct vm_area_struct *vm_area_alloc(struct mm_struct *mm); - -/** - * @brief 释放vma结构体 - * - * @param vma 待释放的vma结构体 - */ -void vm_area_free(struct vm_area_struct *vma); - -/** - * @brief 从链表中删除指定的vma结构体 - * - * @param vma - */ -void vm_area_del(struct vm_area_struct *vma); - -/** - * @brief 查找第一个符合“addr < vm_end”条件的vma - * - * @param mm 内存空间分布结构体 - * @param addr 虚拟地址 - * @return struct vm_area_struct* 符合条件的vma - */ -struct vm_area_struct *vma_find(struct mm_struct *mm, uint64_t addr); - -/** - * @brief 插入vma - * - * @param mm - * @param vma - * @return int - */ -int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma); - -/** - * @brief 重新初始化页表的函数 - * 将所有物理页映射到线性地址空间 - */ -void page_table_init(); - -/** - * @brief 将物理地址映射到页表的函数 - * - * @param virt_addr_start 要映射到的虚拟地址的起始位置 - * @param phys_addr_start 物理地址的起始位置 - * @param length 要映射的区域的长度(字节) - * @param flags 标志位 - * @param use4k 是否使用4k页 - */ -int mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool use4k); - -/** - * @brief 将将物理地址填写到进程的页表的函数 - * - * @param proc_page_table_addr 页表的基地址 - * @param is_phys 页表的基地址是否为物理地址 - * @param virt_addr_start 要映射到的虚拟地址的起始位置 - * @param phys_addr_start 物理地址的起始位置 - * @param length 要映射的区域的长度(字节) - * @param user 用户态是否可访问 - * @param flush 是否刷新tlb - * @param use4k 是否使用4k页 - */ -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); - -int mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul flags); - -/** - * @brief 从页表中清除虚拟地址的映射 - * - * @param proc_page_table_addr 页表的地址 - * @param is_phys 页表地址是否为物理地址 - * @param virt_addr_start 要清除的虚拟地址的起始地址 - * @param length 要清除的区域的长度 - */ -void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul length); - -/** - * @brief 取消当前进程的页表中的虚拟地址映射 - * - * @param virt_addr 虚拟地址 - * @param length 地址长度 - */ -#define mm_unmap_addr(virt_addr, length) ({ mm_unmap_proc_table((uint64_t)get_CR3(), true, virt_addr, length); }) - -/** - * @brief 创建VMA - * - * @param mm 要绑定的内存空间分布结构体 - * @param vaddr 起始虚拟地址 - * @param length 长度(字节) - * @param vm_flags vma的标志 - * @param vm_ops vma的操作接口 - * @param res_vma 返回的vma指针 - * @return int 错误码 - */ -int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flags_t vm_flags, - struct vm_operations_t *vm_ops, struct vm_area_struct **res_vma); - -/** - * @brief 将指定的物理地址映射到指定的vma处 - * - * @param vma 要进行映射的VMA结构体 - * @param paddr 起始物理地址 - * @param offset 要映射的起始位置在vma中的偏移量 - * @param length 要映射的长度 - * @return int 错误码 - */ -int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr, uint64_t offset, uint64_t length); - -/** - * @brief 在页表中映射物理地址到指定的虚拟地址(需要页表中已存在对应的vma) - * - * @param mm 内存管理结构体 - * @param vaddr 虚拟地址 - * @param length 长度(字节) - * @param paddr 物理地址 - * @return int 返回码 - */ -int mm_map(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr); - -/** - * @brief 在页表中取消指定的vma的映射 - * - * @param mm 指定的mm - * @param vma 待取消映射的vma - * @param paddr 返回的被取消映射的起始物理地址 - * @return int 返回码 - */ -int mm_unmap_vma(struct mm_struct *mm, struct vm_area_struct *vma, uint64_t *paddr); - -/** - * @brief 解除一段虚拟地址的映射(这些地址必须在vma中存在) - * - * @param mm 内存空间结构体 - * @param vaddr 起始地址 - * @param length 结束地址 - * @param destroy 是否释放vma结构体 - * @return int 错误码 - */ -int mm_unmap(struct mm_struct *mm, uint64_t vaddr, uint64_t length, bool destroy); - -/** - * @brief 检测是否为有效的2M页(物理内存页) - * - * @param paddr 物理地址 - * @return int8_t 是 -> 1 - * 否 -> 0 - */ -int8_t mm_is_2M_page(uint64_t paddr); - -/** - * @brief 检查页表是否存在不为0的页表项 - * - * @param ptr 页表基指针 - * @return int8_t 存在 -> 1 - * 不存在 -> 0 - */ -int8_t mm_check_page_table(uint64_t *ptr); - -/** - * @brief 调整堆区域的大小(暂时只能增加堆区域) - * - * @todo 缩小堆区域 - * @param old_brk_end_addr 原本的堆内存区域的结束地址 - * @param offset 新的地址相对于原地址的偏移量 - * @return uint64_t - */ -uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset); - -/** - * @brief 获取系统当前的内存信息(未上锁,不一定精准) - * - * @return struct mm_stat_t 内存信息结构体 - */ -struct mm_stat_t mm_stat(); - -/** - * @brief 检测指定地址是否已经被映射 - * - * @param page_table_phys_addr 页表的物理地址 - * @param virt_addr 要检测的地址 - * @return true 已经被映射 - * @return false - */ -bool mm_check_mapped(ul page_table_phys_addr, uint64_t virt_addr); \ No newline at end of file diff --git a/kernel/src/mm/mmap.c b/kernel/src/mm/mmap.c deleted file mode 100644 index d39c45ca..00000000 --- a/kernel/src/mm/mmap.c +++ /dev/null @@ -1,582 +0,0 @@ -#include "mm.h" -#include "slab.h" -#include "internal.h" -#include -#include - -extern uint64_t mm_total_2M_pages; - -/** - * @brief 虚拟地址长度所需要的entry数量 - * - */ -typedef struct -{ - int64_t num_PML4E; - int64_t num_PDPTE; - int64_t num_PDE; - int64_t num_PTE; -} mm_pgt_entry_num_t; - -/** - * @brief 计算虚拟地址长度对应的页表entry数量 - * - * @param length 长度 - * @param ent 返回的entry数量结构体 - */ -static void mm_calculate_entry_num(uint64_t length, mm_pgt_entry_num_t *ent) -{ - if (ent == NULL) - return; - ent->num_PML4E = (length + (1UL << PAGE_GDT_SHIFT) - 1) >> PAGE_GDT_SHIFT; - ent->num_PDPTE = (length + PAGE_1G_SIZE - 1) >> PAGE_1G_SHIFT; - ent->num_PDE = (length + PAGE_2M_SIZE - 1) >> PAGE_2M_SHIFT; - ent->num_PTE = (length + PAGE_4K_SIZE - 1) >> PAGE_4K_SHIFT; -} - -/** - * @brief 将物理地址映射到页表的函数 - * - * @param virt_addr_start 要映射到的虚拟地址的起始位置 - * @param phys_addr_start 物理地址的起始位置 - * @param length 要映射的区域的长度(字节) - * @param flags 标志位 - * @param use4k 是否使用4k页 - */ -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(); - - return mm_map_proc_page_table(global_CR3, true, virt_addr_start, phys_addr_start, length, flags, false, true, use4k); -} - -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(); - return mm_map_proc_page_table(global_CR3, true, virt_addr_start, phys_addr_start, length, flags, true, true, false); -} - -/** - * @brief 将将物理地址填写到进程的页表的函数 - * - * @param proc_page_table_addr 页表的基地址 - * @param is_phys 页表的基地址是否为物理地址 - * @param virt_addr_start 要映射到的虚拟地址的起始位置 - * @param phys_addr_start 物理地址的起始位置 - * @param length 要映射的区域的长度(字节) - * @param user 用户态是否可访问 - * @param flush 是否刷新tlb - * @param use4k 是否使用4k页 - */ -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页表项的地址 - mm_pgt_entry_num_t pgt_num; - mm_calculate_entry_num(length, &pgt_num); - - // 已映射的内存大小 - uint64_t length_mapped = 0; - - // 对user标志位进行校正 - if ((flags & PAGE_U_S) != 0) - user = true; - else - user = false; - - uint64_t pml4e_id = ((virt_addr_start >> PAGE_GDT_SHIFT) & 0x1ff); - uint64_t *pml4_ptr; - if (is_phys) - pml4_ptr = phys_2_virt((ul *)((ul)proc_page_table_addr & (~0xfffUL))); - else - pml4_ptr = (ul *)((ul)proc_page_table_addr & (~0xfffUL)); - - // 循环填写顶层页表 - for (; (pgt_num.num_PML4E > 0) && pml4e_id < 512; ++pml4e_id) - { - // 剩余需要处理的pml4E -1 - --(pgt_num.num_PML4E); - - ul *pml4e_ptr = pml4_ptr + pml4e_id; - - // 创建新的二级页表 - if (*pml4e_ptr == 0) - { - ul *virt_addr = kmalloc(PAGE_4K_SIZE, 0); - memset(virt_addr, 0, PAGE_4K_SIZE); - set_pml4t(pml4e_ptr, mk_pml4t(virt_2_phys(virt_addr), (user ? PAGE_USER_PGT : PAGE_KERNEL_PGT))); - } - - uint64_t pdpte_id = (((virt_addr_start + length_mapped) >> PAGE_1G_SHIFT) & 0x1ff); - uint64_t *pdpt_ptr = (uint64_t *)phys_2_virt(*pml4e_ptr & (~0xfffUL)); - - // 循环填写二级页表 - for (; (pgt_num.num_PDPTE > 0) && pdpte_id < 512; ++pdpte_id) - { - --pgt_num.num_PDPTE; - uint64_t *pdpte_ptr = (pdpt_ptr + pdpte_id); - - // 创建新的三级页表 - if (*pdpte_ptr == 0) - { - ul *virt_addr = kmalloc(PAGE_4K_SIZE, 0); - memset(virt_addr, 0, PAGE_4K_SIZE); - set_pdpt(pdpte_ptr, mk_pdpt(virt_2_phys(virt_addr), (user ? PAGE_USER_DIR : PAGE_KERNEL_DIR))); - } - - uint64_t pde_id = (((virt_addr_start + length_mapped) >> PAGE_2M_SHIFT) & 0x1ff); - uint64_t *pd_ptr = (uint64_t *)phys_2_virt(*pdpte_ptr & (~0xfffUL)); - - // 循环填写三级页表,初始化2M物理页 - for (; (pgt_num.num_PDE > 0) && pde_id < 512; ++pde_id) - { - --pgt_num.num_PDE; - // 计算当前2M物理页对应的pdt的页表项的物理地址 - ul *pde_ptr = pd_ptr + pde_id; - // ====== 使用4k页 ======= - if (unlikely(use4k)) - { - // kdebug("use 4k"); - if (*pde_ptr == 0) - { - // 创建四级页表 - 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 & (~0xfffUL)); - - // 循环填写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."); - else - set_pt(pte_ptr, mk_pt((ul)phys_addr_start + length_mapped, flags | (user ? PAGE_USER_4K_PAGE : PAGE_KERNEL_4K_PAGE))); - length_mapped += PAGE_4K_SIZE; - } - } - // ======= 使用2M页 ======== - else - { - if (unlikely((*pde_ptr != 0) && user == true)) - { - // 如果是用户态可访问的页,则释放当前新获取的物理页 - if (likely((((ul)phys_addr_start + length_mapped) >> PAGE_2M_SHIFT) < mm_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; - } - } - } - } - 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; -} - -/** - * @brief 从页表中清除虚拟地址的映射 - * - * @param proc_page_table_addr 页表的地址 - * @param is_phys 页表地址是否为物理地址 - * @param virt_addr_start 要清除的虚拟地址的起始地址 - * @param length 要清除的区域的长度 - */ -void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul length) -{ - - // 计算线性地址对应的pml4页表项的地址 - mm_pgt_entry_num_t pgt_num; - mm_calculate_entry_num(length, &pgt_num); - // 已取消映射的内存大小 - uint64_t length_unmapped = 0; - - uint64_t pml4e_id = ((virt_addr_start >> PAGE_GDT_SHIFT) & 0x1ff); - uint64_t *pml4_ptr; - if (is_phys) - pml4_ptr = phys_2_virt((ul *)((ul)proc_page_table_addr & (~0xfffUL))); - else - pml4_ptr = (ul *)((ul)proc_page_table_addr & (~0xfffUL)); - - // 循环填写顶层页表 - for (; (pgt_num.num_PML4E > 0) && pml4e_id < 512; ++pml4e_id) - { - // 剩余需要处理的pml4E -1 - --(pgt_num.num_PML4E); - - ul *pml4e_ptr = NULL; - pml4e_ptr = pml4_ptr + pml4e_id; - - // 二级页表不存在 - if (*pml4e_ptr == 0) - { - continue; - } - - uint64_t pdpte_id = (((virt_addr_start + length_unmapped) >> PAGE_1G_SHIFT) & 0x1ff); - uint64_t *pdpt_ptr = (uint64_t *)phys_2_virt(*pml4e_ptr & (~0xfffUL)); - // kdebug("pdpt_ptr=%#018lx", pdpt_ptr); - - // 循环处理二级页表 - for (; (pgt_num.num_PDPTE > 0) && pdpte_id < 512; ++pdpte_id) - { - --pgt_num.num_PDPTE; - uint64_t *pdpte_ptr = (pdpt_ptr + pdpte_id); - // kdebug("pgt_num.num_PDPTE=%ld pdpte_ptr=%#018lx", pgt_num.num_PDPTE, pdpte_ptr); - - // 三级页表为空 - if (*pdpte_ptr == 0) - { - continue; - } - - uint64_t pde_id = (((virt_addr_start + length_unmapped) >> PAGE_2M_SHIFT) & 0x1ff); - uint64_t *pd_ptr = (uint64_t *)phys_2_virt(*pdpte_ptr & (~0xfffUL)); - // kdebug("pd_ptr=%#018lx, *pd_ptr=%#018lx", pd_ptr, *pd_ptr); - - // 循环处理三级页表 - for (; (pgt_num.num_PDE > 0) && pde_id < 512; ++pde_id) - { - --pgt_num.num_PDE; - // 计算当前2M物理页对应的pdt的页表项的物理地址 - ul *pde_ptr = pd_ptr + pde_id; - - // 存在4级页表 - if (((*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 & (~0xfffUL)); - // 循环处理4K页表 - for (; pgt_num.num_PTE > 0 && pte_id < 512; ++pte_id) - { - uint64_t *pte_ptr = pt_ptr + pte_id; - --pgt_num.num_PTE; - *pte_ptr = 0; - length_unmapped += PAGE_4K_SIZE; - } - - // 4级页表已经空了,释放页表 - if (unlikely(mm_check_page_table(pt_ptr)) == 0) - { - *pde_ptr = 0; - kfree(pt_ptr); - } - } - else - { - *pde_ptr = 0; - length_unmapped += PAGE_2M_SIZE; - pgt_num.num_PTE -= 512; - } - } - - // 3级页表已经空了,释放页表 - if (unlikely(mm_check_page_table(pd_ptr)) == 0) - { - *pdpte_ptr = 0; - kfree(pd_ptr); - } - } - // 2级页表已经空了,释放页表 - if (unlikely(mm_check_page_table(pdpt_ptr)) == 0) - { - *pml4e_ptr = 0; - kfree(pdpt_ptr); - } - } - flush_tlb(); -} - -/** - * @brief 创建VMA - * - * @param mm 要绑定的内存空间分布结构体 - * @param vaddr 起始虚拟地址 - * @param length 长度(字节) - * @param vm_flags vma的标志 - * @param vm_ops vma的操作接口 - * @param res_vma 返回的vma指针 - * @return int 错误码 - */ -int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flags_t vm_flags, struct vm_operations_t *vm_ops, struct vm_area_struct **res_vma) -{ - int retval = 0; - // 输入的地址如果不是4K对齐,则报错 - if (unlikely(vaddr & (PAGE_4K_SIZE - 1))) - return -EINVAL; - - struct vm_area_struct *vma = vm_area_alloc(mm); - if (unlikely(vma == NULL)) - return -ENOMEM; - vma->vm_ops = vm_ops; - vma->vm_flags = vm_flags; - vma->vm_start = vaddr; - vma->vm_end = vaddr + length; - // 将VMA加入mm的链表 - retval = vma_insert(mm, vma); - if (retval == -EEXIST || retval == __VMA_MERGED) // 之前已经存在了相同的vma,直接返回 - { - *res_vma = vma_find(mm, vma->vm_start); - kfree(vma); - if (retval == -EEXIST) - return -EEXIST; - else - return 0; - } - - if (res_vma != NULL) - *res_vma = vma; - return 0; -} - -/** - * @brief 将指定的物理地址映射到指定的vma处 - * - * @param vma 要进行映射的VMA结构体 - * @param paddr 起始物理地址 - * @param offset 要映射的起始位置在vma中的偏移量 - * @param length 要映射的长度 - * @return int 错误码 - */ -int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr, uint64_t offset, uint64_t length) -{ - int retval = 0; - uint64_t mapped = 0; - BUG_ON((offset & (PAGE_4K_SIZE - 1)) != 0); - length = PAGE_4K_ALIGN(length); // 将length按照4K进行对齐 - // 获取物理地址对应的页面 - struct Page *pg; - uint64_t page_flags = 0; - if (vma->vm_flags & VM_IO) // 对于mmio的内存,创建新的page结构体 - { - page_flags = PAGE_PWT | PAGE_PCD; - if (unlikely(vma->anon_vma == NULL || vma->anon_vma->page == NULL)) - pg = __create_mmio_page_struct(paddr); - else - pg = vma->anon_vma->page; - } - else - pg = Phy_to_2M_Page(paddr); - - if (unlikely(pg->anon_vma == NULL)) // 若页面不存在anon_vma,则为页面创建anon_vma - { - spin_lock(&pg->op_lock); - if (unlikely(pg->anon_vma == NULL)) - __anon_vma_create_alloc(pg, false); - spin_unlock(&pg->op_lock); - } - barrier(); - // 将anon vma与vma进行绑定 - __anon_vma_add(pg->anon_vma, vma); - barrier(); - // 长度超过界限 - BUG_ON(vma->vm_start + offset + length > vma->vm_end); - - /* - todo: 限制页面的读写权限 - */ - - // ==== 将地址映射到页表 ==== - uint64_t len_4k, len_2m; - // 将地址使用4k页填补,使得地址按照2M对齐 - len_4k = PAGE_2M_ALIGN(vma->vm_start + offset) - (vma->vm_start + offset); - if (len_4k > 0) - len_4k = (len_4k > length) ? length : len_4k; - if (len_4k) - { - if (vma->vm_flags & VM_USER) - page_flags |= PAGE_USER_4K_PAGE; - else - page_flags |= PAGE_KERNEL_4K_PAGE; - - // 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正 - retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset, paddr, len_4k, page_flags, false, false, true); - if (unlikely(retval != 0)) - goto failed; - - mapped += len_4k; - length -= len_4k; - } - - len_4k = length % PAGE_2M_SIZE; - len_2m = length / PAGE_2M_SIZE; - - // 映射连续的2M页 - if (likely(len_2m > 0)) - { - if (vma->vm_flags & VM_USER) - page_flags |= PAGE_USER_PAGE; - else - page_flags |= PAGE_KERNEL_PAGE; - // 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正 - retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset + mapped, paddr + mapped, len_2m, page_flags, false, false, false); - - if (unlikely(retval != 0)) - goto failed; - mapped += len_2m; - } - // 最后再使用4K页填补 - if (likely(len_4k > 0)) - { - - if (vma->vm_flags & VM_USER) - page_flags |= PAGE_USER_4K_PAGE; - else - page_flags |= PAGE_KERNEL_4K_PAGE; - - // 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正 - retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset + mapped, paddr + mapped, len_4k, page_flags, false, false, true); - - if (unlikely(retval != 0)) - goto failed; - mapped += len_4k; - } - - if (vma->vm_flags & VM_IO) - vma->page_offset = 0; - - flush_tlb(); - return 0; -failed:; - kdebug("map VMA failed."); - return retval; -} - -/** - * @brief 在页表中映射物理地址到指定的虚拟地址(需要页表中已存在对应的vma) - * - * @param mm 内存管理结构体 - * @param vaddr 虚拟地址 - * @param length 长度(字节) - * @param paddr 物理地址 - * @return int 返回码 - */ -int mm_map(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr) -{ - int retval = 0; - uint64_t offset = 0; - for (uint64_t mapped = 0; mapped < length;) - { - - struct vm_area_struct *vma = vma_find(mm, vaddr + mapped); - if (unlikely(vma == NULL)) - { - kerror("Map addr failed: vma not found. At address: %#018lx, pid=%ld", vaddr + mapped, current_pcb->pid); - return -EINVAL; - } - - // if (unlikely(vma->vm_start != (vaddr + mapped))) - // { - // kerror("Map addr failed: addr_start is not equal to current: %#018lx.", vaddr + mapped); - // return -EINVAL; - // } - - offset = vaddr + mapped - vma->vm_start; - uint64_t m_len = vma->vm_end - vma->vm_start - offset; - // kdebug("start=%#018lx, offset=%ld", vma->vm_start, offset); - retval = mm_map_vma(vma, paddr + mapped, offset, m_len); - if (unlikely(retval != 0)) - goto failed; - - mapped += m_len; - } - return 0; -failed:; - kerror("Map addr failed."); - return retval; -} - -/** - * @brief 在页表中取消指定的vma的映射 - * - * @param mm 指定的mm - * @param vma 待取消映射的vma - * @param paddr 返回的被取消映射的起始物理地址 - * @return int 返回码 - */ -int mm_unmap_vma(struct mm_struct *mm, struct vm_area_struct *vma, uint64_t *paddr) -{ - // 确保vma对应的mm与指定的mm相一致 - if (unlikely(vma->vm_mm != mm)) - return -EINVAL; - struct anon_vma_t *anon = vma->anon_vma; - if (paddr != NULL) - *paddr = __mm_get_paddr(mm, vma->vm_start); - if (anon == NULL) - kwarn("anon is NULL"); - semaphore_down(&anon->sem); - - mm_unmap_proc_table((uint64_t)mm->pgd, true, vma->vm_start, vma->vm_end - vma->vm_start); - __anon_vma_del(vma); - /** todo: 这里应该会存在bug,应修复。 - * 若anon_vma的等待队列上有其他的进程,由于anon_vma被释放 - * 这些在等待队列上的进程将无法被唤醒。 - */ - list_init(&vma->anon_vma_list); - - semaphore_up(&anon->sem); - - return 0; -} - -/** - * @brief 解除一段虚拟地址的映射(这些地址必须在vma中存在) - * - * @param mm 内存空间结构体 - * @param vaddr 起始地址 - * @param length 结束地址 - * @param destroy 是否释放vma结构体 - * @return int 错误码 - */ -int mm_unmap(struct mm_struct *mm, uint64_t vaddr, uint64_t length, bool destroy) -{ - int retval = 0; - for (uint64_t unmapped = 0; unmapped < length;) - { - struct vm_area_struct *vma = vma_find(mm, vaddr + unmapped); - if (unlikely(vma == NULL)) - { - kerror("Unmap addr failed: vma not found. At address: %#018lx, pid=%ld", vaddr + unmapped, current_pcb->pid); - return -EINVAL; - } - - if (unlikely(vma->vm_start != (vaddr + unmapped))) - { - kerror("Unmap addr failed: addr_start is not equal to current: %#018lx.", vaddr + unmapped); - return -EINVAL; - } - if (vma->anon_vma != NULL) - mm_unmap_vma(mm, vma, NULL); - - unmapped += vma->vm_end - vma->vm_start; - // 释放vma结构体 - if (destroy) - { - vm_area_del(vma); - vm_area_free(vma); - } - } - return 0; -failed:; - kerror("Unmap addr failed."); - return retval; -} diff --git a/kernel/src/mm/mmio.c b/kernel/src/mm/mmio.c deleted file mode 100644 index 3be277d0..00000000 --- a/kernel/src/mm/mmio.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "mmio.h" -#include -extern void __mmio_buddy_init(); - -void mmio_init() -{ - __mmio_buddy_init(); - kinfo("mmio_init success"); -} diff --git a/kernel/src/mm/mmio.h b/kernel/src/mm/mmio.h index e7bdbd91..78706442 100644 --- a/kernel/src/mm/mmio.h +++ b/kernel/src/mm/mmio.h @@ -1,7 +1,5 @@ #pragma once #include "mm.h" -extern void mmio_buddy_init(); -extern void mmio_create(); +extern void mmio_create(uint32_t size, uint64_t vm_flagsu, uint64_t* res_vaddr, uint64_t* res_length); extern int mmio_release(int vaddr, int length); -void mmio_init(); diff --git a/kernel/src/mm/mmio_buddy.rs b/kernel/src/mm/mmio_buddy.rs index 53ea362c..71ca26fc 100644 --- a/kernel/src/mm/mmio_buddy.rs +++ b/kernel/src/mm/mmio_buddy.rs @@ -1,16 +1,19 @@ use crate::libs::spinlock::{SpinLock, SpinLockGuard}; +use crate::mm::kernel_mapper::KernelMapper; use crate::syscall::SystemError; use crate::{ arch::asm::current::current_pcb, - include::bindings::bindings::{ - initial_mm, mm_create_vma, mm_unmap, vm_area_del, vm_area_free, vm_area_struct, vm_flags_t, - vma_find, MMIO_BASE, MMIO_TOP, PAGE_1G_SHIFT, PAGE_1G_SIZE, PAGE_2M_SIZE, PAGE_4K_SHIFT, - PAGE_4K_SIZE, VM_DONTCOPY, VM_IO, - }, - kdebug, kerror, + include::bindings::bindings::{vm_flags_t, PAGE_1G_SHIFT, PAGE_4K_SHIFT, PAGE_4K_SIZE}, + kdebug, + mm::{MMArch, MemoryManagementArch}, }; -use alloc::{boxed::Box, collections::LinkedList, vec::Vec}; -use core::{mem, ptr::null_mut}; +use crate::{kerror, kinfo, kwarn}; +use alloc::{collections::LinkedList, vec::Vec}; +use core::mem; +use core::mem::MaybeUninit; +use core::sync::atomic::{compiler_fence, Ordering}; + +use super::VirtAddr; // 最大的伙伴块的幂 const MMIO_BUDDY_MAX_EXP: u32 = PAGE_1G_SHIFT; @@ -19,8 +22,15 @@ const MMIO_BUDDY_MIN_EXP: u32 = PAGE_4K_SHIFT; // 内存池数组的范围 const MMIO_BUDDY_REGION_COUNT: u32 = MMIO_BUDDY_MAX_EXP - MMIO_BUDDY_MIN_EXP + 1; -lazy_static! { - pub static ref MMIO_POOL: MmioBuddyMemPool = MmioBuddyMemPool::new(); +const MMIO_BASE: VirtAddr = VirtAddr::new(0xffffa10000000000); +const MMIO_TOP: VirtAddr = VirtAddr::new(0xffffa20000000000); + +const PAGE_1G_SIZE: usize = 1 << 30; + +static mut __MMIO_POOL: Option = None; + +pub fn mmio_pool() -> &'static mut MmioBuddyMemPool { + unsafe { __MMIO_POOL.as_mut().unwrap() } } pub enum MmioResult { @@ -32,25 +42,49 @@ pub enum MmioResult { } /// @brief buddy内存池 +#[derive(Debug)] pub struct MmioBuddyMemPool { - pool_start_addr: u64, - pool_size: u64, + pool_start_addr: VirtAddr, + pool_size: usize, free_regions: [SpinLock; MMIO_BUDDY_REGION_COUNT as usize], } -impl Default for MmioBuddyMemPool { - fn default() -> Self { - MmioBuddyMemPool { - pool_start_addr: MMIO_BASE as u64, - pool_size: (MMIO_TOP - MMIO_BASE) as u64, - free_regions: unsafe { mem::zeroed() }, - } - } -} + impl MmioBuddyMemPool { fn new() -> Self { - return MmioBuddyMemPool { - ..Default::default() + let mut free_regions: [MaybeUninit>; + MMIO_BUDDY_REGION_COUNT as usize] = unsafe { MaybeUninit::uninit().assume_init() }; + for i in 0..MMIO_BUDDY_REGION_COUNT { + free_regions[i as usize] = MaybeUninit::new(SpinLock::new(MmioFreeRegionList::new())); + } + let free_regions = unsafe { + mem::transmute::<_, [SpinLock; MMIO_BUDDY_REGION_COUNT as usize]>( + free_regions, + ) }; + + let pool = MmioBuddyMemPool { + pool_start_addr: MMIO_BASE, + pool_size: MMIO_TOP - MMIO_BASE, + free_regions, + }; + kdebug!("MMIO buddy pool init: created"); + + let cnt_1g_blocks = (MMIO_TOP - MMIO_BASE) >> 30; + let mut vaddr_base = MMIO_BASE; + kdebug!("total 1G blocks: {cnt_1g_blocks}"); + for _i in 0..cnt_1g_blocks { + compiler_fence(Ordering::SeqCst); + match pool.give_back_block(vaddr_base, PAGE_1G_SHIFT) { + Ok(_) => { + vaddr_base += PAGE_1G_SIZE; + } + Err(_) => { + panic!("MMIO buddy pool init failed"); + } + } + } + kdebug!("MMIO buddy pool init success"); + return pool; } /// @brief 创建新的地址区域结构体 @@ -58,9 +92,12 @@ impl MmioBuddyMemPool { /// @param vaddr 虚拟地址 /// /// @return 创建好的地址区域结构体 - fn create_region(&self, vaddr: u64) -> Box { - let mut region: Box = Box::new(MmioBuddyAddrRegion::new()); - region.vaddr = vaddr; + fn create_region(&self, vaddr: VirtAddr) -> MmioBuddyAddrRegion { + // kdebug!("create_region for vaddr: {vaddr:?}"); + + let region: MmioBuddyAddrRegion = MmioBuddyAddrRegion::new(vaddr); + + // kdebug!("create_region for vaddr: {vaddr:?} OK!!!"); return region; } @@ -75,16 +112,16 @@ impl MmioBuddyMemPool { /// @return Ok(i32) 返回0 /// /// @return Err(SystemError) 返回错误码 - fn give_back_block(&self, vaddr: u64, exp: u32) -> Result { + fn give_back_block(&self, vaddr: VirtAddr, exp: u32) -> Result { // 确保内存对齐,低位都要为0 - if (vaddr & ((1 << exp) - 1)) != 0 { + if (vaddr.data() & ((1 << exp) - 1)) != 0 { return Err(SystemError::EINVAL); } - let region: Box = self.create_region(vaddr); + let region: MmioBuddyAddrRegion = self.create_region(vaddr); // 加入buddy - let list_guard: &mut SpinLockGuard = - &mut self.free_regions[exp2index(exp)].lock(); - self.push_block(region, list_guard); + let mut list_guard = self.free_regions[exp2index(exp)].lock(); + + self.push_block(region, &mut list_guard); return Ok(0); } @@ -97,12 +134,12 @@ impl MmioBuddyMemPool { /// @param list_guard 【exp-1】对应的链表 fn split_block( &self, - region: Box, + region: MmioBuddyAddrRegion, exp: u32, low_list_guard: &mut SpinLockGuard, ) { - let vaddr: u64 = self.calculate_block_vaddr(region.vaddr, exp - 1); - let new_region: Box = self.create_region(vaddr); + let vaddr = self.calculate_block_vaddr(region.vaddr, exp - 1); + let new_region: MmioBuddyAddrRegion = self.create_region(vaddr); self.push_block(region, low_list_guard); self.push_block(new_region, low_list_guard); } @@ -113,7 +150,7 @@ impl MmioBuddyMemPool { /// /// @param list_guard exp对应的链表 /// - /// @return Ok(Box) 符合要求的内存区域。 + /// @return Ok(MmioBuddyAddrRegion) 符合要求的内存区域。 /// /// @return Err(MmioResult) /// - 没有满足要求的内存块时,返回ENOFOUND @@ -123,7 +160,7 @@ impl MmioBuddyMemPool { &self, exp: u32, list_guard: &mut SpinLockGuard, - ) -> Result, MmioResult> { + ) -> Result { // 申请范围错误 if exp < MMIO_BUDDY_MIN_EXP || exp > MMIO_BUDDY_MAX_EXP { kdebug!("query_addr_region: exp wrong"); @@ -256,12 +293,9 @@ impl MmioBuddyMemPool { /// /// @param exp 内存区域的大小(2^exp) /// - /// @return Ok(Box)符合要求的内存块信息结构体。 + /// @return Ok(MmioBuddyAddrRegion)符合要求的内存块信息结构体。 /// @return Err(MmioResult) 没有满足要求的内存块时,返回__query_addr_region的错误码。 - fn mmio_buddy_query_addr_region( - &self, - exp: u32, - ) -> Result, MmioResult> { + fn mmio_buddy_query_addr_region(&self, exp: u32) -> Result { let list_guard: &mut SpinLockGuard = &mut self.free_regions[exp2index(exp)].lock(); match self.query_addr_region(exp, list_guard) { @@ -279,7 +313,7 @@ impl MmioBuddyMemPool { /// @param list_guard 目标链表 fn push_block( &self, - region: Box, + region: MmioBuddyAddrRegion, list_guard: &mut SpinLockGuard, ) { list_guard.list.push_back(region); @@ -288,8 +322,8 @@ impl MmioBuddyMemPool { /// @brief 根据地址和内存块大小,计算伙伴块虚拟内存的地址 #[inline(always)] - fn calculate_block_vaddr(&self, vaddr: u64, exp: u32) -> u64 { - return vaddr ^ (1 << exp); + fn calculate_block_vaddr(&self, vaddr: VirtAddr, exp: u32) -> VirtAddr { + return VirtAddr::new(vaddr.data() ^ (1 << exp as usize)); } /// @brief 寻找并弹出指定内存块的伙伴块 @@ -306,10 +340,10 @@ impl MmioBuddyMemPool { /// - 没有找到伙伴块,返回ENOFOUND fn pop_buddy_block( &self, - vaddr: u64, + vaddr: VirtAddr, exp: u32, list_guard: &mut SpinLockGuard, - ) -> Result, MmioResult> { + ) -> Result { if list_guard.list.len() == 0 { return Err(MmioResult::ISEMPTY); } else { @@ -317,7 +351,7 @@ impl MmioBuddyMemPool { let buddy_vaddr = self.calculate_block_vaddr(vaddr, exp); // element 只会有一个元素 - let mut element: Vec> = list_guard + let mut element: Vec = list_guard .list .drain_filter(|x| x.vaddr == buddy_vaddr) .collect(); @@ -335,13 +369,13 @@ impl MmioBuddyMemPool { /// /// @param list_guard 【exp】对应的链表 /// - /// @return Ok(Box) 内存块信息结构体的引用。 + /// @return Ok(MmioBuddyAddrRegion) 内存块信息结构体的引用。 /// /// @return Err(MmioResult) 当链表为空,无法删除时,返回ISEMPTY fn pop_block( &self, list_guard: &mut SpinLockGuard, - ) -> Result, MmioResult> { + ) -> Result { if !list_guard.list.is_empty() { list_guard.num_free -= 1; return Ok(list_guard.list.pop_back().unwrap()); @@ -377,17 +411,15 @@ impl MmioBuddyMemPool { break; } // 获取内存块 - let vaddr: u64 = list_guard.list.back().unwrap().vaddr; + let vaddr: VirtAddr = list_guard.list.back().unwrap().vaddr; // 获取伙伴内存块 match self.pop_buddy_block(vaddr, exp, list_guard) { Err(err) => { return Err(err); } Ok(buddy_region) => { - let region: Box = list_guard.list.pop_back().unwrap(); - let copy_region: Box = Box::new(MmioBuddyAddrRegion { - vaddr: region.vaddr, - }); + let region: MmioBuddyAddrRegion = list_guard.list.pop_back().unwrap(); + let copy_region = region.clone(); // 在两块内存都被取出之后才进行合并 match self.merge_blocks(region, buddy_region, exp, high_list_guard) { Err(err) => { @@ -415,8 +447,8 @@ impl MmioBuddyMemPool { /// @return Err(MmioResult) 两个内存块不是伙伴块,返回EINVAL fn merge_blocks( &self, - region_1: Box, - region_2: Box, + region_1: MmioBuddyAddrRegion, + region_2: MmioBuddyAddrRegion, exp: u32, high_list_guard: &mut SpinLockGuard, ) -> Result { @@ -444,102 +476,43 @@ impl MmioBuddyMemPool { /// @return Err(SystemError) 失败返回错误码 pub fn create_mmio( &self, - size: u32, - vm_flags: vm_flags_t, + size: usize, + _vm_flags: vm_flags_t, res_vaddr: *mut u64, res_length: *mut u64, ) -> Result { if size > PAGE_1G_SIZE || size == 0 { return Err(SystemError::EPERM); } - let mut retval: i32 = 0; + let retval: i32 = 0; // 计算前导0 - let mut size_exp: u32 = 31 - size.leading_zeros(); + #[cfg(target_arch = "x86_64")] + let mut size_exp: u32 = 63 - size.leading_zeros(); // 记录最终申请的空间大小 - let mut new_size: u32 = size; + let mut new_size = size; // 对齐要申请的空间大小 // 如果要申请的空间大小小于4k,则分配4k if size_exp < PAGE_4K_SHIFT { - new_size = PAGE_4K_SIZE; + new_size = PAGE_4K_SIZE as usize; size_exp = PAGE_4K_SHIFT; } else if (new_size & (!(1 << size_exp))) != 0 { // 向左对齐空间大小 size_exp += 1; new_size = 1 << size_exp; } - match MMIO_POOL.mmio_buddy_query_addr_region(size_exp) { + match self.mmio_buddy_query_addr_region(size_exp) { Ok(region) => { - unsafe { - *res_vaddr = region.vaddr; - *res_length = new_size as u64; - } - // 创建vma - let flags: u64 = vm_flags | (VM_IO | VM_DONTCOPY) as u64; - let len_4k: u64 = (new_size % PAGE_2M_SIZE) as u64; - let len_2m: u64 = new_size as u64 - len_4k; - let mut loop_i: u64 = 0; - // 先分配2M的vma - loop { - if loop_i >= len_2m { - break; - } - let vma: *mut *mut vm_area_struct = null_mut(); - retval = unsafe { - mm_create_vma( - &mut initial_mm, - region.vaddr + loop_i, - PAGE_2M_SIZE.into(), - flags, - null_mut(), - vma, - ) - }; - if retval != 0 { - kdebug!( - "failed to create mmio 2m vma. pid = {:?}", - current_pcb().pid - ); - unsafe { - vm_area_del(*vma); - vm_area_free(*vma); - } - return Err(SystemError::from_posix_errno(retval).unwrap()); - } - loop_i += PAGE_2M_SIZE as u64; - } - // 分配4K的vma - loop_i = len_2m; - loop { - if loop_i >= size as u64 { - break; - } - let vma: *mut *mut vm_area_struct = null_mut(); - retval = unsafe { - mm_create_vma( - &mut initial_mm, - region.vaddr + loop_i, - PAGE_4K_SIZE.into(), - flags, - null_mut(), - vma, - ) - }; - if retval != 0 { - kdebug!( - "failed to create mmio 4k vma. pid = {:?}", - current_pcb().pid - ); - unsafe { - vm_area_del(*vma); - vm_area_free(*vma); - } - return Err(SystemError::from_posix_errno(retval).unwrap()); - } - loop_i += PAGE_4K_SIZE as u64; - } + // todo: 是否需要创建vma?或者用新重写的机制去做? + // kdebug!( + // "create_mmio: vaddr = {:?}, length = {}", + // region.vaddr, + // new_size + // ); + unsafe { *res_vaddr = region.vaddr.data() as u64 }; + unsafe { *res_length = new_size as u64 }; } Err(_) => { - kdebug!("failed to create mmio vma.pid = {:?}", current_pcb().pid); + kerror!("failed to create mmio. pid = {:?}", current_pcb().pid); return Err(SystemError::ENOMEM); } } @@ -555,83 +528,62 @@ impl MmioBuddyMemPool { /// @return Ok(i32) 成功返回0 /// /// @return Err(SystemError) 失败返回错误码 - pub fn release_mmio(&self, vaddr: u64, length: u64) -> Result { - //先将要释放的空间取消映射 - unsafe { - mm_unmap(&mut initial_mm, vaddr, length, false); + pub fn release_mmio(&self, vaddr: VirtAddr, length: usize) -> Result { + assert!(vaddr.check_aligned(MMArch::PAGE_SIZE)); + assert!(length & (MMArch::PAGE_SIZE - 1) == 0); + if vaddr < self.pool_start_addr + || vaddr.data() >= self.pool_start_addr.data() + self.pool_size + { + return Err(SystemError::EINVAL); } - let mut loop_i: u64 = 0; - loop { - if loop_i >= length { - break; - } - // 获取要释放的vma的结构体 - let vma: *mut vm_area_struct = unsafe { vma_find(&mut initial_mm, vaddr + loop_i) }; - if vma == null_mut() { - kdebug!( - "mmio_release failed: vma not found. At address: {:?}, pid = {:?}", - vaddr + loop_i, - current_pcb().pid - ); - return Err(SystemError::EINVAL); - } - // 检查vma起始地址是否正确 - if unsafe { (*vma).vm_start != (vaddr + loop_i) } { - kdebug!( - "mmio_release failed: addr_start is not equal to current: {:?}. pid = {:?}", - vaddr + loop_i, - current_pcb().pid - ); - return Err(SystemError::EINVAL); - } - // 将vma对应空间归还 - match MMIO_POOL.give_back_block(unsafe { (*vma).vm_start }, unsafe { - 31 - ((*vma).vm_end - (*vma).vm_start).leading_zeros() - }) { - Ok(_) => { - loop_i += unsafe { (*vma).vm_end - (*vma).vm_start }; - unsafe { - vm_area_del(vma); - vm_area_free(vma); - } - } - Err(err) => { - // vma对应空间没有成功归还的话,就不删除vma - kdebug!( - "mmio_release give_back failed: pid = {:?}", - current_pcb().pid - ); - return Err(err); - } - } + // todo: 重构MMIO管理机制,创建类似全局的manager之类的,管理MMIO的空间? + + // 暂时认为传入的vaddr都是正确的 + let page_count = length / MMArch::PAGE_SIZE; + // 取消映射 + let mut bindings = KernelMapper::lock(); + let mut kernel_mapper = bindings.as_mut(); + if kernel_mapper.is_none() { + kwarn!("release_mmio: kernel_mapper is read only"); + return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); } + + for i in 0..page_count { + unsafe { + kernel_mapper + .as_mut() + .unwrap() + .unmap(vaddr + i * MMArch::PAGE_SIZE, true) + }; + } + + // todo: 归还到buddy + return Ok(0); } } /// @brief mmio伙伴系统内部的地址区域结构体 -pub struct MmioBuddyAddrRegion { - vaddr: u64, +#[derive(Debug, Clone)] +struct MmioBuddyAddrRegion { + vaddr: VirtAddr, } impl MmioBuddyAddrRegion { - pub fn new() -> Self { - return MmioBuddyAddrRegion { - ..Default::default() - }; + pub fn new(vaddr: VirtAddr) -> Self { + return MmioBuddyAddrRegion { vaddr }; } -} -impl Default for MmioBuddyAddrRegion { - fn default() -> Self { - MmioBuddyAddrRegion { - vaddr: Default::default(), - } + + #[allow(dead_code)] + pub fn vaddr(&self) -> VirtAddr { + return self.vaddr; } } /// @brief 空闲页数组结构体 +#[derive(Debug)] pub struct MmioFreeRegionList { /// 存储mmio_buddy的地址链表 - list: LinkedList>, + list: LinkedList, /// 空闲块的数量 num_free: i64, } @@ -652,25 +604,6 @@ impl Default for MmioFreeRegionList { } } -/// @brief 初始化mmio的伙伴系统 -#[no_mangle] -pub extern "C" fn __mmio_buddy_init() { - // 创建一堆1GB的地址块 - let cnt_1g_blocks: u32 = ((MMIO_TOP - MMIO_BASE) / PAGE_1G_SIZE as i64) as u32; - let mut vaddr_base: u64 = MMIO_BASE as u64; - for _ in 0..cnt_1g_blocks { - match MMIO_POOL.give_back_block(vaddr_base, PAGE_1G_SHIFT) { - Ok(_) => { - vaddr_base += PAGE_1G_SIZE as u64; - } - Err(_) => { - kerror!("__mmio_buddy_init failed"); - return; - } - } - } -} - /// @brief 将内存对象大小的幂转换成内存池中的数组的下标 /// /// @param exp内存大小 @@ -681,6 +614,15 @@ fn exp2index(exp: u32) -> usize { return (exp - 12) as usize; } +pub fn mmio_init() { + kdebug!("Initializing MMIO buddy memory pool..."); + // 初始化mmio内存池 + unsafe { + __MMIO_POOL = Some(MmioBuddyMemPool::new()); + } + + kinfo!("MMIO buddy memory pool init done"); +} /// @brief 创建一块mmio区域,并将vma绑定到initial_mm /// /// @param size mmio区域的大小(字节) @@ -699,7 +641,8 @@ pub extern "C" fn mmio_create( res_vaddr: *mut u64, res_length: *mut u64, ) -> i32 { - if let Err(err) = MMIO_POOL.create_mmio(size, vm_flags, res_vaddr, res_length) { + // kdebug!("mmio_create"); + if let Err(err) = mmio_pool().create_mmio(size as usize, vm_flags, res_vaddr, res_length) { return err.to_posix_errno(); } else { return 0; @@ -717,9 +660,7 @@ pub extern "C" fn mmio_create( /// @return Err(i32) 失败返回错误码 #[no_mangle] pub extern "C" fn mmio_release(vaddr: u64, length: u64) -> i32 { - if let Err(err) = MMIO_POOL.release_mmio(vaddr, length) { - return err.to_posix_errno(); - } else { - return 0; - } + return mmio_pool() + .release_mmio(VirtAddr::new(vaddr as usize), length as usize) + .unwrap_or_else(|err| err.to_posix_errno()); } diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 69f36ea5..8364e3c9 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -1,9 +1,63 @@ -use crate::include::bindings::bindings::{mm_struct, process_control_block, PAGE_OFFSET}; +use alloc::sync::Arc; + +use crate::{ + arch::MMArch, + include::bindings::bindings::{process_control_block, PAGE_OFFSET}, + syscall::SystemError, +}; + +use core::{ + cmp, + fmt::Debug, + intrinsics::unlikely, + ops::{Add, AddAssign, Sub, SubAssign}, + ptr, + sync::atomic::{AtomicBool, Ordering}, +}; + +use self::{ + allocator::page_frame::{VirtPageFrame, VirtPageFrameIter}, + page::round_up_to_page_size, + ucontext::{AddressSpace, UserMapper}, +}; pub mod allocator; +pub mod c_adapter; pub mod gfp; +pub mod kernel_mapper; pub mod mmio_buddy; +pub mod no_init; +pub mod page; pub mod syscall; +pub mod ucontext; + +/// 内核INIT进程的用户地址空间结构体(仅在process_init中初始化) +static mut __INITIAL_PROCESS_ADDRESS_SPACE: Option> = None; + +/// 获取内核INIT进程的用户地址空间结构体 +#[allow(non_snake_case)] +#[inline(always)] +pub fn INITIAL_PROCESS_ADDRESS_SPACE() -> Arc { + unsafe { + return __INITIAL_PROCESS_ADDRESS_SPACE + .as_ref() + .expect("INITIAL_PROCESS_ADDRESS_SPACE is null") + .clone(); + } +} + +/// 设置内核INIT进程的用户地址空间结构体全局变量 +#[allow(non_snake_case)] +pub unsafe fn set_INITIAL_PROCESS_ADDRESS_SPACE(address_space: Arc) { + static INITIALIZED: AtomicBool = AtomicBool::new(false); + if INITIALIZED + .compare_exchange(false, true, Ordering::SeqCst, Ordering::Acquire) + .is_err() + { + panic!("INITIAL_PROCESS_ADDRESS_SPACE is already initialized"); + } + __INITIAL_PROCESS_ADDRESS_SPACE = Some(address_space); +} /// @brief 将内核空间的虚拟地址转换为物理地址 #[inline(always)] @@ -17,10 +71,561 @@ pub fn phys_2_virt(addr: usize) -> usize { addr + PAGE_OFFSET as usize } -// ====== 重构内存管理后,请删除18-24行 ====== +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub enum PageTableKind { + /// 用户可访问的页表 + User, + /// 内核页表 + Kernel, +} + +/// 物理内存地址 +#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Hash)] +#[repr(transparent)] +pub struct PhysAddr(usize); + +impl PhysAddr { + #[inline(always)] + pub const fn new(address: usize) -> Self { + Self(address) + } + + /// @brief 获取物理地址的值 + #[inline(always)] + pub fn data(&self) -> usize { + self.0 + } + + /// @brief 将物理地址加上一个偏移量 + #[inline(always)] + pub fn add(self, offset: usize) -> Self { + Self(self.0 + offset) + } + + /// @brief 判断物理地址是否按照指定要求对齐 + #[inline(always)] + pub fn check_aligned(&self, align: usize) -> bool { + return self.0 & (align - 1) == 0; + } + + #[inline(always)] + pub fn is_null(&self) -> bool { + return self.0 == 0; + } +} + +impl Debug for PhysAddr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "PhysAddr({:#x})", self.0) + } +} + +impl core::ops::Add for PhysAddr { + type Output = Self; + + #[inline(always)] + fn add(self, rhs: usize) -> Self::Output { + return Self(self.0 + rhs); + } +} + +impl core::ops::AddAssign for PhysAddr { + #[inline(always)] + fn add_assign(&mut self, rhs: usize) { + self.0 += rhs; + } +} + +impl core::ops::Add for PhysAddr { + type Output = Self; + + #[inline(always)] + fn add(self, rhs: PhysAddr) -> Self::Output { + return Self(self.0 + rhs.0); + } +} + +impl core::ops::AddAssign for PhysAddr { + #[inline(always)] + fn add_assign(&mut self, rhs: PhysAddr) { + self.0 += rhs.0; + } +} + +impl core::ops::Sub for PhysAddr { + type Output = Self; + + #[inline(always)] + fn sub(self, rhs: usize) -> Self::Output { + return Self(self.0 - rhs); + } +} + +impl core::ops::SubAssign for PhysAddr { + #[inline(always)] + fn sub_assign(&mut self, rhs: usize) { + self.0 -= rhs; + } +} + +impl core::ops::Sub for PhysAddr { + type Output = usize; + + #[inline(always)] + fn sub(self, rhs: PhysAddr) -> Self::Output { + return self.0 - rhs.0; + } +} + +impl core::ops::SubAssign for PhysAddr { + #[inline(always)] + fn sub_assign(&mut self, rhs: PhysAddr) { + self.0 -= rhs.0; + } +} + +/// 虚拟内存地址 +#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Hash)] +#[repr(transparent)] +pub struct VirtAddr(usize); + +impl VirtAddr { + #[inline(always)] + pub const fn new(address: usize) -> Self { + return Self(address); + } + + /// @brief 获取虚拟地址的值 + #[inline(always)] + pub fn data(&self) -> usize { + return self.0; + } + + /// @brief 判断虚拟地址的类型 + #[inline(always)] + pub fn kind(&self) -> PageTableKind { + if self.check_user() { + return PageTableKind::User; + } else { + return PageTableKind::Kernel; + } + } + + /// @brief 判断虚拟地址是否按照指定要求对齐 + #[inline(always)] + pub fn check_aligned(&self, align: usize) -> bool { + return self.0 & (align - 1) == 0; + } + + /// @brief 判断虚拟地址是否在用户空间 + #[inline(always)] + pub fn check_user(&self) -> bool { + if self < &MMArch::USER_END_VADDR { + return true; + } else { + return false; + } + } + + #[inline(always)] + pub fn as_ptr(self) -> *mut T { + return self.0 as *mut T; + } + + #[inline(always)] + pub fn is_null(&self) -> bool { + return self.0 == 0; + } +} + +impl Add for VirtAddr { + type Output = Self; + + #[inline(always)] + fn add(self, rhs: VirtAddr) -> Self::Output { + return Self(self.0 + rhs.0); + } +} + +impl Add for VirtAddr { + type Output = Self; + + #[inline(always)] + fn add(self, rhs: usize) -> Self::Output { + return Self(self.0 + rhs); + } +} + +impl Sub for VirtAddr { + type Output = usize; + + #[inline(always)] + fn sub(self, rhs: VirtAddr) -> Self::Output { + return self.0 - rhs.0; + } +} + +impl Sub for VirtAddr { + type Output = Self; + + #[inline(always)] + fn sub(self, rhs: usize) -> Self::Output { + return Self(self.0 - rhs); + } +} + +impl AddAssign for VirtAddr { + #[inline(always)] + fn add_assign(&mut self, rhs: usize) { + self.0 += rhs; + } +} + +impl AddAssign for VirtAddr { + #[inline(always)] + fn add_assign(&mut self, rhs: VirtAddr) { + self.0 += rhs.0; + } +} + +impl SubAssign for VirtAddr { + #[inline(always)] + fn sub_assign(&mut self, rhs: usize) { + self.0 -= rhs; + } +} + +impl SubAssign for VirtAddr { + #[inline(always)] + fn sub_assign(&mut self, rhs: VirtAddr) { + self.0 -= rhs.0; + } +} + +impl Debug for VirtAddr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "VirtAddr({:#x})", self.0) + } +} + +/// @brief 物理内存区域 +#[derive(Clone, Copy, Debug)] +pub struct PhysMemoryArea { + /// 物理基地址 + pub base: PhysAddr, + /// 该区域的物理内存大小 + pub size: usize, +} + +pub trait MemoryManagementArch: Clone + Copy + Debug { + /// 页面大小的shift(假如页面4K,那么这个值就是12,因为2^12=4096) + const PAGE_SHIFT: usize; + /// 每个页表的页表项数目。(以2^n次幂来表示)假如有512个页表项,那么这个值就是9 + const PAGE_ENTRY_SHIFT: usize; + /// 页表层级数量 + const PAGE_LEVELS: usize; + + /// 页表项的有效位的index(假如页表项的第0-51位有效,那么这个值就是52) + const ENTRY_ADDRESS_SHIFT: usize; + /// 页面的页表项的默认值 + const ENTRY_FLAG_DEFAULT_PAGE: usize; + /// 页表的页表项的默认值 + const ENTRY_FLAG_DEFAULT_TABLE: usize; + /// 页表项的present位被置位之后的值 + const ENTRY_FLAG_PRESENT: usize; + /// 页表项为read only时的值 + const ENTRY_FLAG_READONLY: usize; + /// 页表项为可读写状态的值 + const ENTRY_FLAG_READWRITE: usize; + /// 页面项标记页面为user page的值 + const ENTRY_FLAG_USER: usize; + /// 页面项标记页面为write through的值 + const ENTRY_FLAG_WRITE_THROUGH: usize; + /// 页面项标记页面为cache disable的值 + const ENTRY_FLAG_CACHE_DISABLE: usize; + /// 标记当前页面不可执行的标志位(Execute disable)(也就是说,不能从这段内存里面获取处理器指令) + const ENTRY_FLAG_NO_EXEC: usize; + /// 标记当前页面可执行的标志位(Execute enable) + const ENTRY_FLAG_EXEC: usize; + + /// 虚拟地址与物理地址的偏移量 + const PHYS_OFFSET: usize; + + /// 每个页面的大小 + const PAGE_SIZE: usize = 1 << Self::PAGE_SHIFT; + /// 通过这个mask,获取地址的页内偏移量 + const PAGE_OFFSET_MASK: usize = Self::PAGE_SIZE - 1; + /// 页表项的地址、数据部分的shift。 + /// 打个比方,如果这个值为52,那么意味着页表项的[0, 52)位,用于表示地址以及其他的标志位 + const PAGE_ADDRESS_SHIFT: usize = Self::PAGE_LEVELS * Self::PAGE_ENTRY_SHIFT + Self::PAGE_SHIFT; + /// 最大的虚拟地址(对于不同的架构,由于上述PAGE_ADDRESS_SHIFT可能包括了reserved bits, 事实上能表示的虚拟地址应该比这个值要小) + const PAGE_ADDRESS_SIZE: usize = 1 << Self::PAGE_ADDRESS_SHIFT; + /// 页表项的值与这个常量进行与运算,得到的结果是所填写的物理地址 + const PAGE_ADDRESS_MASK: usize = Self::PAGE_ADDRESS_SIZE - Self::PAGE_SIZE; + /// 每个页表项的大小 + const PAGE_ENTRY_SIZE: usize = 1 << (Self::PAGE_SHIFT - Self::PAGE_ENTRY_SHIFT); + /// 每个页表的页表项数目 + const PAGE_ENTRY_NUM: usize = 1 << Self::PAGE_ENTRY_SHIFT; + /// 该字段用于根据虚拟地址,获取该虚拟地址在对应的页表中是第几个页表项 + const PAGE_ENTRY_MASK: usize = Self::PAGE_ENTRY_NUM - 1; + + const PAGE_NEGATIVE_MASK: usize = !((Self::PAGE_ADDRESS_SIZE) - 1); + + const ENTRY_ADDRESS_SIZE: usize = 1 << Self::ENTRY_ADDRESS_SHIFT; + /// 该mask用于获取页表项中地址字段 + const ENTRY_ADDRESS_MASK: usize = Self::ENTRY_ADDRESS_SIZE - Self::PAGE_SIZE; + /// 这个mask用于获取页表项中的flags + const ENTRY_FLAGS_MASK: usize = !Self::ENTRY_ADDRESS_MASK; + + /// 用户空间的最高地址 + const USER_END_VADDR: VirtAddr; + /// 用户堆的起始地址 + const USER_BRK_START: VirtAddr; + /// 用户栈起始地址(向下生长,不包含该值) + const USER_STACK_START: VirtAddr; + + /// @brief 用于初始化内存管理模块与架构相关的信息。 + /// 该函数应调用其他模块的接口,生成内存区域结构体,提供给BumpAllocator使用 + unsafe fn init() -> &'static [PhysMemoryArea]; + + /// @brief 读取指定虚拟地址的值,并假设它是类型T的指针 + #[inline(always)] + unsafe fn read(address: VirtAddr) -> T { + return ptr::read(address.data() as *const T); + } + + /// @brief 将value写入到指定的虚拟地址 + #[inline(always)] + unsafe fn write(address: VirtAddr, value: T) { + ptr::write(address.data() as *mut T, value); + } + + #[inline(always)] + unsafe fn write_bytes(address: VirtAddr, value: u8, count: usize) { + ptr::write_bytes(address.data() as *mut u8, value, count); + } + + /// @brief 刷新TLB中,关于指定虚拟地址的条目 + unsafe fn invalidate_page(address: VirtAddr); + + /// @brief 刷新TLB中,所有的条目 + unsafe fn invalidate_all(); + + /// @brief 获取顶级页表的物理地址 + unsafe fn table(table_kind: PageTableKind) -> PhysAddr; + + /// @brief 设置顶级页表的物理地址到处理器中 + unsafe fn set_table(table_kind: PageTableKind, table: PhysAddr); + + /// @brief 将物理地址转换为虚拟地址. + /// + /// @param phys 物理地址 + /// + /// @return 转换后的虚拟地址。如果转换失败,返回None + #[inline(always)] + unsafe fn phys_2_virt(phys: PhysAddr) -> Option { + if let Some(vaddr) = phys.data().checked_add(Self::PHYS_OFFSET) { + return Some(VirtAddr::new(vaddr)); + } else { + return None; + } + } + + /// 将虚拟地址转换为物理地址 + /// + /// ## 参数 + /// + /// - `virt` 虚拟地址 + /// + /// ## 返回值 + /// + /// 转换后的物理地址。如果转换失败,返回None + #[inline(always)] + unsafe fn virt_2_phys(virt: VirtAddr) -> Option { + if let Some(paddr) = virt.data().checked_sub(Self::PHYS_OFFSET) { + return Some(PhysAddr::new(paddr)); + } else { + return None; + } + } + + /// @brief 判断指定的虚拟地址是否正确(符合规范) + fn virt_is_valid(virt: VirtAddr) -> bool; + + /// 获取内存管理初始化时,创建的第一个内核页表的地址 + fn initial_page_table() -> PhysAddr; + + /// 初始化新的usermapper,为用户进程创建页表 + fn setup_new_usermapper() -> Result; +} + +/// @brief 虚拟地址范围 +/// 该结构体用于表示一个虚拟地址范围,包括起始地址与大小 +/// +/// 请注意与VMA进行区分,该结构体被VMA所包含 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct VirtRegion { + start: VirtAddr, + size: usize, +} + +#[allow(dead_code)] +impl VirtRegion { + /// # 创建一个新的虚拟地址范围 + pub fn new(start: VirtAddr, size: usize) -> Self { + VirtRegion { start, size } + } + + /// 获取虚拟地址范围的起始地址 + #[inline(always)] + pub fn start(&self) -> VirtAddr { + self.start + } + + /// 获取虚拟地址范围的截止地址(不包括返回的地址) + #[inline(always)] + pub fn end(&self) -> VirtAddr { + return self.start().add(self.size); + } + + /// # Create a new VirtRegion from a range [start, end) + /// + /// If end <= start, return None + pub fn between(start: VirtAddr, end: VirtAddr) -> Option { + if unlikely(end.data() <= start.data()) { + return None; + } + let size = end.data() - start.data(); + return Some(VirtRegion::new(start, size)); + } + + /// # 取两个虚拟地址范围的交集 + /// + /// 如果两个虚拟地址范围没有交集,返回None + pub fn intersect(&self, other: &VirtRegion) -> Option { + let start = self.start.max(other.start); + let end = self.end().min(other.end()); + return VirtRegion::between(start, end); + } + + /// 设置虚拟地址范围的起始地址 + #[inline(always)] + pub fn set_start(&mut self, start: VirtAddr) { + self.start = start; + } + + #[inline(always)] + pub fn size(&self) -> usize { + self.size + } + + /// 设置虚拟地址范围的大小 + #[inline(always)] + pub fn set_size(&mut self, size: usize) { + self.size = size; + } + + /// 判断虚拟地址范围是否为空 + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.size == 0 + } + + /// 将虚拟地址区域的大小向上对齐到页大小 + #[inline(always)] + pub fn round_up_size_to_page(self) -> Self { + return VirtRegion::new(self.start, round_up_to_page_size(self.size)); + } + + /// 判断两个虚拟地址范围是否由于具有交集而导致冲突 + #[inline(always)] + pub fn collide(&self, other: &VirtRegion) -> bool { + return self.intersect(other).is_some(); + } + + pub fn iter_pages(&self) -> VirtPageFrameIter { + return VirtPageFrame::iter_range( + VirtPageFrame::new(self.start), + VirtPageFrame::new(self.end()), + ); + } + + /// 获取[self.start(), region.start())的虚拟地址范围 + /// + /// 如果self.start() >= region.start(),返回None + pub fn before(self, region: &VirtRegion) -> Option { + return Self::between(self.start(), region.start()); + } + + /// 获取[region.end(),self.end())的虚拟地址范围 + /// + /// 如果 self.end() >= region.end() ,返回None + pub fn after(self, region: &VirtRegion) -> Option { + // if self.end() > region.end() none + return Self::between(region.end(), self.end()); + } + + /// 把当前虚拟地址范围内的某个虚拟地址,转换为另一个虚拟地址范围内的虚拟地址 + /// + /// 如果vaddr不在当前虚拟地址范围内,返回None + /// + /// 如果vaddr在当前虚拟地址范围内,返回vaddr在new_base中的虚拟地址 + pub fn rebase(self, vaddr: VirtAddr, new_base: &VirtRegion) -> Option { + if !self.contains(vaddr) { + return None; + } + let offset = vaddr.data() - self.start().data(); + let new_start = new_base.start().data() + offset; + return Some(VirtAddr::new(new_start)); + } + + /// 判断虚拟地址范围是否包含指定的虚拟地址 + pub fn contains(&self, addr: VirtAddr) -> bool { + return self.start() <= addr && addr < self.end(); + } + + /// 创建当前虚拟地址范围的页面迭代器 + pub fn pages(&self) -> VirtPageFrameIter { + return VirtPageFrame::iter_range( + VirtPageFrame::new(self.start()), + VirtPageFrame::new(self.end()), + ); + } +} + +impl PartialOrd for VirtRegion { + fn partial_cmp(&self, other: &Self) -> Option { + return self.start.partial_cmp(&other.start); + } +} + +impl Ord for VirtRegion { + fn cmp(&self, other: &Self) -> cmp::Ordering { + return self.start.cmp(&other.start); + } +} + +/// ## 判断虚拟地址是否超出了用户空间 +/// +/// 如果虚拟地址超出了用户空间,返回Err(SystemError::EFAULT). +/// 如果end < start,返回Err(SystemError::EOVERFLOW) +/// +/// 否则返回Ok(()) +pub fn verify_area(addr: VirtAddr, size: usize) -> Result<(), SystemError> { + let end = addr.add(size); + if unlikely(end.data() < addr.data()) { + return Err(SystemError::EOVERFLOW); + } + + if !addr.check_user() || !end.check_user() { + return Err(SystemError::EFAULT); + } + + return Ok(()); +} +// ====== 重构内存管理、进程管理后,请删除这几行 BEGIN ====== //BUG pcb问题 unsafe impl Send for process_control_block {} unsafe impl Sync for process_control_block {} -unsafe impl Send for mm_struct {} -unsafe impl Sync for mm_struct {} +// ====== 重构内存管理后,请删除这几行 END ======= diff --git a/kernel/src/mm/no_init.rs b/kernel/src/mm/no_init.rs new file mode 100644 index 00000000..352cd396 --- /dev/null +++ b/kernel/src/mm/no_init.rs @@ -0,0 +1,79 @@ +//! 该文件用于系统启动早期,内存管理器初始化之前,提供一些简单的内存映射功能 +//! +//! 这里假设在内核引导文件中,已经填写了前100M的内存映射关系,因此这里不需要任何动态分配。 +//! +//! 映射关系为: +//! +//! 虚拟地址 0-100M与虚拟地址 0x8000_0000_0000 - 0x8000_0640_0000 之间具有重映射关系。 +//! 也就是说,他们的第二级页表在最顶级页表中,占用了第0和第256个页表项。 +//! + +use crate::mm::{MMArch, MemoryManagementArch, PhysAddr}; +use core::marker::PhantomData; + +use super::{ + allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}, + page::PageFlags, + PageTableKind, VirtAddr, +}; + +/// 伪分配器 +struct PseudoAllocator { + phantom: PhantomData, +} + +impl PseudoAllocator { + pub const fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// 为NoInitAllocator实现FrameAllocator +impl FrameAllocator for PseudoAllocator { + unsafe fn allocate(&mut self, _count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { + panic!("NoInitAllocator can't allocate page frame"); + } + + unsafe fn free(&mut self, _address: PhysAddr, _count: PageFrameCount) { + panic!("NoInitAllocator can't free page frame"); + } + /// @brief: 获取内存区域页帧的使用情况 + /// @param self + /// @return 页帧的使用情况 + unsafe fn usage(&self) -> PageFrameUsage { + panic!("NoInitAllocator can't get page frame usage"); + } +} + +/// Use pseudo mapper to map physical memory to virtual memory. +/// +/// ## Safety +/// +/// 调用该函数时,必须保证内存管理器尚未初始化。否则将导致未定义的行为 +/// +/// 并且,内核引导文件必须以4K页为粒度,填写了前100M的内存映射关系。(具体以本文件开头的注释为准) +pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { + assert!(vaddr.check_aligned(MMArch::PAGE_SIZE)); + assert!(paddr.check_aligned(MMArch::PAGE_SIZE)); + + let mut pseudo_allocator = PseudoAllocator::::new(); + + let mut mapper = crate::mm::page::PageMapper::::new( + PageTableKind::Kernel, + MMArch::table(PageTableKind::Kernel), + &mut pseudo_allocator, + ); + + let flags: PageFlags = PageFlags::new().set_write(true).set_execute(true); + + for i in 0..count.data() { + let vaddr = vaddr + i * MMArch::PAGE_SIZE; + let paddr = paddr + i * MMArch::PAGE_SIZE; + let flusher = mapper.map_phys(vaddr, paddr, flags).unwrap(); + flusher.ignore(); + } + + mapper.make_current(); +} diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs new file mode 100644 index 00000000..13023687 --- /dev/null +++ b/kernel/src/mm/page.rs @@ -0,0 +1,924 @@ +use core::{ + fmt::{self, Debug, Error, Formatter}, + marker::PhantomData, + mem, + ops::Add, + sync::atomic::{compiler_fence, Ordering}, +}; + +use crate::{ + arch::{interrupt::ipi::send_ipi, MMArch}, + exception::ipi::{IpiKind, IpiTarget}, + kerror, kwarn, +}; + +use super::{ + allocator::page_frame::FrameAllocator, syscall::ProtFlags, MemoryManagementArch, PageTableKind, + PhysAddr, VirtAddr, +}; + +#[derive(Debug)] +pub struct PageTable { + /// 当前页表表示的虚拟地址空间的起始地址 + base: VirtAddr, + /// 当前页表所在的物理地址 + phys: PhysAddr, + /// 当前页表的层级(请注意,最顶级页表的level为[Arch::PAGE_LEVELS - 1]) + level: usize, + phantom: PhantomData, +} + +#[allow(dead_code)] +impl PageTable { + pub unsafe fn new(base: VirtAddr, phys: PhysAddr, level: usize) -> Self { + Self { + base, + phys, + level, + phantom: PhantomData, + } + } + + /// 获取顶级页表 + /// + /// ## 参数 + /// + /// - table_kind 页表类型 + /// + /// ## 返回值 + /// + /// 返回顶级页表 + pub unsafe fn top_level_table(table_kind: PageTableKind) -> Self { + return Self::new( + VirtAddr::new(0), + Arch::table(table_kind), + Arch::PAGE_LEVELS - 1, + ); + } + + /// 获取当前页表的物理地址 + #[inline(always)] + pub fn phys(&self) -> PhysAddr { + self.phys + } + + /// 当前页表表示的虚拟地址空间的起始地址 + #[inline(always)] + pub fn base(&self) -> VirtAddr { + self.base + } + + /// 获取当前页表的层级 + #[inline(always)] + pub fn level(&self) -> usize { + self.level + } + + /// 获取当前页表自身所在的虚拟地址 + #[inline(always)] + pub unsafe fn virt(&self) -> VirtAddr { + return Arch::phys_2_virt(self.phys).unwrap(); + } + + /// 获取第i个页表项所表示的虚拟内存空间的起始地址 + pub fn entry_base(&self, i: usize) -> Option { + if i < Arch::PAGE_ENTRY_NUM { + let shift = self.level * Arch::PAGE_ENTRY_SHIFT + Arch::PAGE_SHIFT; + return Some(self.base.add(i << shift)); + } else { + return None; + } + } + + /// 获取当前页表的第i个页表项所在的虚拟地址(注意与entry_base进行区分) + pub unsafe fn entry_virt(&self, i: usize) -> Option { + if i < Arch::PAGE_ENTRY_NUM { + return Some(self.virt().add(i * Arch::PAGE_ENTRY_SIZE)); + } else { + return None; + } + } + + /// 获取当前页表的第i个页表项 + pub unsafe fn entry(&self, i: usize) -> Option> { + let entry_virt = self.entry_virt(i)?; + return Some(PageEntry::new(Arch::read::(entry_virt))); + } + + /// 设置当前页表的第i个页表项 + pub unsafe fn set_entry(&self, i: usize, entry: PageEntry) -> Option<()> { + let entry_virt = self.entry_virt(i)?; + Arch::write::(entry_virt, entry.data()); + return Some(()); + } + + /// 判断当前页表的第i个页表项是否已经填写了值 + /// + /// ## 参数 + /// - Some(true) 如果已经填写了值 + /// - Some(false) 如果未填写值 + /// - None 如果i超出了页表项的范围 + pub fn entry_mapped(&self, i: usize) -> Option { + let etv = unsafe { self.entry_virt(i) }?; + if unsafe { Arch::read::(etv) } != 0 { + return Some(true); + } else { + return Some(false); + } + } + + /// 根据虚拟地址,获取对应的页表项在页表中的下标 + /// + /// ## 参数 + /// + /// - addr: 虚拟地址 + /// + /// ## 返回值 + /// + /// 页表项在页表中的下标。如果addr不在当前页表所表示的虚拟地址空间中,则返回None + pub unsafe fn index_of(&self, addr: VirtAddr) -> Option { + let addr = VirtAddr::new(addr.data() & Arch::PAGE_ADDRESS_MASK); + let shift = self.level * Arch::PAGE_ENTRY_SHIFT + Arch::PAGE_SHIFT; + + let mask = (MMArch::PAGE_ENTRY_NUM << shift) - 1; + if addr < self.base || addr >= self.base.add(mask) { + return None; + } else { + return Some((addr.data() >> shift) & MMArch::PAGE_ENTRY_MASK); + } + } + + /// 获取第i个页表项指向的下一级页表 + pub unsafe fn next_level_table(&self, index: usize) -> Option { + if self.level == 0 { + return None; + } + + // 返回下一级页表 + return Some(PageTable::new( + self.entry_base(index)?, + self.entry(index)?.address().ok()?, + self.level - 1, + )); + } +} + +/// 页表项 +#[derive(Copy, Clone)] +pub struct PageEntry { + data: usize, + phantom: PhantomData, +} + +impl Debug for PageEntry { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + f.write_fmt(format_args!("PageEntry({:#x})", self.data)) + } +} + +impl PageEntry { + #[inline(always)] + pub fn new(data: usize) -> Self { + Self { + data, + phantom: PhantomData, + } + } + + #[inline(always)] + pub fn data(&self) -> usize { + self.data + } + + /// 获取当前页表项指向的物理地址 + /// + /// ## 返回值 + /// + /// - Ok(PhysAddr) 如果当前页面存在于物理内存中, 返回物理地址 + /// - Err(PhysAddr) 如果当前页表项不存在, 返回物理地址 + #[inline(always)] + pub fn address(&self) -> Result { + let paddr = PhysAddr::new(self.data & Arch::PAGE_ADDRESS_MASK); + + if self.present() { + Ok(paddr) + } else { + Err(paddr) + } + } + + #[inline(always)] + pub fn flags(&self) -> PageFlags { + unsafe { PageFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } + } + + #[inline(always)] + pub fn set_flags(&mut self, flags: PageFlags) { + self.data = (self.data & !Arch::ENTRY_FLAGS_MASK) | flags.data(); + } + + #[inline(always)] + pub fn present(&self) -> bool { + return self.data & Arch::ENTRY_FLAG_PRESENT != 0; + } +} + +/// 页表项的标志位 +#[derive(Copy, Clone, Hash)] +pub struct PageFlags { + data: usize, + phantom: PhantomData, +} + +#[allow(dead_code)] +impl PageFlags { + #[inline(always)] + pub fn new() -> Self { + let mut r = unsafe { + Self::from_data( + Arch::ENTRY_FLAG_DEFAULT_PAGE + | Arch::ENTRY_FLAG_READONLY + | Arch::ENTRY_FLAG_NO_EXEC, + ) + }; + + #[cfg(target_arch = "x86_64")] + { + if crate::arch::mm::X86_64MMArch::is_xd_reserved() { + r = r.set_execute(true); + } + } + + return r; + } + + /// 根据ProtFlags生成PageFlags + /// + /// ## 参数 + /// + /// - prot_flags: 页的保护标志 + /// - user: 用户空间是否可访问 + pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> PageFlags { + let flags: PageFlags = PageFlags::new() + .set_user(user) + .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) + .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); + + return flags; + } + + #[inline(always)] + pub fn data(&self) -> usize { + self.data + } + + #[inline(always)] + pub const unsafe fn from_data(data: usize) -> Self { + return Self { + data: data, + phantom: PhantomData, + }; + } + + /// 为新页表的页表项设置默认值 + /// + /// 默认值为: + /// - present + /// - read only + /// - kernel space + /// - no exec + #[inline(always)] + pub fn new_page_table(user: bool) -> Self { + return unsafe { + let r = Self::from_data(Arch::ENTRY_FLAG_DEFAULT_TABLE | Arch::ENTRY_FLAG_READWRITE); + if user { + r.set_user(true) + } else { + r + } + }; + } + + /// 取得当前页表项的所有权,更新当前页表项的标志位,并返回更新后的页表项。 + /// + /// ## 参数 + /// - flag 要更新的标志位的值 + /// - value 如果为true,那么将flag对应的位设置为1,否则设置为0 + /// + /// ## 返回值 + /// + /// 更新后的页表项 + #[inline(always)] + #[must_use] + pub fn update_flags(mut self, flag: usize, value: bool) -> Self { + if value { + self.data |= flag; + } else { + self.data &= !flag; + } + return self; + } + + /// 判断当前页表项是否存在指定的flag(只有全部flag都存在才返回true) + #[inline(always)] + pub fn has_flag(&self, flag: usize) -> bool { + return self.data & flag == flag; + } + + #[inline(always)] + pub fn present(&self) -> bool { + return self.has_flag(Arch::ENTRY_FLAG_PRESENT); + } + + /// 设置当前页表项的权限 + /// + /// @param value 如果为true,那么将当前页表项的权限设置为用户态可访问 + #[must_use] + #[inline(always)] + pub fn set_user(self, value: bool) -> Self { + return self.update_flags(Arch::ENTRY_FLAG_USER, value); + } + + /// 用户态是否可以访问当前页表项 + #[inline(always)] + pub fn has_user(&self) -> bool { + return self.has_flag(Arch::ENTRY_FLAG_USER); + } + + /// 设置当前页表项的可写性, 如果为true,那么将当前页表项的权限设置为可写, 否则设置为只读 + /// + /// ## 返回值 + /// + /// 更新后的页表项. + /// + /// **请注意,**本函数会取得当前页表项的所有权,因此返回的页表项不是原来的页表项 + #[must_use] + #[inline(always)] + pub fn set_write(self, value: bool) -> Self { + // 有的架构同时具有可写和不可写的标志位,因此需要同时更新 + return self + .update_flags(Arch::ENTRY_FLAG_READONLY, !value) + .update_flags(Arch::ENTRY_FLAG_READWRITE, value); + } + + /// 当前页表项是否可写 + #[inline(always)] + pub fn has_write(&self) -> bool { + // 有的架构同时具有可写和不可写的标志位,因此需要同时判断 + return self.data & (Arch::ENTRY_FLAG_READWRITE | Arch::ENTRY_FLAG_READONLY) + == Arch::ENTRY_FLAG_READWRITE; + } + + /// 设置当前页表项的可执行性, 如果为true,那么将当前页表项的权限设置为可执行, 否则设置为不可执行 + #[must_use] + #[inline(always)] + pub fn set_execute(self, mut value: bool) -> Self { + #[cfg(target_arch = "x86_64")] + { + // 如果xd位被保留,那么将可执行性设置为true + if crate::arch::mm::X86_64MMArch::is_xd_reserved() { + value = true; + } + } + + // 有的架构同时具有可执行和不可执行的标志位,因此需要同时更新 + return self + .update_flags(Arch::ENTRY_FLAG_NO_EXEC, !value) + .update_flags(Arch::ENTRY_FLAG_EXEC, value); + } + + /// 当前页表项是否可执行 + #[inline(always)] + pub fn has_execute(&self) -> bool { + // 有的架构同时具有可执行和不可执行的标志位,因此需要同时判断 + return self.data & (Arch::ENTRY_FLAG_EXEC | Arch::ENTRY_FLAG_NO_EXEC) + == Arch::ENTRY_FLAG_EXEC; + } + + /// 设置当前页表项的缓存策略 + /// + /// ## 参数 + /// + /// - value: 如果为true,那么将当前页表项的缓存策略设置为不缓存。 + #[inline(always)] + pub fn set_page_cache_disable(self, value: bool) -> Self { + return self.update_flags(Arch::ENTRY_FLAG_CACHE_DISABLE, value); + } + + /// 获取当前页表项的缓存策略 + /// + /// ## 返回值 + /// + /// 如果当前页表项的缓存策略为不缓存,那么返回true,否则返回false。 + #[inline(always)] + pub fn has_page_cache_disable(&self) -> bool { + return self.has_flag(Arch::ENTRY_FLAG_CACHE_DISABLE); + } + + /// 设置当前页表项的写穿策略 + /// + /// ## 参数 + /// + /// - value: 如果为true,那么将当前页表项的写穿策略设置为写穿。 + #[inline(always)] + pub fn set_page_write_through(self, value: bool) -> Self { + return self.update_flags(Arch::ENTRY_FLAG_WRITE_THROUGH, value); + } + + /// 获取当前页表项的写穿策略 + /// + /// ## 返回值 + /// + /// 如果当前页表项的写穿策略为写穿,那么返回true,否则返回false。 + #[inline(always)] + pub fn has_page_write_through(&self) -> bool { + return self.has_flag(Arch::ENTRY_FLAG_WRITE_THROUGH); + } + + /// MMIO内存的页表项标志 + #[inline(always)] + pub fn mmio_flags() -> Self { + return Self::new() + .set_user(false) + .set_write(true) + .set_execute(true) + .set_page_cache_disable(true) + .set_page_write_through(true); + } +} + +impl fmt::Debug for PageFlags { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PageFlags") + .field("bits", &format_args!("{:#0x}", self.data)) + .field("present", &self.present()) + .field("has_write", &self.has_write()) + .field("has_execute", &self.has_execute()) + .field("has_user", &self.has_user()) + .finish() + } +} + +/// 页表映射器 +#[derive(Hash)] +pub struct PageMapper { + /// 页表类型 + table_kind: PageTableKind, + /// 根页表物理地址 + table_paddr: PhysAddr, + /// 页分配器 + frame_allocator: F, + phantom: PhantomData Arch>, +} + +impl PageMapper { + /// 创建新的页面映射器 + /// + /// ## 参数 + /// - table_kind 页表类型 + /// - table_paddr 根页表物理地址 + /// - allocator 页分配器 + /// + /// ## 返回值 + /// + /// 页面映射器 + pub unsafe fn new(table_kind: PageTableKind, table_paddr: PhysAddr, allocator: F) -> Self { + return Self { + table_kind, + table_paddr, + frame_allocator: allocator, + phantom: PhantomData, + }; + } + + /// 创建页表,并为这个页表创建页面映射器 + pub unsafe fn create(table_kind: PageTableKind, mut allocator: F) -> Option { + let table_paddr = allocator.allocate_one()?; + // 清空页表 + let table_vaddr = Arch::phys_2_virt(table_paddr)?; + Arch::write_bytes(table_vaddr, 0, Arch::PAGE_SIZE); + return Some(Self::new(table_kind, table_paddr, allocator)); + } + + /// 获取当前页表的页面映射器 + #[inline(always)] + pub unsafe fn current(table_kind: PageTableKind, allocator: F) -> Self { + let table_paddr = Arch::table(table_kind); + return Self::new(table_kind, table_paddr, allocator); + } + + /// 判断当前页表分配器所属的页表是否是当前页表 + #[inline(always)] + pub fn is_current(&self) -> bool { + return unsafe { self.table().phys() == Arch::table(self.table_kind) }; + } + + /// 将当前页表分配器所属的页表设置为当前页表 + #[inline(always)] + pub unsafe fn make_current(&self) { + Arch::set_table(self.table_kind, self.table_paddr); + } + + /// 获取当前页表分配器所属的根页表的结构体 + #[inline(always)] + pub fn table(&self) -> PageTable { + // 由于只能通过new方法创建PageMapper,因此这里假定table_paddr是有效的 + return unsafe { + PageTable::new(VirtAddr::new(0), self.table_paddr, Arch::PAGE_LEVELS - 1) + }; + } + + /// 获取当前PageMapper所对应的页分配器实例的引用 + #[inline(always)] + #[allow(dead_code)] + pub fn allocator_ref(&self) -> &F { + return &self.frame_allocator; + } + + /// 获取当前PageMapper所对应的页分配器实例的可变引用 + #[inline(always)] + pub fn allocator_mut(&mut self) -> &mut F { + return &mut self.frame_allocator; + } + + /// 从当前PageMapper的页分配器中分配一个物理页,并将其映射到指定的虚拟地址 + pub unsafe fn map( + &mut self, + virt: VirtAddr, + flags: PageFlags, + ) -> Option> { + compiler_fence(Ordering::SeqCst); + let phys: PhysAddr = self.frame_allocator.allocate_one()?; + compiler_fence(Ordering::SeqCst); + return self.map_phys(virt, phys, flags); + } + + /// 映射一个物理页到指定的虚拟地址 + pub unsafe fn map_phys( + &mut self, + virt: VirtAddr, + phys: PhysAddr, + flags: PageFlags, + ) -> Option> { + // 验证虚拟地址和物理地址是否对齐 + if !(virt.check_aligned(Arch::PAGE_SIZE) && phys.check_aligned(Arch::PAGE_SIZE)) { + kerror!( + "Try to map unaligned page: virt={:?}, phys={:?}", + virt, + phys + ); + return None; + } + let virt = VirtAddr::new(virt.data() & (!Arch::PAGE_NEGATIVE_MASK)); + + // TODO: 验证flags是否合法 + + // 创建页表项 + let entry = PageEntry::new(phys.data() | flags.data()); + let mut table = self.table(); + loop { + let i = table.index_of(virt)?; + assert!(i < Arch::PAGE_ENTRY_NUM); + if table.level() == 0 { + // todo: 检查是否已经映射 + // 现在不检查的原因是,刚刚启动系统时,内核会映射一些页。 + if table.entry_mapped(i)? == true { + kwarn!("Page {:?} already mapped", virt); + } + // kdebug!("Mapping {:?} to {:?}, i = {i}, entry={:?}, flags={:?}", virt, phys, entry, flags); + compiler_fence(Ordering::SeqCst); + table.set_entry(i, entry); + compiler_fence(Ordering::SeqCst); + return Some(PageFlush::new(virt)); + } else { + let next_table = table.next_level_table(i); + if let Some(next_table) = next_table { + table = next_table; + // kdebug!("Mapping {:?} to next level table...", virt); + } else { + // kdebug!("Allocating next level table for {:?}..., i={i}", virt); + // 分配下一级页表 + let frame = self.frame_allocator.allocate_one()?; + // 清空这个页帧 + MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); + + // 设置页表项的flags + // let flags = Arch::ENTRY_FLAG_READWRITE + // | Arch::ENTRY_FLAG_DEFAULT_TABLE + // | if virt.kind() == PageTableKind::User { + // Arch::ENTRY_FLAG_USER + // } else { + // 0 + // }; + let flags: PageFlags = + PageFlags::new_page_table(virt.kind() == PageTableKind::User); + + // kdebug!("Flags: {:?}", flags); + + // 把新分配的页表映射到当前页表 + table.set_entry(i, PageEntry::new(frame.data() | flags.data())); + + // 获取新分配的页表 + table = table.next_level_table(i)?; + } + } + } + } + + /// 将物理地址映射到具有线性偏移量的虚拟地址 + #[allow(dead_code)] + pub unsafe fn map_linearly( + &mut self, + phys: PhysAddr, + flags: PageFlags, + ) -> Option<(VirtAddr, PageFlush)> { + let virt: VirtAddr = Arch::phys_2_virt(phys)?; + return self.map_phys(virt, phys, flags).map(|flush| (virt, flush)); + } + + /// 修改虚拟地址的页表项的flags,并返回页表项刷新器 + /// + /// 请注意,需要在修改完flags后,调用刷新器的flush方法,才能使修改生效 + /// + /// ## 参数 + /// - virt 虚拟地址 + /// - flags 新的页表项的flags + /// + /// ## 返回值 + /// + /// 如果修改成功,返回刷新器,否则返回None + pub unsafe fn remap( + &mut self, + virt: VirtAddr, + flags: PageFlags, + ) -> Option> { + return self + .visit(virt, |p1, i| { + let mut entry = p1.entry(i)?; + entry.set_flags(flags); + p1.set_entry(i, entry); + Some(PageFlush::new(virt)) + }) + .flatten(); + } + + /// 根据虚拟地址,查找页表,获取对应的物理地址和页表项的flags + /// + /// ## 参数 + /// + /// - virt 虚拟地址 + /// + /// ## 返回值 + /// + /// 如果查找成功,返回物理地址和页表项的flags,否则返回None + pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, PageFlags)> { + let entry: PageEntry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??; + let paddr = entry.address().ok()?; + let flags = entry.flags(); + return Some((paddr, flags)); + } + + /// 取消虚拟地址的映射,释放页面,并返回页表项刷新器 + /// + /// 请注意,需要在取消映射后,调用刷新器的flush方法,才能使修改生效 + /// + /// ## 参数 + /// + /// - virt 虚拟地址 + /// - unmap_parents 是否在父页表内,取消空闲子页表的映射 + /// + /// ## 返回值 + /// 如果取消成功,返回刷新器,否则返回None + pub unsafe fn unmap(&mut self, virt: VirtAddr, unmap_parents: bool) -> Option> { + let (paddr, _, flusher) = self.unmap_phys(virt, unmap_parents)?; + self.frame_allocator.free_one(paddr); + return Some(flusher); + } + + /// 取消虚拟地址的映射,并返回物理地址和页表项的flags + /// + /// ## 参数 + /// + /// - vaddr 虚拟地址 + /// - unmap_parents 是否在父页表内,取消空闲子页表的映射 + /// + /// ## 返回值 + /// + /// 如果取消成功,返回物理地址和页表项的flags,否则返回None + pub unsafe fn unmap_phys( + &mut self, + virt: VirtAddr, + unmap_parents: bool, + ) -> Option<(PhysAddr, PageFlags, PageFlush)> { + if !virt.check_aligned(Arch::PAGE_SIZE) { + kerror!("Try to unmap unaligned page: virt={:?}", virt); + return None; + } + + let mut table = self.table(); + return unmap_phys_inner(virt, &mut table, unmap_parents, self.allocator_mut()) + .map(|(paddr, flags)| (paddr, flags, PageFlush::::new(virt))); + } + + /// 在页表中,访问虚拟地址对应的页表项,并调用传入的函数F + fn visit( + &self, + virt: VirtAddr, + f: impl FnOnce(&mut PageTable, usize) -> T, + ) -> Option { + let mut table = self.table(); + unsafe { + loop { + let i = table.index_of(virt)?; + if table.level() == 0 { + return Some(f(&mut table, i)); + } else { + table = table.next_level_table(i)?; + } + } + } + } +} + +/// 取消页面映射,返回被取消映射的页表项的:【物理地址】和【flags】 +/// +/// ## 参数 +/// +/// - vaddr 虚拟地址 +/// - table 页表 +/// - unmap_parents 是否在父页表内,取消空闲子页表的映射 +/// - allocator 页面分配器(如果页表从这个分配器分配,那么在取消映射时,也需要归还到这个分配器内) +/// +/// ## 返回值 +/// +/// 如果取消成功,返回被取消映射的页表项的:【物理地址】和【flags】,否则返回None +unsafe fn unmap_phys_inner( + vaddr: VirtAddr, + table: &mut PageTable, + unmap_parents: bool, + allocator: &mut impl FrameAllocator, +) -> Option<(PhysAddr, PageFlags)> { + // 获取页表项的索引 + let i = table.index_of(vaddr)?; + + // 如果当前是最后一级页表,直接取消页面映射 + if table.level() == 0 { + let entry = table.entry(i)?; + table.set_entry(i, PageEntry::new(0)); + return Some((entry.address().ok()?, entry.flags())); + } + + let mut subtable = table.next_level_table(i)?; + // 递归地取消映射 + let result = unmap_phys_inner(vaddr, &mut subtable, unmap_parents, allocator)?; + + // TODO: This is a bad idea for architectures where the kernel mappings are done in the process tables, + // as these mappings may become out of sync + if unmap_parents { + // 如果子页表已经没有映射的页面了,就取消子页表的映射 + + // 检查子页表中是否还有映射的页面 + let x = (0..Arch::PAGE_ENTRY_NUM) + .map(|k| subtable.entry(k).expect("invalid page entry")) + .any(|e| e.present()); + if !x { + // 如果没有,就取消子页表的映射 + table.set_entry(i, PageEntry::new(0)); + // 释放子页表 + allocator.free_one(subtable.phys()); + } + } + + return Some(result); +} + +impl Debug for PageMapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PageMapper") + .field("table_paddr", &self.table_paddr) + .field("frame_allocator", &self.frame_allocator) + .finish() + } +} + +/// 页表刷新器的trait +pub trait Flusher { + /// 取消对指定的page flusher的刷新 + fn consume(&mut self, flush: PageFlush); +} + +/// 用于刷新某个虚拟地址的刷新器。这个刷新器一经产生,就必须调用flush()方法, +/// 否则会造成对页表的更改被忽略,这是不安全的 +#[must_use = "The flusher must call the 'flush()', or the changes to page table will be unsafely ignored."] +pub struct PageFlush { + virt: VirtAddr, + phantom: PhantomData, +} + +impl PageFlush { + pub fn new(virt: VirtAddr) -> Self { + return Self { + virt, + phantom: PhantomData, + }; + } + + pub fn flush(self) { + unsafe { Arch::invalidate_page(self.virt) }; + } + + /// 忽略掉这个刷新器 + pub unsafe fn ignore(self) { + mem::forget(self); + } +} + +/// 用于刷新整个页表的刷新器。这个刷新器一经产生,就必须调用flush()方法, +/// 否则会造成对页表的更改被忽略,这是不安全的 +#[must_use = "The flusher must call the 'flush()', or the changes to page table will be unsafely ignored."] +pub struct PageFlushAll { + phantom: PhantomData Arch>, +} + +#[allow(dead_code)] +impl PageFlushAll { + pub fn new() -> Self { + return Self { + phantom: PhantomData, + }; + } + + pub fn flush(self) { + unsafe { Arch::invalidate_all() }; + } + + /// 忽略掉这个刷新器 + pub unsafe fn ignore(self) { + mem::forget(self); + } +} + +impl Flusher for PageFlushAll { + /// 为page flush all 实现consume,消除对单个页面的刷新。(刷新整个页表了就不需要刷新单个页面了) + fn consume(&mut self, flush: PageFlush) { + unsafe { flush.ignore() }; + } +} + +impl + ?Sized> Flusher for &mut T { + /// 允许一个flusher consume掉另一个flusher + fn consume(&mut self, flush: PageFlush) { + >::consume(self, flush); + } +} + +impl Flusher for () { + fn consume(&mut self, _flush: PageFlush) {} +} + +impl Drop for PageFlushAll { + fn drop(&mut self) { + unsafe { + Arch::invalidate_all(); + } + } +} + +/// 未在当前CPU上激活的页表的刷新器 +/// +/// 如果页表没有在当前cpu上激活,那么需要发送ipi到其他核心,尝试在其他核心上刷新页表 +/// +/// TODO: 这个方式很暴力,也许把它改成在指定的核心上刷新页表会更好。(可以测试一下开销) +#[derive(Debug)] +pub struct InactiveFlusher; + +impl InactiveFlusher { + pub fn new() -> Self { + return Self {}; + } +} + +impl Flusher for InactiveFlusher { + fn consume(&mut self, flush: PageFlush) { + unsafe { + flush.ignore(); + } + } +} + +impl Drop for InactiveFlusher { + fn drop(&mut self) { + // 发送刷新页表的IPI + send_ipi(IpiKind::FlushTLB, IpiTarget::Other); + } +} + +/// # 把一个地址向下对齐到页大小 +pub fn round_down_to_page_size(addr: usize) -> usize { + addr & !(MMArch::PAGE_SIZE - 1) +} + +/// # 把一个地址向上对齐到页大小 +pub fn round_up_to_page_size(addr: usize) -> usize { + round_down_to_page_size(addr + MMArch::PAGE_SIZE - 1) +} diff --git a/kernel/src/mm/slab.c b/kernel/src/mm/slab.c deleted file mode 100644 index 15d3d894..00000000 --- a/kernel/src/mm/slab.c +++ /dev/null @@ -1,713 +0,0 @@ -#include "slab.h" -#include - -struct slab kmalloc_cache_group[16] = - { - {32, 0, 0, NULL, NULL, NULL, NULL}, - {64, 0, 0, NULL, NULL, NULL, NULL}, - {128, 0, 0, NULL, NULL, NULL, NULL}, - {256, 0, 0, NULL, NULL, NULL, NULL}, - {512, 0, 0, NULL, NULL, NULL, NULL}, - {1024, 0, 0, NULL, NULL, NULL, NULL}, // 1KB - {2048, 0, 0, NULL, NULL, NULL, NULL}, - {4096, 0, 0, NULL, NULL, NULL, NULL}, // 4KB - {8192, 0, 0, NULL, NULL, NULL, NULL}, - {16384, 0, 0, NULL, NULL, NULL, NULL}, - {32768, 0, 0, NULL, NULL, NULL, NULL}, - {65536, 0, 0, NULL, NULL, NULL, NULL}, - {131072, 0, 0, NULL, NULL, NULL, NULL}, // 128KB - {262144, 0, 0, NULL, NULL, NULL, NULL}, - {524288, 0, 0, NULL, NULL, NULL, NULL}, - {1048576, 0, 0, NULL, NULL, NULL, NULL}, // 1MB -}; - -/** - * @brief 创建一个内存池 - * - * @param size 内存池容量大小 - * @param constructor 构造函数 - * @param destructor 析构函数 - * @param arg 参数 - * @return struct slab* 构建好的内存池对象 - */ -struct slab *slab_create(ul size, void *(*constructor)(void *vaddr, ul arg), void *(*destructor)(void *vaddr, ul arg), ul arg) -{ - struct slab *slab_pool = (struct slab *)kmalloc(sizeof(struct slab), 0); - - // BUG - if (slab_pool == NULL) - { - kBUG("slab_create()->kmalloc()->slab == NULL"); - return NULL; - } - - memset(slab_pool, 0, sizeof(struct slab)); - - slab_pool->size = SIZEOF_LONG_ALIGN(size); - slab_pool->count_total_using = 0; - slab_pool->count_total_free = 0; - // 直接分配cache_pool_entry结构体,避免每次访问都要检测是否为NULL,提升效率 - slab_pool->cache_pool_entry = (struct slab_obj *)kmalloc(sizeof(struct slab_obj), 0); - - // BUG - if (slab_pool->cache_pool_entry == NULL) - { - kBUG("slab_create()->kmalloc()->slab->cache_pool_entry == NULL"); - kfree(slab_pool); - return NULL; - } - memset(slab_pool->cache_pool_entry, 0, sizeof(struct slab_obj)); - - // dma内存池设置为空 - slab_pool->cache_dma_pool_entry = NULL; - - // 设置构造及析构函数 - slab_pool->constructor = constructor; - slab_pool->destructor = destructor; - - list_init(&slab_pool->cache_pool_entry->list); - - // 分配属于内存池的内存页 - slab_pool->cache_pool_entry->page = alloc_pages(ZONE_NORMAL, 1, PAGE_KERNEL); - - // BUG - if (slab_pool->cache_pool_entry->page == NULL) - { - kBUG("slab_create()->kmalloc()->slab->cache_pool_entry == NULL"); - kfree(slab_pool->cache_pool_entry); - kfree(slab_pool); - return NULL; - } - - // page_init(slab_pool->cache_pool_entry->page, PAGE_KERNEL); - - slab_pool->cache_pool_entry->count_using = 0; - slab_pool->cache_pool_entry->count_free = PAGE_2M_SIZE / slab_pool->size; - - slab_pool->count_total_free = slab_pool->cache_pool_entry->count_free; - - slab_pool->cache_pool_entry->vaddr = phys_2_virt(slab_pool->cache_pool_entry->page->addr_phys); - - // bitmap有多少有效位 - slab_pool->cache_pool_entry->bmp_count = slab_pool->cache_pool_entry->count_free; - - // 计算位图所占的空间 占用多少byte(按unsigned long大小的上边缘对齐) - slab_pool->cache_pool_entry->bmp_len = ((slab_pool->cache_pool_entry->bmp_count + sizeof(ul) * 8 - 1) >> 6) << 3; - // 初始化位图 - slab_pool->cache_pool_entry->bmp = (ul *)kmalloc(slab_pool->cache_pool_entry->bmp_len, 0); - - // BUG - if (slab_pool->cache_pool_entry->bmp == NULL) - { - kBUG("slab_create()->kmalloc()->slab->cache_pool_entry == NULL"); - free_pages(slab_pool->cache_pool_entry->page, 1); - kfree(slab_pool->cache_pool_entry); - kfree(slab_pool); - return NULL; - } - // 将位图清空 - memset(slab_pool->cache_pool_entry->bmp, 0, slab_pool->cache_pool_entry->bmp_len); - - return slab_pool; -} - -/** - * @brief 销毁内存池 - * 只有当slab是空的时候才能销毁 - * @param slab_pool 要销毁的内存池 - * @return ul - * - */ -ul slab_destroy(struct slab *slab_pool) -{ - struct slab_obj *slab_obj_ptr = slab_pool->cache_pool_entry; - if (slab_pool->count_total_using) - { - kBUG("slab_cache->count_total_using != 0"); - return ESLAB_NOTNULL; - } - - struct slab_obj *tmp_slab_obj = NULL; - while (!list_empty(&slab_obj_ptr->list)) - { - tmp_slab_obj = slab_obj_ptr; - // 获取下一个slab_obj的起始地址 - slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list); - - list_del(&tmp_slab_obj->list); - - kfree(tmp_slab_obj->bmp); - - page_clean(tmp_slab_obj->page); - - free_pages(tmp_slab_obj->page, 1); - - kfree(tmp_slab_obj); - } - - kfree(slab_obj_ptr->bmp); - page_clean(slab_obj_ptr->page); - free_pages(slab_obj_ptr->page, 1); - kfree(slab_obj_ptr); - kfree(slab_pool); - return 0; -} - -/** - * @brief 分配SLAB内存池中的内存对象 - * - * @param slab_pool slab内存池 - * @param arg 传递给内存对象构造函数的参数 - * @return void* 内存空间的虚拟地址 - */ -void *slab_malloc(struct slab *slab_pool, ul arg) -{ - struct slab_obj *slab_obj_ptr = slab_pool->cache_pool_entry; - struct slab_obj *tmp_slab_obj = NULL; - - // slab内存池中已经没有空闲的内存对象,进行扩容 - if (slab_pool->count_total_free == 0) - { - tmp_slab_obj = (struct slab_obj *)kmalloc(sizeof(struct slab_obj), 0); - - // BUG - if (tmp_slab_obj == NULL) - { - kBUG("slab_malloc()->kmalloc()->slab->tmp_slab_obj == NULL"); - return NULL; - } - - memset(tmp_slab_obj, 0, sizeof(struct slab_obj)); - list_init(&tmp_slab_obj->list); - - tmp_slab_obj->page = alloc_pages(ZONE_NORMAL, 1, PAGE_KERNEL); - - // BUG - if (tmp_slab_obj->page == NULL) - { - kBUG("slab_malloc()->kmalloc()=>tmp_slab_obj->page == NULL"); - kfree(tmp_slab_obj); - return NULL; - } - - tmp_slab_obj->count_using = 0; - tmp_slab_obj->count_free = PAGE_2M_SIZE / slab_pool->size; - tmp_slab_obj->vaddr = phys_2_virt(tmp_slab_obj->page->addr_phys); - tmp_slab_obj->bmp_count = tmp_slab_obj->count_free; - // 计算位图所占的空间 占用多少byte(按unsigned long大小的上边缘对齐) - tmp_slab_obj->bmp_len = ((tmp_slab_obj->bmp_count + sizeof(ul) * 8 - 1) >> 6) << 3; - tmp_slab_obj->bmp = (ul *)kmalloc(tmp_slab_obj->bmp_len, 0); - - // BUG - if (tmp_slab_obj->bmp == NULL) - { - kBUG("slab_malloc()->kmalloc()=>tmp_slab_obj->bmp == NULL"); - free_pages(tmp_slab_obj->page, 1); - kfree(tmp_slab_obj); - return NULL; - } - - memset(tmp_slab_obj->bmp, 0, tmp_slab_obj->bmp_len); - - list_add(&slab_pool->cache_pool_entry->list, &tmp_slab_obj->list); - - slab_pool->count_total_free += tmp_slab_obj->count_free; - - slab_obj_ptr = tmp_slab_obj; - } - - // 扩容完毕或无需扩容,开始分配内存对象 - int tmp_md; - do - { - if (slab_obj_ptr->count_free == 0) - { - slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list); - continue; - } - - for (int i = 0; i < slab_obj_ptr->bmp_count; ++i) - { - // 当前bmp对应的内存对象都已经被分配 - if (*(slab_obj_ptr->bmp + (i >> 6)) == 0xffffffffffffffffUL) - { - i += 63; - continue; - } - - // 第i个内存对象是空闲的 - tmp_md = i % 64; - if ((*(slab_obj_ptr->bmp + (i >> 6)) & (1UL << tmp_md)) == 0) - { - // 置位bmp - *(slab_obj_ptr->bmp + (i >> 6)) |= (1UL << tmp_md); - - // 更新当前slab对象的计数器 - ++(slab_obj_ptr->count_using); - --(slab_obj_ptr->count_free); - // 更新slab内存池的计数器 - ++(slab_pool->count_total_using); - --(slab_pool->count_total_free); - - if (slab_pool->constructor != NULL) - { - // 返回内存对象指针(要求构造函数返回内存对象指针) - return slab_pool->constructor((char *)slab_obj_ptr->vaddr + slab_pool->size * i, arg); - } - // 返回内存对象指针 - else - return (void *)((char *)slab_obj_ptr->vaddr + slab_pool->size * i); - } - } - - } while (slab_obj_ptr != slab_pool->cache_pool_entry); - - // should not be here - - kBUG("slab_malloc() ERROR: can't malloc"); - - // 释放内存 - if (tmp_slab_obj != NULL) - { - list_del(&tmp_slab_obj->list); - kfree(tmp_slab_obj->bmp); - page_clean(tmp_slab_obj->page); - free_pages(tmp_slab_obj->page, 1); - kfree(tmp_slab_obj); - } - return NULL; -} - -/** - * @brief 回收slab内存池中的对象 - * - * @param slab_pool 对应的内存池 - * @param addr 内存对象的虚拟地址 - * @param arg 传递给虚构函数的参数 - * @return ul - */ -ul slab_free(struct slab *slab_pool, void *addr, ul arg) -{ - struct slab_obj *slab_obj_ptr = slab_pool->cache_pool_entry; - - do - { - // 虚拟地址不在当前内存池对象的管理范围内 - if (!(slab_obj_ptr->vaddr <= addr && addr <= (slab_obj_ptr->vaddr + PAGE_2M_SIZE))) - { - slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list); - } - else - { - - // 计算出给定内存对象是第几个 - int index = (addr - slab_obj_ptr->vaddr) / slab_pool->size; - - // 复位位图中对应的位 - *(slab_obj_ptr->bmp + (index >> 6)) ^= (1UL << index % 64); - - ++(slab_obj_ptr->count_free); - --(slab_obj_ptr->count_using); - - ++(slab_pool->count_total_free); - --(slab_pool->count_total_using); - - // 有对应的析构函数,调用析构函数 - if (slab_pool->destructor != NULL) - slab_pool->destructor((char *)slab_obj_ptr->vaddr + slab_pool->size * index, arg); - - // 当前内存对象池的正在使用的内存对象为0,且内存池的空闲对象大于当前对象池的2倍,则销毁当前对象池,以减轻系统内存压力 - if ((slab_obj_ptr->count_using == 0) && ((slab_pool->count_total_free >> 1) >= slab_obj_ptr->count_free) && (slab_obj_ptr != slab_pool->cache_pool_entry)) - { - - list_del(&slab_obj_ptr->list); - slab_pool->count_total_free -= slab_obj_ptr->count_free; - - kfree(slab_obj_ptr->bmp); - page_clean(slab_obj_ptr->page); - free_pages(slab_obj_ptr->page, 1); - - kfree(slab_obj_ptr); - } - } - - return 0; - } while (slab_obj_ptr != slab_pool->cache_pool_entry); - - kwarn("slab_free(): address not in current slab"); - return ENOT_IN_SLAB; -} - -/** - * @brief 初始化内存池组 - * 在初始化通用内存管理单元期间,尚无内存空间分配函数,需要我们手动为SLAB内存池指定存储空间 - * @return ul - */ -ul slab_init() -{ - kinfo("Initializing SLAB..."); - // 将slab的内存池空间放置在mms的后方 - ul tmp_addr = memory_management_struct.end_of_struct; - for (int i = 0; i < 16; ++i) - { - io_mfence(); - spin_init(&kmalloc_cache_group[i].lock); - // 将slab内存池对象的空间放置在mms的后面,并且预留4个unsigned long 的空间以防止内存越界 - kmalloc_cache_group[i].cache_pool_entry = (struct slab_obj *)memory_management_struct.end_of_struct; - - memory_management_struct.end_of_struct += sizeof(struct slab_obj) + (sizeof(ul) << 2); - - list_init(&kmalloc_cache_group[i].cache_pool_entry->list); - - // 初始化内存池对象 - kmalloc_cache_group[i].cache_pool_entry->count_using = 0; - kmalloc_cache_group[i].cache_pool_entry->count_free = PAGE_2M_SIZE / kmalloc_cache_group[i].size; - kmalloc_cache_group[i].cache_pool_entry->bmp_len = (((kmalloc_cache_group[i].cache_pool_entry->count_free + sizeof(ul) * 8 - 1) >> 6) << 3); - kmalloc_cache_group[i].cache_pool_entry->bmp_count = kmalloc_cache_group[i].cache_pool_entry->count_free; - - // 在slab对象后方放置bmp - kmalloc_cache_group[i].cache_pool_entry->bmp = (ul *)memory_management_struct.end_of_struct; - - // bmp后方预留4个unsigned long的空间防止内存越界,且按照8byte进行对齐 - memory_management_struct.end_of_struct = (ul)(memory_management_struct.end_of_struct + kmalloc_cache_group[i].cache_pool_entry->bmp_len + (sizeof(ul) << 2)) & (~(sizeof(ul) - 1)); - io_mfence(); - // @todo:此处可优化,直接把所有位设置为0,然后再对部分不存在对应的内存对象的位设置为1 - memset(kmalloc_cache_group[i].cache_pool_entry->bmp, 0xff, kmalloc_cache_group[i].cache_pool_entry->bmp_len); - for (int j = 0; j < kmalloc_cache_group[i].cache_pool_entry->bmp_count; ++j) - *(kmalloc_cache_group[i].cache_pool_entry->bmp + (j >> 6)) ^= 1UL << (j % 64); - - kmalloc_cache_group[i].count_total_using = 0; - kmalloc_cache_group[i].count_total_free = kmalloc_cache_group[i].cache_pool_entry->count_free; - io_mfence(); - } - - struct Page *page = NULL; - - // 将上面初始化内存池组时,所占用的内存页进行初始化 - ul tmp_page_mms_end = virt_2_phys(memory_management_struct.end_of_struct) >> PAGE_2M_SHIFT; - - ul page_num = 0; - for (int i = PAGE_2M_ALIGN(virt_2_phys(tmp_addr)) >> PAGE_2M_SHIFT; i <= tmp_page_mms_end; ++i) - { - - page = memory_management_struct.pages_struct + i; - page_num = page->addr_phys >> PAGE_2M_SHIFT; - *(memory_management_struct.bmp + (page_num >> 6)) |= (1UL << (page_num % 64)); - ++page->zone->count_pages_using; - io_mfence(); - --page->zone->count_pages_free; - page_init(page, PAGE_KERNEL_INIT | PAGE_KERNEL | PAGE_PGT_MAPPED); - } - io_mfence(); - - // 为slab内存池对象分配内存空间 - ul *virt = NULL; - for (int i = 0; i < 16; ++i) - { - // 获取一个新的空页并添加到空页表,然后返回其虚拟地址 - virt = (ul *)((memory_management_struct.end_of_struct + PAGE_2M_SIZE * i + PAGE_2M_SIZE - 1) & PAGE_2M_MASK); - - page = Virt_To_2M_Page(virt); - - page_num = page->addr_phys >> PAGE_2M_SHIFT; - - *(memory_management_struct.bmp + (page_num >> 6)) |= (1UL << (page_num % 64)); - - ++page->zone->count_pages_using; - io_mfence(); // 该位置必须加一个mfence,否则O3优化运行时会报错 - --page->zone->count_pages_free; - page_init(page, PAGE_PGT_MAPPED | PAGE_KERNEL | PAGE_KERNEL_INIT); - - kmalloc_cache_group[i].cache_pool_entry->page = page; - - kmalloc_cache_group[i].cache_pool_entry->vaddr = virt; - } - - kinfo("SLAB initialized successfully!"); - - return 0; -} - -/** - * @brief 在kmalloc中创建slab_obj的函数(与slab_malloc()中的类似) - * - * @param size - * @return struct slab_obj* 创建好的slab_obj - */ - -struct slab_obj *kmalloc_create_slab_obj(ul size) -{ - struct Page *page = alloc_pages(ZONE_NORMAL, 1, 0); - - // BUG - if (page == NULL) - { - kBUG("kmalloc_create()->alloc_pages()=>page == NULL"); - return NULL; - } - - page_init(page, PAGE_KERNEL); - - ul *vaddr = NULL; - ul struct_size = 0; - struct slab_obj *slab_obj_ptr; - - // 根据size大小,选择不同的分支来处理 - // 之所以选择512byte为分界点,是因为,此时bmp大小刚好为512byte。显而易见,选择过小的话会导致kmalloc函数与当前函数反复互相调用,最终导致栈溢出 - switch (size) - { - // ============ 对于size<=512byte的内存池对象,将slab_obj结构体和bmp放置在物理页的内部 ======== - // 由于这些对象的特征是,bmp占的空间大,而内存块的空间小,这样做的目的是避免再去申请一块内存来存储bmp,减少浪费。 - case 32: - case 64: - case 128: - case 256: - case 512: - vaddr = phys_2_virt(page->addr_phys); - // slab_obj结构体的大小 (本身的大小+bmp的大小) - struct_size = sizeof(struct slab_obj) + PAGE_2M_SIZE / size / 8; - // 将slab_obj放置到物理页的末尾 - slab_obj_ptr = (struct slab_obj *)((unsigned char *)vaddr + PAGE_2M_SIZE - struct_size); - slab_obj_ptr->bmp = (void *)slab_obj_ptr + sizeof(struct slab_obj); - - slab_obj_ptr->count_free = (PAGE_2M_SIZE - struct_size) / size; - slab_obj_ptr->count_using = 0; - slab_obj_ptr->bmp_count = slab_obj_ptr->count_free; - slab_obj_ptr->vaddr = vaddr; - slab_obj_ptr->page = page; - - list_init(&slab_obj_ptr->list); - - slab_obj_ptr->bmp_len = ((slab_obj_ptr->bmp_count + sizeof(ul) * 8 - 1) >> 6) << 3; - - // @todo:此处可优化,直接把所有位设置为0,然后再对部分不存在对应的内存对象的位设置为1 - memset(slab_obj_ptr->bmp, 0xff, slab_obj_ptr->bmp_len); - - for (int i = 0; i < slab_obj_ptr->bmp_count; ++i) - *(slab_obj_ptr->bmp + (i >> 6)) ^= 1UL << (i % 64); - - break; - // ================= 较大的size时,slab_obj和bmp不再放置于当前物理页内部 ============ - // 因为在这种情况下,bmp很短,继续放置在当前物理页内部则会造成可分配的对象少,加剧了内存空间的浪费 - case 1024: // 1KB - case 2048: - case 4096: // 4KB - case 8192: - case 16384: - case 32768: - case 65536: - case 131072: // 128KB - case 262144: - case 524288: - case 1048576: // 1MB - slab_obj_ptr = (struct slab_obj *)kmalloc(sizeof(struct slab_obj), 0); - - slab_obj_ptr->count_free = PAGE_2M_SIZE / size; - slab_obj_ptr->count_using = 0; - slab_obj_ptr->bmp_count = slab_obj_ptr->count_free; - - slab_obj_ptr->bmp_len = ((slab_obj_ptr->bmp_count + sizeof(ul) * 8 - 1) >> 6) << 3; - - slab_obj_ptr->bmp = (ul *)kmalloc(slab_obj_ptr->bmp_len, 0); - - // @todo:此处可优化,直接把所有位设置为0,然后再对部分不存在对应的内存对象的位设置为1 - memset(slab_obj_ptr->bmp, 0xff, slab_obj_ptr->bmp_len); - for (int i = 0; i < slab_obj_ptr->bmp_count; ++i) - *(slab_obj_ptr->bmp + (i >> 6)) ^= 1UL << (i % 64); - - slab_obj_ptr->vaddr = phys_2_virt(page->addr_phys); - slab_obj_ptr->page = page; - list_init(&slab_obj_ptr->list); - break; - // size 错误 - default: - kerror("kamlloc_create(): Wrong size%d", size); - free_pages(page, 1); - return NULL; - break; - } - - return slab_obj_ptr; -} - -/** - * @brief 通用内存分配函数 - * - * @param size 要分配的内存大小 - * @param gfp 内存的flag - * @return void* 内核内存虚拟地址 - */ -void *kmalloc(unsigned long size, gfp_t gfp) -{ - void *result = NULL; - if (size > 1048576) - { - kwarn("kmalloc(): Can't alloc such memory: %ld bytes, because it is too large.", size); - return NULL; - } - int index; - for (int i = 0; i < 16; ++i) - { - if (kmalloc_cache_group[i].size >= size) - { - index = i; - break; - } - } - // 对当前内存池加锁 - spin_lock(&kmalloc_cache_group[index].lock); - - struct slab_obj *slab_obj_ptr = kmalloc_cache_group[index].cache_pool_entry; - - // 内存池没有可用的内存对象,需要进行扩容 - if (unlikely(kmalloc_cache_group[index].count_total_free == 0)) - { - // 创建slab_obj - slab_obj_ptr = kmalloc_create_slab_obj(kmalloc_cache_group[index].size); - - // BUG - if (unlikely(slab_obj_ptr == NULL)) - { - kBUG("kmalloc()->kmalloc_create_slab_obj()=>slab == NULL"); - goto failed; - } - - kmalloc_cache_group[index].count_total_free += slab_obj_ptr->count_free; - list_add(&kmalloc_cache_group[index].cache_pool_entry->list, &slab_obj_ptr->list); - } - else // 内存对象充足 - { - do - { - // 跳转到下一个内存池对象 - if (slab_obj_ptr->count_free == 0) - slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list); - else - break; - } while (slab_obj_ptr != kmalloc_cache_group[index].cache_pool_entry); - } - // 寻找一块可用的内存对象 - int md; - for (int i = 0; i < slab_obj_ptr->bmp_count; ++i) - { - - // 当前bmp全部被使用 - if (*(slab_obj_ptr->bmp + (i >> 6)) == 0xffffffffffffffffUL) - { - i += 63; - continue; - } - md = i % 64; - // 找到相应的内存对象 - if ((*(slab_obj_ptr->bmp + (i >> 6)) & (1UL << md)) == 0) - { - *(slab_obj_ptr->bmp + (i >> 6)) |= (1UL << md); - ++(slab_obj_ptr->count_using); - --(slab_obj_ptr->count_free); - - --kmalloc_cache_group[index].count_total_free; - ++kmalloc_cache_group[index].count_total_using; - // 放锁 - spin_unlock(&kmalloc_cache_group[index].lock); - // 返回内存对象 - result = (void *)((char *)slab_obj_ptr->vaddr + kmalloc_cache_group[index].size * i); - goto done; - } - } - goto failed; -done:; - if (gfp & __GFP_ZERO) - memset(result, 0, size); - return result; -failed:; - spin_unlock(&kmalloc_cache_group[index].lock); - kerror("kmalloc(): Cannot alloc more memory: %d bytes", size); - return NULL; -} - -/** - * @brief 通用内存释放函数 - * - * @param address 要释放的内存线性地址 - * @return unsigned long - */ -unsigned long kfree(void *address) -{ - if (unlikely(address == NULL)) - return 0; - struct slab_obj *slab_obj_ptr = NULL; - - // 将线性地址按照2M物理页对齐, 获得所在物理页的起始线性地址 - void *page_base_addr = (void *)((ul)address & PAGE_2M_MASK); - - int index; - - for (int i = 0; i < 16; ++i) - { - slab_obj_ptr = kmalloc_cache_group[i].cache_pool_entry; - - do - { - // 不属于当前slab_obj的管理范围 - if (likely(slab_obj_ptr->vaddr != page_base_addr)) - { - slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list); - } - else - { - // 对当前内存池加锁 - spin_lock(&kmalloc_cache_group[i].lock); - // 计算地址属于哪一个内存对象 - index = (address - slab_obj_ptr->vaddr) / kmalloc_cache_group[i].size; - - // 复位bmp - *(slab_obj_ptr->bmp + (index >> 6)) ^= 1UL << (index % 64); - - ++(slab_obj_ptr->count_free); - --(slab_obj_ptr->count_using); - ++kmalloc_cache_group[i].count_total_free; - --kmalloc_cache_group[i].count_total_using; - - // 回收空闲的slab_obj - // 条件:当前slab_obj_ptr的使用为0、总空闲内存对象>=当前slab_obj的总对象的2倍 且当前slab_pool不为起始slab_obj - if ((slab_obj_ptr->count_using == 0) && (kmalloc_cache_group[i].count_total_free >= ((slab_obj_ptr->bmp_count) << 1)) && (kmalloc_cache_group[i].cache_pool_entry != slab_obj_ptr)) - { - switch (kmalloc_cache_group[i].size) - { - case 32: - case 64: - case 128: - case 256: - case 512: - // 在这种情况下,slab_obj是被安放在page内部的 - list_del(&slab_obj_ptr->list); - - kmalloc_cache_group[i].count_total_free -= slab_obj_ptr->bmp_count; - page_clean(slab_obj_ptr->page); - free_pages(slab_obj_ptr->page, 1); - break; - - default: - // 在这种情况下,slab_obj是被安放在额外获取的内存对象中的 - list_del(&slab_obj_ptr->list); - kmalloc_cache_group[i].count_total_free -= slab_obj_ptr->bmp_count; - - kfree(slab_obj_ptr->bmp); - - page_clean(slab_obj_ptr->page); - free_pages(slab_obj_ptr->page, 1); - - kfree(slab_obj_ptr); - break; - } - } - // 放锁 - spin_unlock(&kmalloc_cache_group[i].lock); - return 0; - } - - } while (slab_obj_ptr != kmalloc_cache_group[i].cache_pool_entry); - } - kBUG("kfree(): Can't free memory. address=%#018lx", address); - return ECANNOT_FREE_MEM; -} \ No newline at end of file diff --git a/kernel/src/mm/slab.h b/kernel/src/mm/slab.h index 218b576d..19fb8b69 100644 --- a/kernel/src/mm/slab.h +++ b/kernel/src/mm/slab.h @@ -1,54 +1,6 @@ #pragma once #include "mm.h" -#include -#include -#include -#include - -#define SIZEOF_LONG_ALIGN(size) ((size + sizeof(long) - 1) & ~(sizeof(long) - 1)) -#define SIZEOF_INT_ALIGN(size) ((size + sizeof(int) - 1) & ~(sizeof(int) - 1)) - -// SLAB存储池count_using不为空 -#define ESLAB_NOTNULL 101 -#define ENOT_IN_SLAB 102 // 地址不在当前slab内存池中 -#define ECANNOT_FREE_MEM 103 // 无法释放内存 - -struct slab_obj -{ - struct List list; - // 当前slab对象所使用的内存页 - struct Page *page; - - ul count_using; - ul count_free; - - // 当前页面所在的线性地址 - void *vaddr; - - // 位图 - ul bmp_len; // 位图的长度(字节) - ul bmp_count; // 位图的有效位数 - ul *bmp; -}; - -// slab内存池 -struct slab -{ - ul size; // 单位:byte - ul count_total_using; - ul count_total_free; - // 内存池对象 - struct slab_obj *cache_pool_entry; - // dma内存池对象 - struct slab_obj *cache_dma_pool_entry; - - spinlock_t lock; // 当前内存池的操作锁 - - // 内存池的构造函数和析构函数 - void *(*constructor)(void *vaddr, ul arg); - void *(*destructor)(void *vaddr, ul arg); -}; /** * @brief 通用内存分配函数 @@ -57,19 +9,16 @@ struct slab * @param gfp 内存的flag * @return void* 分配得到的内存的指针 */ -void *kmalloc(unsigned long size, gfp_t gfp); +extern void *kmalloc(unsigned long size, gfp_t gfp); /** * @brief 从kmalloc申请一块内存,并将这块内存清空 - * + * * @param size 要分配的内存大小 * @param gfp 内存的flag * @return void* 分配得到的内存的指针 */ -static __always_inline void *kzalloc(size_t size, gfp_t gfp) -{ - return kmalloc(size, gfp | __GFP_ZERO); -} +extern void *kzalloc(size_t size, gfp_t gfp); /** * @brief 通用内存释放函数 @@ -77,58 +26,4 @@ static __always_inline void *kzalloc(size_t size, gfp_t gfp) * @param address 要释放的内存地址 * @return unsigned long */ -unsigned long kfree(void *address); - -/** - * @brief 创建一个内存池 - * - * @param size 内存池容量大小 - * @param constructor 构造函数 - * @param destructor 析构函数 - * @param arg 参数 - * @return struct slab* 构建好的内存池对象 - */ -struct slab *slab_create(ul size, void *(*constructor)(void *vaddr, ul arg), void *(*destructor)(void *vaddr, ul arg), ul arg); - -/** - * @brief 销毁内存池对象 - * 只有当slab对象是空的时候才能销毁 - * @param slab_pool 要销毁的内存池对象 - * @return ul - * - */ -ul slab_destroy(struct slab *slab_pool); - -/** - * @brief 分配SLAB内存池中的内存对象 - * - * @param slab_pool slab内存池 - * @param arg 传递给内存对象构造函数的参数 - * @return void* 内存空间的虚拟地址 - */ -void *slab_malloc(struct slab *slab_pool, ul arg); - -/** - * @brief 回收slab内存池中的对象 - * - * @param slab_pool 对应的内存池 - * @param addr 内存对象的虚拟地址 - * @param arg 传递给虚构函数的参数 - * @return ul - */ -ul slab_free(struct slab *slab_pool, void *addr, ul arg); - -/** - * @brief 在kmalloc中创建slab_obj的函数(与slab_malloc()类似) - * - * @param size - * @return struct slab_obj* 创建好的slab_obj - */ -struct slab_obj *kmalloc_create_slab_obj(ul size); - -/** - * @brief 初始化内存池组 - * 在初始化通用内存管理单元期间,尚无内存空间分配函数,需要我们手动为SLAB内存池指定存储空间 - * @return ul - */ -ul slab_init(); +extern unsigned long kfree(void *address); diff --git a/kernel/src/mm/syscall.rs b/kernel/src/mm/syscall.rs index 2adb2f7e..ef60a006 100644 --- a/kernel/src/mm/syscall.rs +++ b/kernel/src/mm/syscall.rs @@ -1,43 +1,219 @@ +use core::intrinsics::unlikely; + +use alloc::sync::Arc; + use crate::{ - include::bindings::bindings::mm_stat_t, + arch::MMArch, + kerror, + libs::align::{check_aligned, page_align_up}, + mm::MemoryManagementArch, syscall::{Syscall, SystemError}, }; -extern "C" { - fn sys_do_brk(new_addr: usize) -> usize; - fn sys_do_sbrk(incr: isize) -> usize; - fn sys_do_mstat(dst: *mut mm_stat_t, from_user: bool) -> usize; +use super::{ + allocator::page_frame::{PageFrameCount, VirtPageFrame}, + ucontext::{AddressSpace, DEFAULT_MMAP_MIN_ADDR}, + verify_area, VirtAddr, +}; + +bitflags! { + /// Memory protection flags + pub struct ProtFlags: u64 { + const PROT_NONE = 0x0; + const PROT_READ = 0x1; + const PROT_WRITE = 0x2; + const PROT_EXEC = 0x4; + } + + /// Memory mapping flags + pub struct MapFlags: u64 { + const MAP_NONE = 0x0; + /// share changes + const MAP_SHARED = 0x1; + /// changes are private + const MAP_PRIVATE = 0x2; + /// Interpret addr exactly + const MAP_FIXED = 0x10; + /// don't use a file + const MAP_ANONYMOUS = 0x20; + // linux-6.1-rc5/include/uapi/asm-generic/mman.h#7 + /// stack-like segment + const MAP_GROWSDOWN = 0x100; + /// ETXTBSY + const MAP_DENYWRITE = 0x800; + /// Mark it as an executable + const MAP_EXECUTABLE = 0x1000; + /// Pages are locked + const MAP_LOCKED = 0x2000; + /// don't check for reservations + const MAP_NORESERVE = 0x4000; + /// populate (prefault) pagetables + const MAP_POPULATE = 0x8000; + /// do not block on IO + const MAP_NONBLOCK = 0x10000; + /// give out an address that is best suited for process/thread stacks + const MAP_STACK = 0x20000; + /// create a huge page mapping + const MAP_HUGETLB = 0x40000; + /// perform synchronous page faults for the mapping + const MAP_SYNC = 0x80000; + /// MAP_FIXED which doesn't unmap underlying mapping + const MAP_FIXED_NOREPLACE = 0x100000; + + /// For anonymous mmap, memory could be uninitialized + const MAP_UNINITIALIZED = 0x4000000; + + } } + impl Syscall { - pub fn brk(new_addr: usize) -> Result { - let ret = unsafe { sys_do_brk(new_addr) }; - if (ret as isize) < 0 { - return Err( - SystemError::from_posix_errno(-(ret as isize) as i32).expect("brk: Invalid errno") - ); + pub fn brk(new_addr: VirtAddr) -> Result { + // kdebug!("brk: new_addr={:?}", new_addr); + let address_space = AddressSpace::current()?; + let mut address_space = address_space.write(); + + unsafe { + address_space + .set_brk(VirtAddr::new(page_align_up(new_addr.data()))) + .ok(); + + return Ok(address_space.sbrk(0).unwrap()); } - return Ok(ret); } - pub fn sbrk(incr: isize) -> Result { - let ret = unsafe { sys_do_sbrk(incr) }; - if (ret as isize) < 0 { - return Err( - SystemError::from_posix_errno(-(ret as isize) as i32).expect("sbrk: Invalid errno") - ); - } - return Ok(ret); + pub fn sbrk(incr: isize) -> Result { + // kdebug!("pid:{}, sbrk: incr={}", current_pcb().pid, incr); + + let address_space = AddressSpace::current()?; + let mut address_space = address_space.write(); + let r = unsafe { address_space.sbrk(incr) }; + + // kdebug!("pid:{}, sbrk: r={:?}", current_pcb().pid, r); + return r; } - /// 获取内存统计信息 + /// ## mmap系统调用 /// - /// TODO: 该函数不是符合POSIX标准的,在将来需要删除! - pub fn mstat(dst: *mut mm_stat_t, from_user: bool) -> Result { - let ret = unsafe { sys_do_mstat(dst, from_user) }; - if (ret as isize) < 0 { - return Err(SystemError::from_posix_errno(-(ret as isize) as i32) - .expect("mstat: Invalid errno")); + /// 该函数的实现参考了Linux内核的实现,但是并不完全相同。因为有些功能咱们还没实现 + /// + /// ## 参数 + /// + /// - `start_vaddr`:映射的起始地址 + /// - `len`:映射的长度 + /// - `prot`:保护标志 + /// - `flags`:映射标志 + /// - `fd`:文件描述符(暂时不支持) + /// - `offset`:文件偏移量 (暂时不支持) + /// + /// ## 返回值 + /// + /// 成功时返回映射的起始地址,失败时返回错误码 + pub fn mmap( + start_vaddr: VirtAddr, + len: usize, + prot_flags: usize, + map_flags: usize, + _fd: i32, + _offset: usize, + ) -> Result { + let map_flags = MapFlags::from_bits_truncate(map_flags as u64); + let prot_flags = ProtFlags::from_bits_truncate(prot_flags as u64); + + if start_vaddr < VirtAddr::new(DEFAULT_MMAP_MIN_ADDR) + && map_flags.contains(MapFlags::MAP_FIXED) + { + kerror!( + "mmap: MAP_FIXED is not supported for address below {}", + DEFAULT_MMAP_MIN_ADDR + ); + return Err(SystemError::EINVAL); } - return Ok(ret); + // 暂时不支持除匿名页以外的映射 + if !map_flags.contains(MapFlags::MAP_ANONYMOUS) { + kerror!("mmap: not support file mapping"); + return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); + } + + // 暂时不支持巨页映射 + if map_flags.contains(MapFlags::MAP_HUGETLB) { + kerror!("mmap: not support huge page mapping"); + return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); + } + let current_address_space = AddressSpace::current()?; + let start_page = current_address_space.write().map_anonymous( + start_vaddr, + len, + prot_flags, + map_flags, + true, + )?; + return Ok(start_page.virt_address().data()); + } + + /// ## munmap系统调用 + /// + /// ## 参数 + /// + /// - `start_vaddr`:取消映射的起始地址(已经对齐到页) + /// - `len`:取消映射的字节数(已经对齐到页) + /// + /// ## 返回值 + /// + /// 成功时返回0,失败时返回错误码 + pub fn munmap(start_vaddr: VirtAddr, len: usize) -> Result { + assert!(start_vaddr.check_aligned(MMArch::PAGE_SIZE)); + assert!(check_aligned(len, MMArch::PAGE_SIZE)); + + if unlikely(verify_area(start_vaddr, len).is_err()) { + return Err(SystemError::EINVAL); + } + if unlikely(len == 0) { + return Err(SystemError::EINVAL); + } + + let current_address_space: Arc = AddressSpace::current()?; + let start_frame = VirtPageFrame::new(start_vaddr); + let page_count = PageFrameCount::new(len / MMArch::PAGE_SIZE); + + current_address_space + .write() + .munmap(start_frame, page_count) + .map_err(|_| SystemError::EINVAL)?; + return Ok(0); + } + + /// ## mprotect系统调用 + /// + /// ## 参数 + /// + /// - `start_vaddr`:起始地址(已经对齐到页) + /// - `len`:长度(已经对齐到页) + /// - `prot_flags`:保护标志 + pub fn mprotect( + start_vaddr: VirtAddr, + len: usize, + prot_flags: usize, + ) -> Result { + assert!(start_vaddr.check_aligned(MMArch::PAGE_SIZE)); + assert!(check_aligned(len, MMArch::PAGE_SIZE)); + + if unlikely(verify_area(start_vaddr, len).is_err()) { + return Err(SystemError::EINVAL); + } + if unlikely(len == 0) { + return Err(SystemError::EINVAL); + } + + let prot_flags = ProtFlags::from_bits(prot_flags as u64).ok_or(SystemError::EINVAL)?; + + let current_address_space: Arc = AddressSpace::current()?; + let start_frame = VirtPageFrame::new(start_vaddr); + let page_count = PageFrameCount::new(len / MMArch::PAGE_SIZE); + + current_address_space + .write() + .mprotect(start_frame, page_count, prot_flags) + .map_err(|_| SystemError::EINVAL)?; + return Ok(0); } } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs new file mode 100644 index 00000000..6fc7d89b --- /dev/null +++ b/kernel/src/mm/ucontext.rs @@ -0,0 +1,1319 @@ +// 进程的用户空间内存管理 + +use core::{ + cmp, + hash::Hasher, + intrinsics::unlikely, + ops::Add, + sync::atomic::{compiler_fence, Ordering}, +}; + +use alloc::{ + collections::BTreeMap, + sync::{Arc, Weak}, + vec::Vec, +}; +use hashbrown::HashSet; + +use crate::{ + arch::{asm::current::current_pcb, mm::PageMapper, CurrentIrqArch, MMArch}, + exception::InterruptArch, + libs::{ + align::page_align_up, + rwlock::{RwLock, RwLockWriteGuard}, + spinlock::{SpinLock, SpinLockGuard}, + }, + syscall::SystemError, +}; + +use super::{ + allocator::page_frame::{ + deallocate_page_frames, PageFrameCount, PhysPageFrame, VirtPageFrame, VirtPageFrameIter, + }, + page::{Flusher, InactiveFlusher, PageFlags, PageFlushAll}, + syscall::{MapFlags, ProtFlags}, + MemoryManagementArch, PageTableKind, VirtAddr, VirtRegion, +}; + +/// MMAP_MIN_ADDR的默认值 +/// 以下内容来自linux-5.19: +/// This is the portion of low virtual memory which should be protected +// from userspace allocation. Keeping a user from writing to low pages +// can help reduce the impact of kernel NULL pointer bugs. +// For most ia64, ppc64 and x86 users with lots of address space +// a value of 65536 is reasonable and should cause no problems. +// On arm and other archs it should not be higher than 32768. +// Programs which use vm86 functionality or have some need to map +// this low address space will need CAP_SYS_RAWIO or disable this +// protection by setting the value to 0. +pub const DEFAULT_MMAP_MIN_ADDR: usize = 65536; + +#[derive(Debug)] +pub struct AddressSpace { + inner: RwLock, +} + +impl AddressSpace { + pub fn new(create_stack: bool) -> Result, SystemError> { + let inner = InnerAddressSpace::new(create_stack)?; + let result = Self { + inner: RwLock::new(inner), + }; + return Ok(Arc::new(result)); + } + + /// 从pcb中获取当前进程的地址空间结构体的Arc指针 + pub fn current() -> Result, SystemError> { + let result = current_pcb() + .address_space() + .expect("Current process has no address space"); + return Ok(result); + } + + /// 判断某个地址空间是否为当前进程的地址空间 + pub fn is_current(self: &Arc) -> bool { + let current = Self::current(); + if let Ok(current) = current { + return Arc::ptr_eq(¤t, self); + } + return false; + } +} + +impl core::ops::Deref for AddressSpace { + type Target = RwLock; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl core::ops::DerefMut for AddressSpace { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +/// @brief 用户地址空间结构体(每个进程都有一个) +#[derive(Debug)] +pub struct InnerAddressSpace { + pub user_mapper: UserMapper, + pub mappings: UserMappings, + pub mmap_min: VirtAddr, + /// 用户栈信息结构体 + pub user_stack: Option, + + pub elf_brk_start: VirtAddr, + pub elf_brk: VirtAddr, + + /// 当前进程的堆空间的起始地址 + pub brk_start: VirtAddr, + /// 当前进程的堆空间的结束地址(不包含) + pub brk: VirtAddr, + + pub start_code: VirtAddr, + pub end_code: VirtAddr, + pub start_data: VirtAddr, + pub end_data: VirtAddr, +} + +impl InnerAddressSpace { + pub fn new(create_stack: bool) -> Result { + let mut result = Self { + user_mapper: MMArch::setup_new_usermapper()?, + mappings: UserMappings::new(), + mmap_min: VirtAddr(DEFAULT_MMAP_MIN_ADDR), + elf_brk_start: VirtAddr::new(0), + elf_brk: VirtAddr::new(0), + brk_start: MMArch::USER_BRK_START, + brk: MMArch::USER_BRK_START, + user_stack: None, + start_code: VirtAddr(0), + end_code: VirtAddr(0), + start_data: VirtAddr(0), + end_data: VirtAddr(0), + }; + if create_stack { + // kdebug!("to create user stack."); + result.new_user_stack(UserStack::DEFAULT_USER_STACK_SIZE)?; + } + + return Ok(result); + } + + /// 尝试克隆当前进程的地址空间,包括这些映射都会被克隆 + /// + /// # Returns + /// + /// 返回克隆后的,新的地址空间的Arc指针 + pub fn try_clone(&mut self) -> Result, SystemError> { + let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; + let new_addr_space = AddressSpace::new(false)?; + let mut new_guard = new_addr_space.write(); + + // 拷贝用户栈的结构体信息,但是不拷贝用户栈的内容(因为后面VMA的拷贝会拷贝用户栈的内容) + unsafe { + new_guard.user_stack = Some(self.user_stack.as_ref().unwrap().clone_info_only()); + } + let _current_stack_size = self.user_stack.as_ref().unwrap().stack_size(); + + let current_mapper = &mut self.user_mapper.utable; + + for vma in self.mappings.vmas.iter() { + // TODO: 增加对VMA是否为文件映射的判断,如果是的话,就跳过 + + let vma_guard: SpinLockGuard<'_, VMA> = vma.lock(); + let old_flags = vma_guard.flags(); + let tmp_flags: PageFlags = PageFlags::new().set_write(true); + + // 分配内存页并创建新的VMA + let new_vma = VMA::zeroed( + VirtPageFrame::new(vma_guard.region.start()), + PageFrameCount::new(vma_guard.region.size() / MMArch::PAGE_SIZE), + tmp_flags, + &mut new_guard.user_mapper.utable, + (), + )?; + new_guard.mappings.vmas.insert(new_vma.clone()); + // kdebug!("new vma: {:x?}", new_vma); + let mut new_vma_guard = new_vma.lock(); + for page in new_vma_guard.pages().map(|p| p.virt_address()) { + // kdebug!("page: {:x?}", page); + let current_frame = unsafe { + MMArch::phys_2_virt( + current_mapper + .translate(page) + .expect("VMA page not mapped") + .0, + ) + } + .expect("Phys2Virt: vaddr overflow.") + .data() as *mut u8; + + let new_frame = unsafe { + MMArch::phys_2_virt( + new_guard + .user_mapper + .utable + .translate(page) + .expect("VMA page not mapped") + .0, + ) + } + .expect("Phys2Virt: vaddr overflow.") + .data() as *mut u8; + + unsafe { + // 拷贝数据 + new_frame.copy_from_nonoverlapping(current_frame, MMArch::PAGE_SIZE); + } + } + drop(vma_guard); + + new_vma_guard.remap(old_flags, &mut new_guard.user_mapper.utable, ())?; + drop(new_vma_guard); + } + drop(new_guard); + drop(irq_guard); + return Ok(new_addr_space); + } + + /// 判断当前的地址空间是否是当前进程的地址空间 + #[inline] + pub fn is_current(&self) -> bool { + return self.user_mapper.utable.is_current(); + } + + /// 进行匿名页映射 + /// + /// ## 参数 + /// + /// - `start_vaddr`:映射的起始地址 + /// - `len`:映射的长度 + /// - `prot_flags`:保护标志 + /// - `map_flags`:映射标志 + /// - `round_to_min`:是否将`start_vaddr`对齐到`mmap_min`,如果为`true`,则当`start_vaddr`不为0时,会对齐到`mmap_min`,否则仅向下对齐到页边界 + pub fn map_anonymous( + &mut self, + start_vaddr: VirtAddr, + len: usize, + prot_flags: ProtFlags, + map_flags: MapFlags, + round_to_min: bool, + ) -> Result { + // 用于对齐hint的函数 + let round_hint_to_min = |hint: VirtAddr| { + // 先把hint向下对齐到页边界 + let addr = hint.data() & (!MMArch::PAGE_OFFSET_MASK); + // kdebug!("map_anonymous: hint = {:?}, addr = {addr:#x}", hint); + // 如果hint不是0,且hint小于DEFAULT_MMAP_MIN_ADDR,则对齐到DEFAULT_MMAP_MIN_ADDR + if (addr != 0) && round_to_min && (addr < DEFAULT_MMAP_MIN_ADDR) { + Some(VirtAddr::new(page_align_up(DEFAULT_MMAP_MIN_ADDR))) + } else if addr == 0 { + None + } else { + Some(VirtAddr::new(addr)) + } + }; + // kdebug!("map_anonymous: start_vaddr = {:?}", start_vaddr); + // kdebug!("map_anonymous: len(no align) = {}", len); + + let len = page_align_up(len); + + // kdebug!("map_anonymous: len = {}", len); + + let start_page: VirtPageFrame = self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, mapper, flusher| { + Ok(VMA::zeroed(page, count, flags, mapper, flusher)?) + }, + )?; + + return Ok(start_page); + } + + /// 向进程的地址空间映射页面 + /// + /// # 参数 + /// + /// - `addr`:映射的起始地址,如果为`None`,则由内核自动分配 + /// - `page_count`:映射的页面数量 + /// - `prot_flags`:保护标志 + /// - `map_flags`:映射标志 + /// - `map_func`:映射函数,用于创建VMA + /// + /// # Returns + /// + /// 返回映射的起始虚拟页帧 + /// + /// # Errors + /// + /// - `EINVAL`:参数错误 + pub fn mmap< + F: FnOnce( + VirtPageFrame, + PageFrameCount, + PageFlags, + &mut PageMapper, + &mut dyn Flusher, + ) -> Result, SystemError>, + >( + &mut self, + addr: Option, + page_count: PageFrameCount, + prot_flags: ProtFlags, + map_flags: MapFlags, + map_func: F, + ) -> Result { + if page_count == PageFrameCount::new(0) { + return Err(SystemError::EINVAL); + } + // kdebug!("mmap: addr: {addr:?}, page_count: {page_count:?}, prot_flags: {prot_flags:?}, map_flags: {map_flags:?}"); + + // 找到未使用的区域 + let region = match addr { + Some(vaddr) => { + self.mappings + .find_free_at(self.mmap_min, vaddr, page_count.bytes(), map_flags)? + } + None => self + .mappings + .find_free(self.mmap_min, page_count.bytes()) + .ok_or(SystemError::ENOMEM)?, + }; + + let page = VirtPageFrame::new(region.start()); + + // kdebug!("mmap: page: {:?}, region={region:?}", page.virt_address()); + + compiler_fence(Ordering::SeqCst); + let (mut active, mut inactive); + let flusher = if self.is_current() { + // kdebug!("mmap: current ucontext"); + active = PageFlushAll::new(); + &mut active as &mut dyn Flusher + } else { + // kdebug!("mmap: not current ucontext"); + inactive = InactiveFlusher::new(); + &mut inactive as &mut dyn Flusher + }; + compiler_fence(Ordering::SeqCst); + // 映射页面,并将VMA插入到地址空间的VMA列表中 + self.mappings.insert_vma(map_func( + page, + page_count, + PageFlags::from_prot_flags(prot_flags, true), + &mut self.user_mapper.utable, + flusher, + )?); + + return Ok(page); + } + + /// 取消进程的地址空间中的映射 + /// + /// # 参数 + /// + /// - `start_page`:起始页帧 + /// - `page_count`:取消映射的页帧数量 + /// + /// # Errors + /// + /// - `EINVAL`:参数错误 + /// - `ENOMEM`:内存不足 + pub fn munmap( + &mut self, + start_page: VirtPageFrame, + page_count: PageFrameCount, + ) -> Result<(), SystemError> { + let to_unmap = VirtRegion::new(start_page.virt_address(), page_count.bytes()); + let mut flusher: PageFlushAll = PageFlushAll::new(); + + let regions: Vec> = self.mappings.conflicts(to_unmap).collect::>(); + + for r in regions { + let r = r.lock().region; + let r = self.mappings.remove_vma(&r).unwrap(); + let intersection = r.lock().region().intersect(&to_unmap).unwrap(); + let (before, r, after) = r.extract(intersection).unwrap(); + + // TODO: 当引入后备页映射后,这里需要增加通知文件的逻辑 + + if let Some(before) = before { + // 如果前面有VMA,则需要将前面的VMA重新插入到地址空间的VMA列表中 + self.mappings.insert_vma(before); + } + + if let Some(after) = after { + // 如果后面有VMA,则需要将后面的VMA重新插入到地址空间的VMA列表中 + self.mappings.insert_vma(after); + } + + r.unmap(&mut self.user_mapper.utable, &mut flusher); + } + + // TODO: 当引入后备页映射后,这里需要增加通知文件的逻辑 + + return Ok(()); + } + + pub fn mprotect( + &mut self, + start_page: VirtPageFrame, + page_count: PageFrameCount, + prot_flags: ProtFlags, + ) -> Result<(), SystemError> { + // kdebug!( + // "mprotect: start_page: {:?}, page_count: {:?}, prot_flags:{prot_flags:?}", + // start_page, + // page_count + // ); + let (mut active, mut inactive); + let mut flusher = if self.is_current() { + active = PageFlushAll::new(); + &mut active as &mut dyn Flusher + } else { + inactive = InactiveFlusher::new(); + &mut inactive as &mut dyn Flusher + }; + + let mapper = &mut self.user_mapper.utable; + let region = VirtRegion::new(start_page.virt_address(), page_count.bytes()); + // kdebug!("mprotect: region: {:?}", region); + + let regions = self.mappings.conflicts(region).collect::>(); + // kdebug!("mprotect: regions: {:?}", regions); + + for r in regions { + // kdebug!("mprotect: r: {:?}", r); + let r = r.lock().region().clone(); + let r = self.mappings.remove_vma(&r).unwrap(); + + let intersection = r.lock().region().intersect(®ion).unwrap(); + let (before, r, after) = r.extract(intersection).expect("Failed to extract VMA"); + + if let Some(before) = before { + self.mappings.insert_vma(before); + } + if let Some(after) = after { + self.mappings.insert_vma(after); + } + + let mut r_guard = r.lock(); + // 如果VMA的保护标志不允许指定的修改,则返回错误 + if !r_guard.can_have_flags(prot_flags) { + drop(r_guard); + self.mappings.insert_vma(r.clone()); + return Err(SystemError::EACCES); + } + + let new_flags: PageFlags = r_guard + .flags() + .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) + .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); + + r_guard.remap(new_flags, mapper, &mut flusher)?; + drop(r_guard); + self.mappings.insert_vma(r); + } + + return Ok(()); + } + + /// 创建新的用户栈 + /// + /// ## 参数 + /// + /// - `size`:栈的大小 + pub fn new_user_stack(&mut self, size: usize) -> Result<(), SystemError> { + assert!(self.user_stack.is_none(), "User stack already exists"); + let stack = UserStack::new(self, None, size)?; + self.user_stack = Some(stack); + return Ok(()); + } + + #[inline(always)] + pub fn user_stack_mut(&mut self) -> Option<&mut UserStack> { + return self.user_stack.as_mut(); + } + + /// 取消用户空间内的所有映射 + pub unsafe fn unmap_all(&mut self) { + let mut flusher: PageFlushAll = PageFlushAll::new(); + for vma in self.mappings.iter_vmas() { + vma.unmap(&mut self.user_mapper.utable, &mut flusher); + } + } + + /// 设置进程的堆的内存空间 + /// + /// ## 参数 + /// + /// - `new_brk`:新的堆的结束地址。需要满足页对齐要求,并且是用户空间地址,且大于等于当前的堆的起始地址 + /// + /// ## 返回值 + /// + /// 返回旧的堆的结束地址 + pub unsafe fn set_brk(&mut self, new_brk: VirtAddr) -> Result { + assert!(new_brk.check_aligned(MMArch::PAGE_SIZE)); + + if !new_brk.check_user() || new_brk < self.brk_start { + return Err(SystemError::EFAULT); + } + + let old_brk = self.brk; + // kdebug!("set_brk: old_brk: {:?}, new_brk: {:?}", old_brk, new_brk); + if new_brk > self.brk { + let len = new_brk - self.brk; + let prot_flags = ProtFlags::PROT_READ | ProtFlags::PROT_WRITE | ProtFlags::PROT_EXEC; + let map_flags = MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED; + self.map_anonymous(old_brk, len, prot_flags, map_flags, true)? + .virt_address(); + self.brk = new_brk; + return Ok(old_brk); + } else { + let unmap_len = self.brk - new_brk; + let unmap_start = new_brk; + if unmap_len == 0 { + return Ok(old_brk); + } + self.munmap( + VirtPageFrame::new(unmap_start), + PageFrameCount::from_bytes(unmap_len).unwrap(), + )?; + self.brk = new_brk; + return Ok(old_brk); + } + } + + pub unsafe fn sbrk(&mut self, incr: isize) -> Result { + if incr == 0 { + return Ok(self.brk); + } + + let new_brk = if incr > 0 { + self.brk + incr as usize + } else { + self.brk - (incr.abs() as usize) + }; + + let new_brk = VirtAddr::new(page_align_up(new_brk.data())); + + return self.set_brk(new_brk); + } +} + +impl Drop for InnerAddressSpace { + fn drop(&mut self) { + unsafe { + self.unmap_all(); + } + } +} + +#[derive(Debug, Hash)] +pub struct UserMapper { + pub utable: PageMapper, +} + +impl UserMapper { + pub fn new(utable: PageMapper) -> Self { + return Self { utable }; + } +} + +impl Drop for UserMapper { + fn drop(&mut self) { + if self.utable.is_current() { + // 如果当前要被销毁的用户空间的页表是当前进程的页表,那么就切换回初始内核页表 + unsafe { MMArch::set_table(PageTableKind::User, MMArch::initial_page_table()) } + } + // 释放用户空间顶层页表占用的页帧 + // 请注意,在释放这个页帧之前,用户页表应该已经被完全释放,否则会产生内存泄露 + unsafe { + deallocate_page_frames( + PhysPageFrame::new(self.utable.table().phys()), + PageFrameCount::new(1), + ) + }; + } +} + +/// 用户空间映射信息 +#[derive(Debug)] +pub struct UserMappings { + /// 当前用户空间的虚拟内存区域 + vmas: HashSet>, + /// 当前用户空间的VMA空洞 + vm_holes: BTreeMap, +} + +impl UserMappings { + pub fn new() -> Self { + return Self { + vmas: HashSet::new(), + vm_holes: core::iter::once((VirtAddr::new(0), MMArch::USER_END_VADDR.data())) + .collect::>(), + }; + } + + /// 判断当前进程的VMA内,是否有包含指定的虚拟地址的VMA。 + /// + /// 如果有,返回包含指定虚拟地址的VMA的Arc指针,否则返回None。 + #[allow(dead_code)] + pub fn contains(&self, vaddr: VirtAddr) -> Option> { + for v in self.vmas.iter() { + let guard = v.lock(); + if guard.region.contains(vaddr) { + return Some(v.clone()); + } + } + return None; + } + + /// 获取当前进程的地址空间中,与给定虚拟地址范围有重叠的VMA的迭代器。 + pub fn conflicts(&self, request: VirtRegion) -> impl Iterator> + '_ { + let r = self + .vmas + .iter() + .filter(move |v| !v.lock().region.intersect(&request).is_none()) + .cloned(); + return r; + } + + /// 在当前进程的地址空间中,寻找第一个符合条件的空闲的虚拟内存范围。 + /// + /// @param min_vaddr 最小的起始地址 + /// @param size 请求的大小 + /// + /// @return 如果找到了,返回虚拟内存范围,否则返回None + pub fn find_free(&self, min_vaddr: VirtAddr, size: usize) -> Option { + let _vaddr = min_vaddr; + let mut iter = self + .vm_holes + .iter() + .skip_while(|(hole_vaddr, hole_size)| hole_vaddr.add(**hole_size) <= min_vaddr); + + let (hole_vaddr, size) = iter.find(|(hole_vaddr, hole_size)| { + // 计算当前空洞的可用大小 + let available_size: usize = + if hole_vaddr <= &&min_vaddr && min_vaddr <= hole_vaddr.add(**hole_size) { + **hole_size - (min_vaddr - **hole_vaddr) + } else { + **hole_size + }; + + size <= available_size + })?; + + // 创建一个新的虚拟内存范围。 + let region = VirtRegion::new(cmp::max(*hole_vaddr, min_vaddr), *size); + return Some(region); + } + + pub fn find_free_at( + &self, + min_vaddr: VirtAddr, + vaddr: VirtAddr, + size: usize, + flags: MapFlags, + ) -> Result { + // 如果没有指定地址,那么就在当前进程的地址空间中寻找一个空闲的虚拟内存范围。 + if vaddr == VirtAddr::new(0) { + return self.find_free(min_vaddr, size).ok_or(SystemError::ENOMEM); + } + + // 如果指定了地址,那么就检查指定的地址是否可用。 + + let requested = VirtRegion::new(vaddr, size); + + if requested.end() >= MMArch::USER_END_VADDR || !vaddr.check_aligned(MMArch::PAGE_SIZE) { + return Err(SystemError::EINVAL); + } + + if let Some(_x) = self.conflicts(requested).next() { + if flags.contains(MapFlags::MAP_FIXED_NOREPLACE) { + // 如果指定了 MAP_FIXED_NOREPLACE 标志,由于所指定的地址无法成功建立映射,则放弃映射,不对地址做修正 + return Err(SystemError::EEXIST); + } + + if flags.contains(MapFlags::MAP_FIXED) { + // todo: 支持MAP_FIXED标志对已有的VMA进行覆盖 + return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); + } + + // 如果没有指定MAP_FIXED标志,那么就对地址做修正 + let requested = self.find_free(min_vaddr, size).ok_or(SystemError::ENOMEM)?; + return Ok(requested); + } + + return Ok(requested); + } + + /// 在当前进程的地址空间中,保留一个指定大小的区域,使得该区域不在空洞中。 + /// 该函数会修改vm_holes中的空洞信息。 + /// + /// @param region 要保留的区域 + /// + /// 请注意,在调用本函数之前,必须先确定region所在范围内没有VMA。 + fn reserve_hole(&mut self, region: &VirtRegion) { + let prev_hole: Option<(&VirtAddr, &mut usize)> = + self.vm_holes.range_mut(..region.start()).next_back(); + + if let Some((prev_hole_vaddr, prev_hole_size)) = prev_hole { + let prev_hole_end = prev_hole_vaddr.add(*prev_hole_size); + + if prev_hole_end > region.start() { + // 如果前一个空洞的结束地址大于当前空洞的起始地址,那么就需要调整前一个空洞的大小。 + *prev_hole_size = region.start().data() - prev_hole_vaddr.data(); + } + + if prev_hole_end > region.end() { + // 如果前一个空洞的结束地址大于当前空洞的结束地址,那么就需要增加一个新的空洞。 + self.vm_holes + .insert(region.end(), prev_hole_end - region.end()); + } + } + } + + /// 在当前进程的地址空间中,释放一个指定大小的区域,使得该区域成为一个空洞。 + /// 该函数会修改vm_holes中的空洞信息。 + fn unreserve_hole(&mut self, region: &VirtRegion) { + // 如果将要插入的空洞与后一个空洞相邻,那么就需要合并。 + let next_hole_size: Option = self.vm_holes.remove(®ion.end()); + + if let Some((_prev_hole_vaddr, prev_hole_size)) = self + .vm_holes + .range_mut(..region.start()) + .next_back() + .filter(|(offset, size)| offset.data() + **size == region.start().data()) + { + *prev_hole_size += region.size() + next_hole_size.unwrap_or(0); + } else { + self.vm_holes + .insert(region.start(), region.size() + next_hole_size.unwrap_or(0)); + } + } + + /// 在当前进程的映射关系中,插入一个新的VMA。 + pub fn insert_vma(&mut self, vma: Arc) { + let region = vma.lock().region.clone(); + // 要求插入的地址范围必须是空闲的,也就是说,当前进程的地址空间中,不能有任何与之重叠的VMA。 + assert!(self.conflicts(region).next().is_none()); + self.reserve_hole(®ion); + + self.vmas.insert(vma); + } + + /// @brief 删除一个VMA,并把对应的地址空间加入空洞中。 + /// + /// 这里不会取消VMA对应的地址的映射 + /// + /// @param region 要删除的VMA所在的地址范围 + /// + /// @return 如果成功删除了VMA,则返回被删除的VMA,否则返回None + /// 如果没有可以删除的VMA,则不会执行删除操作,并报告失败。 + pub fn remove_vma(&mut self, region: &VirtRegion) -> Option> { + // 请注意,由于这里会对每个VMA加锁,因此性能很低 + let vma: Arc = self + .vmas + .drain_filter(|vma| vma.lock().region == *region) + .next()?; + self.unreserve_hole(region); + + return Some(vma); + } + + /// @brief Get the iterator of all VMAs in this process. + pub fn iter_vmas(&self) -> hashbrown::hash_set::Iter> { + return self.vmas.iter(); + } +} + +impl Default for UserMappings { + fn default() -> Self { + return Self::new(); + } +} + +/// 加了锁的VMA +/// +/// 备注:进行性能测试,看看SpinLock和RwLock哪个更快。 +#[derive(Debug)] +pub struct LockedVMA(SpinLock); + +impl core::hash::Hash for LockedVMA { + fn hash(&self, state: &mut H) { + self.0.lock().hash(state); + } +} + +impl PartialEq for LockedVMA { + fn eq(&self, other: &Self) -> bool { + self.0.lock().eq(&other.0.lock()) + } +} + +impl Eq for LockedVMA {} + +#[allow(dead_code)] +impl LockedVMA { + pub fn new(vma: VMA) -> Arc { + let r = Arc::new(Self(SpinLock::new(vma))); + r.0.lock().self_ref = Arc::downgrade(&r); + return r; + } + + pub fn lock(&self) -> SpinLockGuard { + return self.0.lock(); + } + + /// 调整当前VMA的页面的标志位 + /// + /// TODO:增加调整虚拟页映射的物理地址的功能 + /// + /// @param flags 新的标志位 + /// @param mapper 页表映射器 + /// @param flusher 页表项刷新器 + /// + pub fn remap( + &self, + flags: PageFlags, + mapper: &mut PageMapper, + mut flusher: impl Flusher, + ) -> Result<(), SystemError> { + let mut guard = self.lock(); + assert!(guard.mapped); + for page in guard.region.pages() { + // 暂时要求所有的页帧都已经映射到页表 + // TODO: 引入Lazy Mapping, 通过缺页中断来映射页帧,这里就不必要求所有的页帧都已经映射到页表了 + let r = unsafe { + mapper + .remap(page.virt_address(), flags) + .expect("Failed to remap, beacuse of some page is not mapped") + }; + flusher.consume(r); + } + guard.flags = flags; + return Ok(()); + } + + pub fn unmap(&self, mapper: &mut PageMapper, mut flusher: impl Flusher) { + let mut guard = self.lock(); + assert!(guard.mapped); + for page in guard.region.pages() { + let (paddr, _, flush) = unsafe { mapper.unmap_phys(page.virt_address(), true) } + .expect("Failed to unmap, beacuse of some page is not mapped"); + + // todo: 获取物理页的anon_vma的守卫 + + // todo: 从anon_vma中删除当前VMA + + // todo: 如果物理页的anon_vma链表长度为0,则释放物理页. + + // 目前由于还没有实现共享页,所以直接释放物理页也没问题。 + // 但是在实现共享页之后,就不能直接释放物理页了,需要在anon_vma链表长度为0的时候才能释放物理页 + unsafe { deallocate_page_frames(PhysPageFrame::new(paddr), PageFrameCount::new(1)) }; + + flusher.consume(flush); + } + guard.mapped = false; + } + + pub fn mapped(&self) -> bool { + return self.0.lock().mapped; + } + + /// 将当前VMA进行切分,切分成3个VMA,分别是: + /// + /// 1. 前面的VMA,如果没有则为None + /// 2. 中间的VMA,也就是传入的Region + /// 3. 后面的VMA,如果没有则为None + pub fn extract( + &self, + region: VirtRegion, + ) -> Option<( + Option>, + Arc, + Option>, + )> { + assert!(region.start().check_aligned(MMArch::PAGE_SIZE)); + assert!(region.end().check_aligned(MMArch::PAGE_SIZE)); + + let mut guard = self.lock(); + { + // 如果传入的region不在当前VMA的范围内,则直接返回None + if unlikely(region.start() < guard.region.start() || region.end() > guard.region.end()) + { + return None; + } + + let intersect: Option = guard.region.intersect(®ion); + // 如果当前VMA不包含region,则直接返回None + if unlikely(intersect.is_none()) { + return None; + } + let intersect: VirtRegion = intersect.unwrap(); + if unlikely(intersect == guard.region) { + // 如果当前VMA完全包含region,则直接返回当前VMA + return Some((None, guard.self_ref.upgrade().unwrap(), None)); + } + } + + let before: Option> = guard.region.before(®ion).map(|virt_region| { + let mut vma: VMA = unsafe { guard.clone() }; + vma.region = virt_region; + + let vma: Arc = LockedVMA::new(vma); + vma + }); + + let after: Option> = guard.region.after(®ion).map(|virt_region| { + let mut vma: VMA = unsafe { guard.clone() }; + vma.region = virt_region; + + let vma: Arc = LockedVMA::new(vma); + vma + }); + + guard.region = region; + + // TODO: 重新设置before、after这两个VMA里面的物理页的anon_vma + + return Some((before, guard.self_ref.upgrade().unwrap(), after)); + } +} + +/// @brief 虚拟内存区域 +#[derive(Debug)] +pub struct VMA { + /// 虚拟内存区域对应的虚拟地址范围 + region: VirtRegion, + /// VMA内的页帧的标志 + flags: PageFlags, + /// VMA内的页帧是否已经映射到页表 + mapped: bool, + /// VMA所属的用户地址空间 + user_address_space: Option>, + self_ref: Weak, +} + +impl core::hash::Hash for VMA { + fn hash(&self, state: &mut H) { + self.region.hash(state); + self.flags.hash(state); + self.mapped.hash(state); + } +} + +#[allow(dead_code)] +impl VMA { + pub fn region(&self) -> &VirtRegion { + return &self.region; + } + + /// # 拷贝当前VMA的内容 + /// + /// ### 安全性 + /// + /// 由于这样操作可能由于错误的拷贝,导致内存泄露、内存重复释放等问题,所以需要小心使用。 + pub unsafe fn clone(&self) -> Self { + return Self { + region: self.region, + flags: self.flags, + mapped: self.mapped, + user_address_space: self.user_address_space.clone(), + self_ref: self.self_ref.clone(), + }; + } + + #[inline(always)] + pub fn flags(&self) -> PageFlags { + return self.flags; + } + + pub fn pages(&self) -> VirtPageFrameIter { + return VirtPageFrameIter::new( + VirtPageFrame::new(self.region.start()), + VirtPageFrame::new(self.region.end()), + ); + } + + pub fn remap( + &mut self, + flags: PageFlags, + mapper: &mut PageMapper, + mut flusher: impl Flusher, + ) -> Result<(), SystemError> { + assert!(self.mapped); + for page in self.region.pages() { + // kdebug!("remap page {:?}", page.virt_address()); + // 暂时要求所有的页帧都已经映射到页表 + // TODO: 引入Lazy Mapping, 通过缺页中断来映射页帧,这里就不必要求所有的页帧都已经映射到页表了 + let r = unsafe { + mapper + .remap(page.virt_address(), flags) + .expect("Failed to remap, beacuse of some page is not mapped") + }; + // kdebug!("consume page {:?}", page.virt_address()); + flusher.consume(r); + // kdebug!("remap page {:?} done", page.virt_address()); + } + self.flags = flags; + return Ok(()); + } + + /// 检查当前VMA是否可以拥有指定的标志位 + /// + /// ## 参数 + /// + /// - `prot_flags` 要检查的标志位 + pub fn can_have_flags(&self, prot_flags: ProtFlags) -> bool { + return (self.flags.has_write() || !prot_flags.contains(ProtFlags::PROT_WRITE)) + && (self.flags.has_execute() || !prot_flags.contains(ProtFlags::PROT_EXEC)); + } + + /// 把物理地址映射到虚拟地址 + /// + /// @param phys 要映射的物理地址 + /// @param destination 要映射到的虚拟地址 + /// @param count 要映射的页帧数量 + /// @param flags 页面标志位 + /// @param mapper 页表映射器 + /// @param flusher 页表项刷新器 + /// + /// @return 返回映射后的虚拟内存区域 + pub fn physmap( + phys: PhysPageFrame, + destination: VirtPageFrame, + count: PageFrameCount, + flags: PageFlags, + mapper: &mut PageMapper, + mut flusher: impl Flusher, + ) -> Result, SystemError> { + { + let mut cur_phy = phys; + let mut cur_dest = destination; + + for _ in 0..count.data() { + // 将物理页帧映射到虚拟页帧 + let r = unsafe { + mapper.map_phys(cur_dest.virt_address(), cur_phy.phys_address(), flags) + } + .expect("Failed to map phys, may be OOM error"); + + // todo: 增加OOM处理 + + // todo: 将VMA加入到anon_vma中 + + // 刷新TLB + flusher.consume(r); + + cur_phy = cur_phy.next(); + cur_dest = cur_dest.next(); + } + } + + let r: Arc = LockedVMA::new(VMA { + region: VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), + flags, + mapped: true, + user_address_space: None, + self_ref: Weak::default(), + }); + return Ok(r); + } + + /// 从页分配器中分配一些物理页,并把它们映射到指定的虚拟地址,然后创建VMA + /// + /// @param destination 要映射到的虚拟地址 + /// @param count 要映射的页帧数量 + /// @param flags 页面标志位 + /// @param mapper 页表映射器 + /// @param flusher 页表项刷新器 + /// + /// @return 返回映射后的虚拟内存区域 + pub fn zeroed( + destination: VirtPageFrame, + page_count: PageFrameCount, + flags: PageFlags, + mapper: &mut PageMapper, + mut flusher: impl Flusher, + ) -> Result, SystemError> { + let mut cur_dest: VirtPageFrame = destination; + // kdebug!( + // "VMA::zeroed: page_count = {:?}, destination={destination:?}", + // page_count + // ); + for _ in 0..page_count.data() { + // kdebug!( + // "VMA::zeroed: cur_dest={cur_dest:?}, vaddr = {:?}", + // cur_dest.virt_address() + // ); + let r = unsafe { mapper.map(cur_dest.virt_address(), flags) } + .expect("Failed to map zero, may be OOM error"); + // todo: 将VMA加入到anon_vma中 + // todo: 增加OOM处理 + + // 稍后再刷新TLB,这里取消刷新 + flusher.consume(r); + cur_dest = cur_dest.next(); + } + let r = LockedVMA::new(VMA { + region: VirtRegion::new( + destination.virt_address(), + page_count.data() * MMArch::PAGE_SIZE, + ), + flags, + mapped: true, + user_address_space: None, + self_ref: Weak::default(), + }); + drop(flusher); + // kdebug!("VMA::zeroed: flusher dropped"); + + // 清空这些内存 + let virt_iter = VirtPageFrameIter::new(destination, destination.add(page_count)); + for frame in virt_iter { + let paddr = mapper.translate(frame.virt_address()).unwrap().0; + + unsafe { + let vaddr = MMArch::phys_2_virt(paddr).unwrap(); + MMArch::write_bytes(vaddr, 0, MMArch::PAGE_SIZE); + } + } + // kdebug!("VMA::zeroed: done"); + return Ok(r); + } +} + +impl Drop for VMA { + fn drop(&mut self) { + // 当VMA被释放时,需要确保它已经被从页表中解除映射 + assert!(!self.mapped, "VMA is still mapped"); + } +} + +impl PartialEq for VMA { + fn eq(&self, other: &Self) -> bool { + return self.region == other.region; + } +} + +impl Eq for VMA {} + +impl PartialOrd for VMA { + fn partial_cmp(&self, other: &Self) -> Option { + return self.region.partial_cmp(&other.region); + } +} + +impl Ord for VMA { + fn cmp(&self, other: &Self) -> cmp::Ordering { + return self.region.cmp(&other.region); + } +} + +#[derive(Debug)] +pub struct UserStack { + // 栈底地址 + stack_bottom: VirtAddr, + // 当前已映射的大小 + mapped_size: usize, + /// 栈顶地址(这个值需要仔细确定!因为它可能不会实时与用户栈的真实栈顶保持一致!要小心!) + current_sp: VirtAddr, +} + +impl UserStack { + /// 默认的用户栈底地址 + pub const DEFAULT_USER_STACK_BOTTOM: VirtAddr = MMArch::USER_STACK_START; + /// 默认的用户栈大小为8MB + pub const DEFAULT_USER_STACK_SIZE: usize = 8 * 1024 * 1024; + /// 用户栈的保护页数量 + pub const GUARD_PAGES_NUM: usize = 4; + + /// 创建一个用户栈 + pub fn new( + vm: &mut InnerAddressSpace, + stack_bottom: Option, + stack_size: usize, + ) -> Result { + let stack_bottom = stack_bottom.unwrap_or(Self::DEFAULT_USER_STACK_BOTTOM); + assert!(stack_bottom.check_aligned(MMArch::PAGE_SIZE)); + + // 分配用户栈的保护页 + let guard_size = Self::GUARD_PAGES_NUM * MMArch::PAGE_SIZE; + let actual_stack_bottom = stack_bottom - guard_size; + + let mut prot_flags = ProtFlags::PROT_READ | ProtFlags::PROT_WRITE; + let map_flags = + MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED_NOREPLACE; + // kdebug!( + // "map anonymous stack: {:?} {}", + // actual_stack_bottom, + // guard_size + // ); + vm.map_anonymous( + actual_stack_bottom, + guard_size, + prot_flags, + map_flags, + false, + )?; + // test_buddy(); + // 设置保护页只读 + prot_flags.remove(ProtFlags::PROT_WRITE); + // kdebug!( + // "to mprotect stack guard pages: {:?} {}", + // actual_stack_bottom, + // guard_size + // ); + vm.mprotect( + VirtPageFrame::new(actual_stack_bottom), + PageFrameCount::new(Self::GUARD_PAGES_NUM), + prot_flags, + )?; + + // kdebug!( + // "mprotect stack guard pages done: {:?} {}", + // actual_stack_bottom, + // guard_size + // ); + + let mut user_stack = UserStack { + stack_bottom: actual_stack_bottom, + mapped_size: guard_size, + current_sp: actual_stack_bottom - guard_size, + }; + + // kdebug!("extend user stack: {:?} {}", stack_bottom, stack_size); + // 分配用户栈 + user_stack.initial_extend(vm, stack_size)?; + // kdebug!("user stack created: {:?} {}", stack_bottom, stack_size); + return Ok(user_stack); + } + + fn initial_extend( + &mut self, + vm: &mut InnerAddressSpace, + mut bytes: usize, + ) -> Result<(), SystemError> { + let prot_flags = ProtFlags::PROT_READ | ProtFlags::PROT_WRITE | ProtFlags::PROT_EXEC; + let map_flags = MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS; + + bytes = page_align_up(bytes); + self.mapped_size += bytes; + + vm.map_anonymous( + self.stack_bottom - self.mapped_size, + bytes, + prot_flags, + map_flags, + false, + )?; + + return Ok(()); + } + + /// 扩展用户栈 + /// + /// ## 参数 + /// + /// - `vm` 用户地址空间结构体 + /// - `bytes` 要扩展的字节数 + /// + /// ## 返回值 + /// + /// - **Ok(())** 扩展成功 + /// - **Err(SystemError)** 扩展失败 + #[allow(dead_code)] + pub fn extend( + &mut self, + vm: &mut RwLockWriteGuard, + mut bytes: usize, + ) -> Result<(), SystemError> { + let prot_flags = ProtFlags::PROT_READ | ProtFlags::PROT_WRITE | ProtFlags::PROT_EXEC; + let map_flags = MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS; + + bytes = page_align_up(bytes); + self.mapped_size += bytes; + + vm.map_anonymous( + self.stack_bottom - self.mapped_size, + bytes, + prot_flags, + map_flags, + false, + )?; + + return Ok(()); + } + + /// 获取栈顶地址 + /// + /// 请注意,如果用户栈的栈顶地址发生变化,这个值可能不会实时更新! + pub fn sp(&self) -> VirtAddr { + return self.current_sp; + } + + pub unsafe fn set_sp(&mut self, sp: VirtAddr) { + self.current_sp = sp; + } + + /// 仅仅克隆用户栈的信息,不会克隆用户栈的内容/映射 + pub unsafe fn clone_info_only(&self) -> Self { + return Self { + stack_bottom: self.stack_bottom, + mapped_size: self.mapped_size, + current_sp: self.current_sp, + }; + } + + /// 获取当前用户栈的大小(不包括保护页) + pub fn stack_size(&self) -> usize { + return self.mapped_size - Self::GUARD_PAGES_NUM * MMArch::PAGE_SIZE; + } +} diff --git a/kernel/src/mm/utils.c b/kernel/src/mm/utils.c deleted file mode 100644 index 9f9cd048..00000000 --- a/kernel/src/mm/utils.c +++ /dev/null @@ -1,109 +0,0 @@ -#include "internal.h" - -extern uint64_t mm_total_2M_pages; - -/** - * @brief 获取指定虚拟地址处映射的物理地址 - * - * @param mm 内存空间分布结构体 - * @param vaddr 虚拟地址 - * @return uint64_t 已映射的物理地址 - */ -uint64_t __mm_get_paddr(struct mm_struct *mm, uint64_t vaddr) -{ - ul *tmp; - - tmp = phys_2_virt((ul *)(((ul)mm->pgd) & (~0xfffUL)) + ((vaddr >> PAGE_GDT_SHIFT) & 0x1ff)); - - // pml4页表项为0 - if (*tmp == 0) - return 0; - - tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((vaddr >> PAGE_1G_SHIFT) & 0x1ff)); - - // pdpt页表项为0 - if (*tmp == 0) - return 0; - - // 读取pdt页表项 - tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(vaddr) >> PAGE_2M_SHIFT) & 0x1ff))); - - // pde页表项为0 - if (*tmp == 0) - return 0; - - if (*tmp & (1 << 7)) - { - // 当前为2M物理页 - return (*tmp) & (~0x1fffUL); - } - else - { - // 存在4级页表 - tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(vaddr) >> PAGE_4K_SHIFT) & 0x1ff))); - - return (*tmp) & (~0x1ffUL); - } -} - -/** - * @brief 检测指定地址是否已经被映射 - * - * @param page_table_phys_addr 页表的物理地址 - * @param virt_addr 要检测的地址 - * @return true 已经被映射 - * @return false - */ -bool mm_check_mapped(ul page_table_phys_addr, uint64_t virt_addr) -{ - ul *tmp; - - tmp = phys_2_virt((ul *)((ul)page_table_phys_addr & (~0xfffUL)) + ((virt_addr >> PAGE_GDT_SHIFT) & 0x1ff)); - - // pml4页表项为0 - if (*tmp == 0) - return 0; - - tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr >> PAGE_1G_SHIFT) & 0x1ff)); - - // pdpt页表项为0 - if (*tmp == 0) - return 0; - - // 读取pdt页表项 - tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr) >> PAGE_2M_SHIFT) & 0x1ff))); - - // pde页表项为0 - if (*tmp == 0) - return 0; - - if (*tmp & (1 << 7)) - { - // 当前为2M物理页 - return true; - } - else - { - // 存在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) < mm_total_2M_pages)) - return 1; - else - return 0; -} diff --git a/kernel/src/mm/vma.c b/kernel/src/mm/vma.c deleted file mode 100644 index 54ed527c..00000000 --- a/kernel/src/mm/vma.c +++ /dev/null @@ -1,275 +0,0 @@ -#include "mm.h" -#include "slab.h" -#include "internal.h" - -/** - * @brief 获取一块新的vma结构体,并将其与指定的mm进行绑定 - * - * @param mm 与VMA绑定的内存空间分布结构体 - * @return struct vm_area_struct* 新的VMA - */ -struct vm_area_struct *vm_area_alloc(struct mm_struct *mm) -{ - struct vm_area_struct *vma = (struct vm_area_struct *)kmalloc(sizeof(struct vm_area_struct), 0); - if (vma) - vma_init(vma, mm); - return vma; -} - -/** - * @brief 从链表中删除指定的vma结构体 - * - * @param vma - */ -void vm_area_del(struct vm_area_struct *vma) -{ - if (vma->vm_mm == NULL) - return; - __vma_unlink_list(vma->vm_mm, vma); -} - -/** - * @brief 释放vma结构体 - * - * @param vma 待释放的vma结构体 - */ -void vm_area_free(struct vm_area_struct *vma) -{ - if (vma->vm_prev == NULL && vma->vm_next == NULL) // 如果当前是剩余的最后一个vma - vma->vm_mm->vmas = NULL; - kfree(vma); -} - -/** - * @brief 将vma结构体插入mm_struct的链表之中 - * - * @param mm 内存空间分布结构体 - * @param vma 待插入的VMA结构体 - * @param prev 链表的前一个结点 - */ -void __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev) -{ - struct vm_area_struct *next = NULL; - vma->vm_prev = prev; - if (prev) // 若指定了前一个结点,则直接连接 - { - next = prev->vm_next; - prev->vm_next = vma; - } - else // 否则将vma直接插入到给定的mm的vma链表之中 - { - next = mm->vmas; - mm->vmas = vma; - } - - vma->vm_next = next; - - if (next != NULL) - next->vm_prev = vma; -} - -/** - * @brief 将vma给定结构体从vma链表的结点之中删除 - * - * @param mm 内存空间分布结构体 - * @param vma 待插入的VMA结构体 - */ -void __vma_unlink_list(struct mm_struct *mm, struct vm_area_struct *vma) -{ - struct vm_area_struct *prev, *next; - next = vma->vm_next; - prev = vma->vm_prev; - if (prev) - prev->vm_next = next; - else // 当前vma是链表中的第一个vma - mm->vmas = next; - - if (next) - next->vm_prev = prev; -} - -/** - * @brief 查找第一个符合“addr < vm_end”条件的vma - * - * @param mm 内存空间分布结构体 - * @param addr 虚拟地址 - * @return struct vm_area_struct* 符合条件的vma - */ -struct vm_area_struct *vma_find(struct mm_struct *mm, uint64_t addr) -{ - struct vm_area_struct *vma = mm->vmas; - struct vm_area_struct *result = NULL; - while (vma != NULL) - { - if (vma->vm_end > addr) - { - result = vma; - break; - } - vma = vma->vm_next; - } - return result; -} - -/** - * @brief 插入vma - * - * @param mm - * @param vma - * @return int - */ -int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma) -{ - - struct vm_area_struct *prev; - - prev = vma_find(mm, vma->vm_start); - - if (prev && prev->vm_start <= vma->vm_start && prev->vm_end >= vma->vm_end) - { - // 已经存在了相同的vma - return -EEXIST; - } - // todo: bugfix: 这里的第二种情况貌似从来不会满足 - else if (prev && ((vma->vm_start >= prev->vm_start && vma->vm_start <= prev->vm_end) || (prev->vm_start <= vma->vm_end && prev->vm_start >= vma->vm_start))) - { - //部分重叠 - if ((!CROSS_2M_BOUND(vma->vm_start, prev->vm_start)) && (!CROSS_2M_BOUND(vma->vm_end, prev->vm_end))&& vma->vm_end) - { - //合并vma 并改变链表vma的范围 - kdebug("before combining vma:vm_start = %#018lx, vm_end = %#018lx\n", vma->vm_start, vma->vm_end); - - prev->vm_start = (vma->vm_start < prev->vm_start )? vma->vm_start : prev->vm_start; - prev->vm_end = (vma->vm_end > prev->vm_end) ? vma->vm_end : prev->vm_end; - // 计算page_offset - prev->page_offset = prev->vm_start - (prev->vm_start & PAGE_2M_MASK); - kdebug("combined vma:vm_start = %#018lx, vm_end = %#018lx\nprev:vm_start = %018lx, vm_end = %018lx\n", vma->vm_start, vma->vm_end, prev->vm_start, prev->vm_end); - kinfo("vma has same part\n"); - return __VMA_MERGED; - } - } - - // prev = vma_find(mm, vma->vm_start); - - if (prev == NULL) // 要将当前vma插入到链表的尾部 - { - struct vm_area_struct *ptr = mm->vmas; - while (ptr) - { - if (ptr->vm_next) - ptr = ptr->vm_next; - else - { - prev = ptr; - break; - } - } - } - else - prev = prev->vm_prev; - __vma_link_list(mm, vma, prev); - return 0; -} - -/** - * @brief 创建anon_vma,并将其与页面结构体进行绑定 - * 若提供的页面结构体指针为NULL,则只创建,不绑定 - * - * @param page 页面结构体的指针 - * @param lock_page 是否将页面结构体加锁 - * @return struct anon_vma_t* 创建好的anon_vma - */ -struct anon_vma_t *__anon_vma_create_alloc(struct Page *page, bool lock_page) -{ - struct anon_vma_t *anon_vma = (struct anon_vma_t *)kmalloc(sizeof(struct anon_vma_t), 0); - if (unlikely(anon_vma == NULL)) - return NULL; - memset(anon_vma, 0, sizeof(struct anon_vma_t)); - - list_init(&anon_vma->vma_list); - semaphore_init(&anon_vma->sem, 1); - - // 需要和page进行绑定 - if (page != NULL) - { - if (lock_page == true) // 需要加锁 - { - uint64_t rflags; - spin_lock(&page->op_lock); - page->anon_vma = anon_vma; - spin_unlock(&page->op_lock); - } - else - page->anon_vma = anon_vma; - - anon_vma->page = page; - } - return anon_vma; -} - -/** - * @brief 将指定的vma加入到anon_vma的管理范围之中 - * - * @param anon_vma 页面的anon_vma - * @param vma 待加入的vma - * @return int 返回码 - */ -int __anon_vma_add(struct anon_vma_t *anon_vma, struct vm_area_struct *vma) -{ - semaphore_down(&anon_vma->sem); - list_add(&anon_vma->vma_list, &vma->anon_vma_list); - vma->anon_vma = anon_vma; - atomic_inc(&anon_vma->ref_count); - semaphore_up(&anon_vma->sem); - return 0; -} - -/** - * @brief 释放anon vma结构体 - * - * @param anon_vma 待释放的anon_vma结构体 - * @return int 返回码 - */ -int __anon_vma_free(struct anon_vma_t *anon_vma) -{ - if (anon_vma->page != NULL) - { - spin_lock(&anon_vma->page->op_lock); - anon_vma->page->anon_vma = NULL; - spin_unlock(&anon_vma->page->op_lock); - } - kfree(anon_vma); - - return 0; -} - -/** - * @brief 从anon_vma的管理范围中删除指定的vma - * (在进入这个函数之前,应该要对anon_vma加锁) - * @param vma 将要取消对应的anon_vma管理的vma结构体 - * @return int 返回码 - */ -int __anon_vma_del(struct vm_area_struct *vma) -{ - // 当前vma没有绑定anon_vma - if (vma->anon_vma == NULL) - return -EINVAL; - - list_del(&vma->anon_vma_list); - atomic_dec(&vma->anon_vma->ref_count); - - // 若当前anon_vma的引用计数归零,则意味着可以释放内存页 - if (unlikely(atomic_read(&vma->anon_vma->ref_count) == 0)) // 应当释放该anon_vma - { - // 若页面结构体是mmio创建的,则释放页面结构体 - if (vma->anon_vma->page->attr & PAGE_DEVICE) - kfree(vma->anon_vma->page); - else - free_pages(vma->anon_vma->page, 1); - __anon_vma_free(vma->anon_vma); - } - - // 清理当前vma的关联数据 - vma->anon_vma = NULL; - list_init(&vma->anon_vma_list); -} diff --git a/kernel/src/process/abi.rs b/kernel/src/process/abi.rs new file mode 100644 index 00000000..58a7d8ab --- /dev/null +++ b/kernel/src/process/abi.rs @@ -0,0 +1,86 @@ +/// An enumeration of the possible values for the `AT_*` constants. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum AtType { + /// End of vector. + Null, + /// Entry should be ignored. + Ignore, + /// File descriptor of program. + ExecFd, + /// Program headers for program. + Phdr, + /// Size of program header entry. + PhEnt, + /// Number of program headers. + PhNum, + /// System page size. + PageSize, + /// Base address of interpreter. + Base, + /// Flags. + Flags, + /// Entry point of program. + Entry, + /// Program is not ELF. + NotElf, + /// Real uid. + Uid, + /// Effective uid. + EUid, + /// Real gid. + Gid, + /// Effective gid. + EGid, + /// String identifying CPU for optimizations. + Platform, + /// Arch dependent hints at CPU capabilities. + HwCap, + /// Frequency at which times() increments. + ClkTck, + /// Secure mode boolean. + Secure, + /// String identifying real platform, may differ from AT_PLATFORM. + BasePlatform, + /// Address of 16 random bytes. + Random, + /// Extension of AT_HWCAP. + HwCap2, + /// Filename of program. + ExecFn, + /// Minimal stack size for signal delivery. + MinSigStackSize, +} + +impl TryFrom for AtType { + type Error = &'static str; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(AtType::Null), + 1 => Ok(AtType::Ignore), + 2 => Ok(AtType::ExecFd), + 3 => Ok(AtType::Phdr), + 4 => Ok(AtType::PhEnt), + 5 => Ok(AtType::PhNum), + 6 => Ok(AtType::PageSize), + 7 => Ok(AtType::Base), + 8 => Ok(AtType::Flags), + 9 => Ok(AtType::Entry), + 10 => Ok(AtType::NotElf), + 11 => Ok(AtType::Uid), + 12 => Ok(AtType::EUid), + 13 => Ok(AtType::Gid), + 14 => Ok(AtType::EGid), + 15 => Ok(AtType::Platform), + 16 => Ok(AtType::HwCap), + 17 => Ok(AtType::ClkTck), + 23 => Ok(AtType::Secure), + 24 => Ok(AtType::BasePlatform), + 25 => Ok(AtType::Random), + 26 => Ok(AtType::HwCap2), + 31 => Ok(AtType::ExecFn), + 51 => Ok(AtType::MinSigStackSize), + _ => Err("Invalid value for AtType"), + } + } +} diff --git a/kernel/src/process/c_adapter.rs b/kernel/src/process/c_adapter.rs new file mode 100644 index 00000000..f7a5a03c --- /dev/null +++ b/kernel/src/process/c_adapter.rs @@ -0,0 +1,115 @@ +use core::{ffi::c_void, ptr::null_mut}; + +use alloc::boxed::Box; + +use crate::{ + arch::{asm::current::current_pcb, fpu::FpState}, + include::bindings::bindings::process_control_block, + syscall::SystemError, +}; + +use super::{fork::copy_mm, process::init_stdio, process_init}; + +#[no_mangle] +pub extern "C" fn rs_process_init() { + process_init(); +} + +#[no_mangle] +pub extern "C" fn rs_process_copy_mm(clone_vm: bool, new_pcb: &mut process_control_block) -> usize { + return copy_mm(clone_vm, new_pcb) + .map(|_| 0) + .unwrap_or_else(|err| err.to_posix_errno() as usize); +} + +/// @brief 初始化当前进程的文件描述符数组 +/// 请注意,如果当前进程已经有文件描述符数组,那么本操作将被禁止 +#[no_mangle] +pub extern "C" fn process_init_files() -> i32 { + let r = current_pcb().init_files(); + if r.is_ok() { + return 0; + } else { + return r.unwrap_err().to_posix_errno(); + } +} + +#[no_mangle] +pub extern "C" fn rs_drop_address_space(pcb: &'static mut process_control_block) -> i32 { + unsafe { + pcb.drop_address_space(); + } + return 0; +} + +/// @brief 拷贝当前进程的文件描述符信息 +/// +/// @param clone_flags 克隆标志位 +/// @param pcb 新的进程的pcb +#[no_mangle] +pub extern "C" fn process_copy_files( + clone_flags: u64, + from: &'static process_control_block, +) -> i32 { + let r = current_pcb().copy_files(clone_flags, from); + if r.is_ok() { + return 0; + } else { + return r.unwrap_err().to_posix_errno(); + } +} + +/// @brief 回收进程的文件描述符数组 +/// +/// @param pcb 要被回收的进程的pcb +/// +/// @return i32 +#[no_mangle] +pub extern "C" fn process_exit_files(pcb: &'static mut process_control_block) -> i32 { + let r: Result<(), SystemError> = pcb.exit_files(); + if r.is_ok() { + return 0; + } else { + return r.unwrap_err().to_posix_errno(); + } +} + +/// @brief 复制当前进程的浮点状态 +#[allow(dead_code)] +#[no_mangle] +pub extern "C" fn rs_dup_fpstate() -> *mut c_void { + // 如果当前进程没有浮点状态,那么就返回一个默认的浮点状态 + if current_pcb().fp_state == null_mut() { + return Box::leak(Box::new(FpState::default())) as *mut FpState as usize as *mut c_void; + } else { + // 如果当前进程有浮点状态,那么就复制一个新的浮点状态 + let state = current_pcb().fp_state as usize as *mut FpState; + unsafe { + let s = state.as_ref().unwrap(); + let state: &mut FpState = Box::leak(Box::new(s.clone())); + + return state as *mut FpState as usize as *mut c_void; + } + } +} + +/// @brief 释放进程的浮点状态所占用的内存 +#[no_mangle] +pub extern "C" fn rs_process_exit_fpstate(pcb: &'static mut process_control_block) { + if pcb.fp_state != null_mut() { + let state = pcb.fp_state as usize as *mut FpState; + unsafe { + drop(Box::from_raw(state)); + } + } +} + +#[no_mangle] +pub extern "C" fn rs_init_stdio() -> i32 { + let r = init_stdio(); + if r.is_ok() { + return 0; + } else { + return r.unwrap_err().to_posix_errno(); + } +} diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs new file mode 100644 index 00000000..3e27a0d2 --- /dev/null +++ b/kernel/src/process/exec.rs @@ -0,0 +1,288 @@ +use core::{fmt::Debug, ptr::null}; + +use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec}; + +use crate::{ + filesystem::vfs::{ + file::{File, FileMode}, + ROOT_INODE, + }, + io::SeekFrom, + libs::elf::ELF_LOADER, + mm::{ + ucontext::{AddressSpace, UserStack}, + VirtAddr, + }, + syscall::SystemError, +}; + +/// 系统支持的所有二进制文件加载器的列表 +const BINARY_LOADERS: [&'static dyn BinaryLoader; 1] = [&ELF_LOADER]; + +pub trait BinaryLoader: 'static + Debug { + /// 检查二进制文件是否为当前加载器支持的格式 + fn probe(self: &'static Self, param: &ExecParam, buf: &[u8]) -> Result<(), ExecError>; + + fn load( + self: &'static Self, + param: &mut ExecParam, + head_buf: &[u8], + ) -> Result; +} + +/// 二进制文件加载结果 +#[derive(Debug)] +pub struct BinaryLoaderResult { + /// 程序入口地址 + entry_point: VirtAddr, +} + +impl BinaryLoaderResult { + pub fn new(entry_point: VirtAddr) -> Self { + Self { entry_point } + } + + pub fn entry_point(&self) -> VirtAddr { + self.entry_point + } +} + +#[allow(dead_code)] +#[derive(Debug)] +pub enum ExecError { + /// 二进制文件不可执行 + NotExecutable, + /// 二进制文件不是当前架构的 + WrongArchitecture, + /// 访问权限不足 + PermissionDenied, + /// 不支持的操作 + NotSupported, + /// 解析文件本身的时候出现错误(比如一些字段本身不合法) + ParseError, + /// 内存不足 + OutOfMemory, + /// 参数错误 + InvalidParemeter, + /// 无效的地址 + BadAddress(Option), + Other(String), +} + +impl Into for ExecError { + fn into(self) -> SystemError { + match self { + ExecError::NotExecutable => SystemError::ENOEXEC, + ExecError::WrongArchitecture => SystemError::EOPNOTSUPP_OR_ENOTSUP, + ExecError::PermissionDenied => SystemError::EACCES, + ExecError::NotSupported => SystemError::EOPNOTSUPP_OR_ENOTSUP, + ExecError::ParseError => SystemError::ENOEXEC, + ExecError::OutOfMemory => SystemError::ENOMEM, + ExecError::InvalidParemeter => SystemError::EINVAL, + ExecError::BadAddress(_addr) => SystemError::EFAULT, + ExecError::Other(_msg) => SystemError::ENOEXEC, + } + } +} + +bitflags! { + pub struct ExecParamFlags: u32 { + // 是否以可执行文件的形式加载 + const EXEC = 1 << 0; + } +} + +#[derive(Debug)] +pub struct ExecParam<'a> { + file_path: &'a str, + file: Option, + vm: Arc, + /// 一些标志位 + flags: ExecParamFlags, + /// 用来初始化进程的一些信息。这些信息由二进制加载器和exec机制来共同填充 + init_info: ProcInitInfo, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum ExecLoadMode { + /// 以可执行文件的形式加载 + Exec, + /// 以动态链接库的形式加载 + DSO, +} + +#[allow(dead_code)] +impl<'a> ExecParam<'a> { + pub fn new(file_path: &'a str, vm: Arc, flags: ExecParamFlags) -> Self { + Self { + file_path, + file: None, + vm, + flags, + init_info: ProcInitInfo::new(), + } + } + + pub fn file_path(&self) -> &'a str { + self.file_path + } + + pub fn vm(&self) -> &Arc { + &self.vm + } + + pub fn flags(&self) -> &ExecParamFlags { + &self.flags + } + + pub fn init_info(&self) -> &ProcInitInfo { + &self.init_info + } + + pub fn init_info_mut(&mut self) -> &mut ProcInitInfo { + &mut self.init_info + } + + /// 获取加载模式 + pub fn load_mode(&self) -> ExecLoadMode { + if self.flags.contains(ExecParamFlags::EXEC) { + ExecLoadMode::Exec + } else { + ExecLoadMode::DSO + } + } + + pub fn file_mut(&mut self) -> &mut File { + self.file.as_mut().unwrap() + } +} + +/// ## 加载二进制文件 +pub fn load_binary_file(param: &mut ExecParam) -> Result { + let inode = ROOT_INODE().lookup(param.file_path)?; + + // 读取文件头部,用于判断文件类型 + let file = File::new(inode, FileMode::O_RDONLY)?; + param.file = Some(file); + let mut head_buf = [0u8; 512]; + param.file_mut().lseek(SeekFrom::SeekSet(0))?; + let _bytes = param.file_mut().read(512, &mut head_buf)?; + // kdebug!("load_binary_file: read {} bytes", _bytes); + + let mut loader = None; + for bl in BINARY_LOADERS.iter() { + let probe_result = bl.probe(param, &head_buf); + if probe_result.is_ok() { + loader = Some(bl); + break; + } + } + // kdebug!("load_binary_file: loader: {:?}", loader); + if loader.is_none() { + return Err(SystemError::ENOEXEC); + } + + let loader: &&dyn BinaryLoader = loader.unwrap(); + assert!(param.vm().is_current()); + // kdebug!("load_binary_file: to load with param: {:?}", param); + + let result: BinaryLoaderResult = loader + .load(param, &head_buf) + .unwrap_or_else(|e| panic!("load_binary_file failed: error: {e:?}, param: {param:?}")); + + // kdebug!("load_binary_file: load success"); + return Ok(result); +} + +/// 程序初始化信息,这些信息会被压入用户栈中 +#[derive(Debug)] +pub struct ProcInitInfo { + pub args: Vec, + pub envs: Vec, + pub auxv: BTreeMap, +} + +impl ProcInitInfo { + pub fn new() -> Self { + Self { + args: Vec::new(), + envs: Vec::new(), + auxv: BTreeMap::new(), + } + } + + /// 把程序初始化信息压入用户栈中 + /// 这个函数会把参数、环境变量、auxv等信息压入用户栈中 + /// + /// ## 返回值 + /// + /// 返回值是一个元组,第一个元素是最终的用户栈顶地址,第二个元素是环境变量pointer数组的起始地址 + pub unsafe fn push_at( + &self, + ustack: &mut UserStack, + ) -> Result<(VirtAddr, VirtAddr), SystemError> { + // 先把程序的名称压入栈中 + self.push_str(ustack, self.args[0].as_str())?; + + // 然后把环境变量压入栈中 + let envps = self + .envs + .iter() + .map(|s| { + self.push_str(ustack, s.as_str()).expect("push_str failed"); + ustack.sp() + }) + .collect::>(); + + // 然后把参数压入栈中 + let argps = self + .args + .iter() + .map(|s| { + self.push_str(ustack, s.as_str()).expect("push_str failed"); + ustack.sp() + }) + .collect::>(); + + // 压入auxv + self.push_slice(ustack, &[null::(), null::()])?; + for (&k, &v) in self.auxv.iter() { + self.push_slice(ustack, &[k as usize, v])?; + } + + // 把环境变量指针压入栈中 + self.push_slice(ustack, &[null::()])?; + self.push_slice(ustack, envps.as_slice())?; + + // 把参数指针压入栈中 + self.push_slice(ustack, &[null::()])?; + self.push_slice(ustack, argps.as_slice())?; + + let argv_ptr = ustack.sp(); + + // 把argc压入栈中 + self.push_slice(ustack, &[self.args.len()])?; + + return Ok((ustack.sp(), argv_ptr)); + } + + fn push_slice(&self, ustack: &mut UserStack, slice: &[T]) -> Result<(), SystemError> { + let mut sp = ustack.sp(); + sp -= slice.len() * core::mem::size_of::(); + sp -= sp.data() % core::mem::align_of::(); + + unsafe { core::slice::from_raw_parts_mut(sp.data() as *mut T, slice.len()) } + .copy_from_slice(slice); + unsafe { + ustack.set_sp(sp); + } + + return Ok(()); + } + + fn push_str(&self, ustack: &mut UserStack, s: &str) -> Result<(), SystemError> { + self.push_slice(ustack, &[b'\0'])?; + self.push_slice(ustack, s.as_bytes())?; + return Ok(()); + } +} diff --git a/kernel/src/process/fork.c b/kernel/src/process/fork.c index 01147d37..2fcf8b4d 100644 --- a/kernel/src/process/fork.c +++ b/kernel/src/process/fork.c @@ -10,6 +10,7 @@ extern void kernel_thread_func(void); extern uint64_t rs_procfs_register_pid(uint64_t); extern uint64_t rs_procfs_unregister_pid(uint64_t); extern void *rs_dup_fpstate(); +extern uint64_t rs_process_copy_mm(bool clone_vm, struct process_control_block *new_pcb); extern int process_copy_files(uint64_t clone_flags, struct process_control_block *pcb); int process_copy_flags(uint64_t clone_flags, struct process_control_block *pcb); @@ -137,7 +138,7 @@ unsigned long do_fork(struct pt_regs *regs, unsigned long clone_flags, unsigned // 创建对应procfs文件 rs_procfs_register_pid(tsk->pid); - + // kdebug("Fork ok. pid: %d\n", tsk->pid); // 唤醒进程 process_wakeup(tsk); @@ -185,88 +186,9 @@ int process_copy_flags(uint64_t clone_flags, struct process_control_block *pcb) */ int process_copy_mm(uint64_t clone_flags, struct process_control_block *pcb) { - int retval = 0; - // 与父进程共享内存空间 - if (clone_flags & CLONE_VM) - { - pcb->mm = current_pcb->mm; - - return retval; - } - - // 分配新的内存空间分布结构体 - struct mm_struct *new_mms = (struct mm_struct *)kmalloc(sizeof(struct mm_struct), 0); - memset(new_mms, 0, sizeof(struct mm_struct)); - - memcpy(new_mms, current_pcb->mm, sizeof(struct mm_struct)); - new_mms->vmas = NULL; - pcb->mm = new_mms; - - // 分配顶层页表, 并设置顶层页表的物理地址 - new_mms->pgd = (pml4t_t *)virt_2_phys(kmalloc(PAGE_4K_SIZE, 0)); - // 由于高2K部分为内核空间,在接下来需要覆盖其数据,因此不用清零 - memset(phys_2_virt(new_mms->pgd), 0, PAGE_4K_SIZE / 2); - - // 拷贝内核空间的页表指针 - memcpy(phys_2_virt(new_mms->pgd) + 256, phys_2_virt(initial_proc[proc_current_cpu_id]->mm->pgd) + 256, - PAGE_4K_SIZE / 2); - - uint64_t *current_pgd = (uint64_t *)phys_2_virt(current_pcb->mm->pgd); - - uint64_t *new_pml4t = (uint64_t *)phys_2_virt(new_mms->pgd); - - // 拷贝用户空间的vma - struct vm_area_struct *vma = current_pcb->mm->vmas; - while (vma != NULL) - { - if (vma->vm_end > USER_MAX_LINEAR_ADDR || vma->vm_flags & VM_DONTCOPY) - { - vma = vma->vm_next; - continue; - } - - int64_t vma_size = vma->vm_end - vma->vm_start; - // kdebug("vma_size=%ld, vm_start=%#018lx", vma_size, vma->vm_start); - if (vma_size > PAGE_2M_SIZE / 2) - { - int page_to_alloc = (PAGE_2M_ALIGN(vma_size)) >> PAGE_2M_SHIFT; - for (int i = 0; i < page_to_alloc; ++i) - { - uint64_t pa = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys; - - struct vm_area_struct *new_vma = NULL; - int ret = mm_create_vma(new_mms, vma->vm_start + i * PAGE_2M_SIZE, PAGE_2M_SIZE, vma->vm_flags, - vma->vm_ops, &new_vma); - // 防止内存泄露 - if (unlikely(ret == -EEXIST)) - free_pages(Phy_to_2M_Page(pa), 1); - else - mm_map_vma(new_vma, pa, 0, PAGE_2M_SIZE); - - memcpy((void *)phys_2_virt(pa), (void *)(vma->vm_start + i * PAGE_2M_SIZE), - (vma_size >= PAGE_2M_SIZE) ? PAGE_2M_SIZE : vma_size); - vma_size -= PAGE_2M_SIZE; - } - } - else - { - uint64_t map_size = PAGE_4K_ALIGN(vma_size); - uint64_t va = (uint64_t)kmalloc(map_size, 0); - - struct vm_area_struct *new_vma = NULL; - int ret = mm_create_vma(new_mms, vma->vm_start, map_size, vma->vm_flags, vma->vm_ops, &new_vma); - // 防止内存泄露 - if (unlikely(ret == -EEXIST)) - kfree((void *)va); - else - mm_map_vma(new_vma, virt_2_phys(va), 0, map_size); - - memcpy((void *)va, (void *)vma->vm_start, vma_size); - } - vma = vma->vm_next; - } - - return retval; + pcb->address_space = NULL; + bool clone_vm = (clone_flags & CLONE_VM); + return (int)rs_process_copy_mm(clone_vm, pcb); } /** diff --git a/kernel/src/process/fork.rs b/kernel/src/process/fork.rs index 80b40d9a..09e5f14f 100644 --- a/kernel/src/process/fork.rs +++ b/kernel/src/process/fork.rs @@ -44,7 +44,7 @@ pub extern "C" fn process_copy_sighand(clone_flags: u64, pcb: *mut process_contr // kdebug!("DEFAULT_SIGACTION.sa_flags={}", DEFAULT_SIGACTION.sa_flags); // 拷贝sigaction - let mut flags: u64 = 0; + let mut flags: usize = 0; spin_lock_irqsave(unsafe { &mut (*current_pcb().sighand).siglock }, &mut flags); compiler_fence(core::sync::atomic::Ordering::SeqCst); @@ -64,7 +64,7 @@ pub extern "C" fn process_copy_sighand(clone_flags: u64, pcb: *mut process_contr } compiler_fence(core::sync::atomic::Ordering::SeqCst); - spin_unlock_irqrestore(unsafe { &mut (*current_pcb().sighand).siglock }, &flags); + spin_unlock_irqrestore(unsafe { &mut (*current_pcb().sighand).siglock }, flags); compiler_fence(core::sync::atomic::Ordering::SeqCst); // 将信号的处理函数设置为default(除了那些被手动屏蔽的) @@ -131,3 +131,39 @@ pub extern "C" fn process_exit_sighand(pcb: *mut process_control_block) { (*pcb).signal = 0 as *mut crate::include::bindings::bindings::signal_struct; } } + +/// 拷贝进程的地址空间 +/// +/// ## 参数 +/// +/// - `clone_vm`: 是否与父进程共享地址空间。true表示共享 +/// - `new_pcb`: 新进程的pcb +/// +/// ## 返回值 +/// +/// - 成功:返回Ok(()) +/// - 失败:返回Err(SystemError) +/// +/// ## Panic +/// +/// - 如果当前进程没有用户地址空间,则panic +pub fn copy_mm(clone_vm: bool, new_pcb: &mut process_control_block) -> Result<(), SystemError> { + // kdebug!("copy_mm, clone_vm: {}", clone_vm); + let old_address_space = current_pcb() + .address_space() + .expect("copy_mm: Failed to get address space of current process."); + + if clone_vm { + unsafe { new_pcb.set_address_space(old_address_space) }; + return Ok(()); + } + + let new_address_space = old_address_space.write().try_clone().unwrap_or_else(|e| { + panic!( + "copy_mm: Failed to clone address space of current process, current pid: [{}], new pid: [{}]. Error: {:?}", + current_pcb().pid, new_pcb.pid, e + ) + }); + unsafe { new_pcb.set_address_space(new_address_space) }; + return Ok(()); +} diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 6a5a9707..342dc471 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -1,6 +1,38 @@ +use core::{ + ptr::null_mut, + sync::atomic::{compiler_fence, Ordering}, +}; + +use crate::{ + arch::asm::current::current_pcb, + kdebug, + mm::{ + set_INITIAL_PROCESS_ADDRESS_SPACE, ucontext::AddressSpace, INITIAL_PROCESS_ADDRESS_SPACE, + }, +}; + +pub mod abi; +pub mod c_adapter; +pub mod exec; pub mod fork; pub mod initial_proc; pub mod pid; pub mod preempt; pub mod process; pub mod syscall; + +pub fn process_init() { + unsafe { + compiler_fence(Ordering::SeqCst); + current_pcb().address_space = null_mut(); + kdebug!("To create address space for INIT process."); + // test_buddy(); + set_INITIAL_PROCESS_ADDRESS_SPACE( + AddressSpace::new(true).expect("Failed to create address space for INIT process."), + ); + kdebug!("INIT process address space created."); + compiler_fence(Ordering::SeqCst); + current_pcb().set_address_space(INITIAL_PROCESS_ADDRESS_SPACE()); + compiler_fence(Ordering::SeqCst); + }; +} diff --git a/kernel/src/process/proc-types.h b/kernel/src/process/proc-types.h index 712806ca..9bb97f5d 100644 --- a/kernel/src/process/proc-types.h +++ b/kernel/src/process/proc-types.h @@ -61,14 +61,14 @@ struct thread_struct // ========= pcb->flags ========= // 进程标志位 -#define PF_KTHREAD (1UL << 0) // 内核线程 -#define PF_NEED_SCHED (1UL << 1) // 进程需要被调度 -#define PF_VFORK (1UL << 2) // 标志进程是否由于vfork而存在资源共享 -#define PF_KFORK (1UL << 3) // 标志在内核态下调用fork(临时标记,do_fork()结束后会将其复位) -#define PF_NOFREEZE (1UL << 4) // 当前进程不能被冻结 -#define PF_EXITING (1UL << 5) // 进程正在退出 -#define PF_WAKEKILL (1UL << 6) // 进程由于接收到终止信号唤醒 -#define PF_SIGNALED (1UL << 7) // 进程由于接收到信号而退出 +#define PF_KTHREAD (1UL << 0) // 内核线程 +#define PF_NEED_SCHED (1UL << 1) // 进程需要被调度 +#define PF_VFORK (1UL << 2) // 标志进程是否由于vfork而存在资源共享 +#define PF_KFORK (1UL << 3) // 标志在内核态下调用fork(临时标记,do_fork()结束后会将其复位) +#define PF_NOFREEZE (1UL << 4) // 当前进程不能被冻结 +#define PF_EXITING (1UL << 5) // 进程正在退出 +#define PF_WAKEKILL (1UL << 6) // 进程由于接收到终止信号唤醒 +#define PF_SIGNALED (1UL << 7) // 进程由于接收到信号而退出 #define PF_NEED_MIGRATE (1UL << 8) // 进程需要迁移到其他的核心 /** @@ -87,9 +87,6 @@ struct process_control_block // pcb的名字 char name[PCB_NAME_LEN]; - // 内存空间分布结构体, 记录内存页表和程序段信息 - struct mm_struct *mm; - // 进程切换时保存的状态信息 struct thread_struct *thread; @@ -111,7 +108,7 @@ struct process_control_block int64_t rt_time_slice; // 由实时调度器管理的时间片 // 进程拥有的文件描述符的指针数组(由Rust进行管理) - void * fds; + void *fds; // 链表中的下一个pcb struct process_control_block *prev_pcb, *next_pcb; @@ -136,11 +133,15 @@ struct process_control_block // 如果当前进程等待被迁移到另一个cpu核心上(也就是flags中的PF_NEED_MIGRATE被置位), // 该字段存储要被迁移到的目标处理器核心号 uint32_t migrate_to; - void* fp_state;//Fpstate 用于用户态切换到内核态时保存浮点寄存器里面的值 + // Fpstate 用于用户态切换到内核态时保存浮点寄存器里面的值 + void *fp_state; + // 指向进程的地址空间的arc指针. + void *address_space; }; // 将进程的pcb和内核栈融合到一起,8字节对齐 -union proc_union { +union proc_union +{ struct process_control_block pcb; ul stack[STACK_SIZE / sizeof(ul)]; } __attribute__((aligned(8))); diff --git a/kernel/src/process/process.c b/kernel/src/process/process.c index 9d4b2b90..6dd9840e 100644 --- a/kernel/src/process/process.c +++ b/kernel/src/process/process.c @@ -38,7 +38,6 @@ extern void kernel_thread_func(void); extern void rs_procfs_unregister_pid(uint64_t); ul _stack_start; // initial proc的栈基地址(虚拟地址) -extern struct mm_struct initial_mm; extern struct signal_struct INITIAL_SIGNALS; extern struct sighand_struct INITIAL_SIGHAND; @@ -46,17 +45,20 @@ extern void process_exit_sighand(struct process_control_block *pcb); extern void process_exit_signal(struct process_control_block *pcb); extern void initial_proc_init_signal(struct process_control_block *pcb); extern void rs_process_exit_fpstate(struct process_control_block *pcb); +extern void rs_drop_address_space(struct process_control_block *pcb); extern int process_init_files(); extern int rs_init_stdio(); +extern uint64_t rs_do_execve(const char *filename, const char *const argv[], const char *const envp[], struct pt_regs *regs); +extern uint64_t rs_exec_init_process(struct pt_regs *regs); // 设置初始进程的PCB -#define INITIAL_PROC(proc) \ - { \ - .state = PROC_UNINTERRUPTIBLE, .flags = PF_KTHREAD, .preempt_count = 0, .signal = 0, .cpu_id = 0, \ - .mm = &initial_mm, .thread = &initial_thread, .addr_limit = 0xffffffffffffffff, .pid = 0, .priority = 2, \ - .virtual_runtime = 0, .fds = {0}, .next_pcb = &proc, .prev_pcb = &proc, .parent_pcb = &proc, .exit_code = 0, \ - .wait_child_proc_exit = 0, .worker_private = NULL, .policy = SCHED_NORMAL, .sig_blocked = 0, \ - .signal = &INITIAL_SIGNALS, .sighand = &INITIAL_SIGHAND, \ +#define INITIAL_PROC(proc) \ + { \ + .state = PROC_UNINTERRUPTIBLE, .flags = PF_KTHREAD, .preempt_count = 0, .signal = 0, .cpu_id = 0, \ + .thread = &initial_thread, .addr_limit = 0xffffffffffffffff, .pid = 0, .priority = 2, \ + .virtual_runtime = 0, .fds = {0}, .next_pcb = &proc, .prev_pcb = &proc, .parent_pcb = &proc, .exit_code = 0, \ + .wait_child_proc_exit = 0, .worker_private = NULL, .policy = SCHED_NORMAL, .sig_blocked = 0, \ + .signal = &INITIAL_SIGNALS, .sighand = &INITIAL_SIGHAND, .address_space = NULL \ } struct thread_struct initial_thread = { @@ -113,8 +115,10 @@ void __switch_to(struct process_control_block *prev, struct process_control_bloc // initial_tss[0].ist2, initial_tss[0].ist3, initial_tss[0].ist4, initial_tss[0].ist5, // initial_tss[0].ist6, initial_tss[0].ist7); - __asm__ __volatile__("movq %%fs, %0 \n\t" : "=a"(prev->thread->fs)); - __asm__ __volatile__("movq %%gs, %0 \n\t" : "=a"(prev->thread->gs)); + __asm__ __volatile__("movq %%fs, %0 \n\t" + : "=a"(prev->thread->fs)); + __asm__ __volatile__("movq %%gs, %0 \n\t" + : "=a"(prev->thread->gs)); __asm__ __volatile__("movq %0, %%fs \n\t" ::"a"(next->thread->fs)); __asm__ __volatile__("movq %0, %%gs \n\t" ::"a"(next->thread->gs)); @@ -145,359 +149,6 @@ int process_open_exec_file(char *path) return fd; } -/** - * @brief 加载elf格式的程序文件到内存中,并设置regs - * - * @param regs 寄存器 - * @param path 文件路径 - * @return int - */ -static int process_load_elf_file(struct pt_regs *regs, char *path) -{ - int retval = 0; - int fd = process_open_exec_file(path); - - if ((long)fd < 0) - { - kdebug("(long)fd=%ld", (long)fd); - return (unsigned long)fd; - } - - void *buf = kzalloc(PAGE_4K_SIZE, 0); - uint64_t pos = 0; - - retval = enter_syscall_int(SYS_LSEEK, fd, 0, SEEK_SET, 0, 0, 0, 0, 0); - - // 读取 Elf64_Ehdr - retval = enter_syscall_int(SYS_READ, fd, (uint64_t)buf, sizeof(Elf64_Ehdr), 0, 0, 0, 0, 0); - - pos = enter_syscall_int(SYS_LSEEK, fd, 0, SEEK_CUR, 0, 0, 0, 0, 0); - - if (retval != sizeof(Elf64_Ehdr)) - { - kerror("retval=%d, not equal to sizeof(Elf64_Ehdr):%d", retval, sizeof(Elf64_Ehdr)); - } - retval = 0; - if (!elf_check(buf)) - { - kerror("Not an ELF file: %s", path); - retval = -ENOTSUP; - goto load_elf_failed; - } - -#if ARCH(X86_64) - // 暂时只支持64位的文件 - if (((Elf32_Ehdr *)buf)->e_ident[EI_CLASS] != ELFCLASS64) - { - kdebug("((Elf32_Ehdr *)buf)->e_ident[EI_CLASS]=%d", ((Elf32_Ehdr *)buf)->e_ident[EI_CLASS]); - retval = -EUNSUPPORTED; - goto load_elf_failed; - } - Elf64_Ehdr ehdr = *(Elf64_Ehdr *)buf; - // 暂时只支持AMD64架构 - if (ehdr.e_machine != EM_AMD64) - { - kerror("e_machine=%d", ehdr.e_machine); - retval = -EUNSUPPORTED; - goto load_elf_failed; - } -#else -#error Unsupported architecture! -#endif - if (ehdr.e_type != ET_EXEC) - { - kerror("Not executable file! filename=%s\tehdr->e_type=%d", path, ehdr.e_type); - retval = -EUNSUPPORTED; - goto load_elf_failed; - } - // kdebug("filename=%s:\te_entry=%#018lx", path, ehdr.e_entry); - regs->rip = ehdr.e_entry; - current_pcb->mm->code_addr_start = ehdr.e_entry; - - // kdebug("ehdr.e_phoff=%#018lx\t ehdr.e_phentsize=%d, ehdr.e_phnum=%d", ehdr.e_phoff, ehdr.e_phentsize, - // ehdr.e_phnum); 将指针移动到program header处 - - // 读取所有的phdr - pos = ehdr.e_phoff; - - pos = enter_syscall_int(SYS_LSEEK, fd, pos, SEEK_SET, 0, 0, 0, 0, 0); - - memset(buf, 0, PAGE_4K_SIZE); - - enter_syscall_int(SYS_READ, fd, (uint64_t)buf, (uint64_t)ehdr.e_phentsize * (uint64_t)ehdr.e_phnum, 0, 0, 0, 0, 0); - - pos = enter_syscall_int(SYS_LSEEK, fd, 0, SEEK_CUR, 0, 0, 0, 0, 0); - - if ((long)retval < 0) - { - kdebug("(unsigned long)filp=%d", (long)retval); - retval = -ENOEXEC; - goto load_elf_failed; - } - - Elf64_Phdr *phdr = buf; - // 将程序加载到内存中 - for (int i = 0; i < ehdr.e_phnum; ++i, ++phdr) - { - // kdebug("phdr[%d] phdr->p_offset=%#018lx phdr->p_vaddr=%#018lx phdr->p_memsz=%ld phdr->p_filesz=%ld - // phdr->p_type=%d", i, phdr->p_offset, phdr->p_vaddr, phdr->p_memsz, phdr->p_filesz, phdr->p_type); - - // 不是可加载的段 - if (phdr->p_type != PT_LOAD) - continue; - - int64_t remain_mem_size = phdr->p_memsz; - int64_t remain_file_size = phdr->p_filesz; - pos = phdr->p_offset; - - uint64_t virt_base = 0; - uint64_t beginning_offset = 0; // 由于页表映射导致的virtbase与实际的p_vaddr之间的偏移量 - - if (remain_mem_size >= PAGE_2M_SIZE) // 接下来存在映射2M页的情况,因此将vaddr按2M向下对齐 - virt_base = phdr->p_vaddr & PAGE_2M_MASK; - else // 接下来只有4K页的映射 - virt_base = phdr->p_vaddr & PAGE_4K_MASK; - - beginning_offset = phdr->p_vaddr - virt_base; - remain_mem_size += beginning_offset; - - while (remain_mem_size > 0) - { - // kdebug("loading..."); - int64_t map_size = 0; - if (remain_mem_size >= PAGE_2M_SIZE) - { - uint64_t pa = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys; - struct vm_area_struct *vma = NULL; - int ret = - mm_create_vma(current_pcb->mm, virt_base, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); - - // 防止内存泄露 - if (ret == -EEXIST) - free_pages(Phy_to_2M_Page(pa), 1); - else - mm_map(current_pcb->mm, virt_base, PAGE_2M_SIZE, pa); - // mm_map_vma(vma, pa, 0, PAGE_2M_SIZE); - io_mfence(); - memset((void *)virt_base, 0, PAGE_2M_SIZE); - map_size = PAGE_2M_SIZE; - } - else - { - // todo: 使用4K、8K、32K大小内存块混合进行分配,提高空间利用率(减少了bmp的大小) - map_size = ALIGN(remain_mem_size, PAGE_4K_SIZE); - // 循环分配4K大小内存块 - for (uint64_t off = 0; off < map_size; off += PAGE_4K_SIZE) - { - uint64_t paddr = virt_2_phys((uint64_t)kmalloc(PAGE_4K_SIZE, 0)); - - struct vm_area_struct *vma = NULL; - int val = mm_create_vma(current_pcb->mm, virt_base + off, PAGE_4K_SIZE, VM_USER | VM_ACCESS_FLAGS, - NULL, &vma); - // kdebug("virt_base=%#018lx", virt_base + off); - if (val == -EEXIST) - kfree(phys_2_virt(paddr)); - else - mm_map(current_pcb->mm, virt_base + off, PAGE_4K_SIZE, paddr); - // mm_map_vma(vma, paddr, 0, PAGE_4K_SIZE); - io_mfence(); - memset((void *)(virt_base + off), 0, PAGE_4K_SIZE); - } - } - - pos = enter_syscall_int(SYS_LSEEK, fd, pos, SEEK_SET, 0, 0, 0, 0, 0); - - int64_t val = 0; - if (remain_file_size > 0) - { - int64_t to_trans = (remain_file_size > PAGE_2M_SIZE) ? PAGE_2M_SIZE : remain_file_size; - - void *buf3 = kzalloc(PAGE_4K_SIZE, 0); - while (to_trans > 0) - { - int64_t x = 0; - - x = enter_syscall_int(SYS_READ, fd, (uint64_t)buf3, to_trans, 0, 0, 0, 0, 0); - memcpy(virt_base + beginning_offset + val, buf3, x); - val += x; - to_trans -= x; - - pos = enter_syscall_int(SYS_LSEEK, fd, 0, SEEK_CUR, 0, 0, 0, 0, 0); - } - kfree(buf3); - - // kdebug("virt_base + beginning_offset=%#018lx, val=%d, to_trans=%d", virt_base + beginning_offset, - // val, - // to_trans); - // kdebug("to_trans=%d", to_trans); - } - - if (val < 0) - goto load_elf_failed; - - remain_mem_size -= map_size; - remain_file_size -= val; - virt_base += map_size; - } - } - - // 分配2MB的栈内存空间 - regs->rsp = current_pcb->mm->stack_start; - regs->rbp = current_pcb->mm->stack_start; - - { - struct vm_area_struct *vma = NULL; - uint64_t pa = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys; - int val = mm_create_vma(current_pcb->mm, current_pcb->mm->stack_start - PAGE_2M_SIZE, PAGE_2M_SIZE, - VM_USER | VM_ACCESS_FLAGS, NULL, &vma); - if (val == -EEXIST) - free_pages(Phy_to_2M_Page(pa), 1); - else - mm_map_vma(vma, pa, 0, PAGE_2M_SIZE); - } - - // 清空栈空间 - memset((void *)(current_pcb->mm->stack_start - PAGE_2M_SIZE), 0, PAGE_2M_SIZE); - -load_elf_failed:; - { - enter_syscall_int(SYS_CLOSE, fd, 0, 0, 0, 0, 0, 0, 0); - } - - if (buf != NULL) - kfree(buf); - return retval; -} -/** - * @brief 使当前进程去执行新的代码 - * - * @param regs 当前进程的寄存器 - * @param path 可执行程序的路径 - * @param argv 参数列表 - * @param envp 环境变量 - * @return ul 错误码 - */ -#pragma GCC push_options -#pragma GCC optimize("O0") -ul do_execve(struct pt_regs *regs, char *path, char *argv[], char *envp[]) -{ - - // 当前进程正在与父进程共享地址空间,需要创建 - // 独立的地址空间才能使新程序正常运行 - if (current_pcb->flags & PF_VFORK) - { - // kdebug("proc:%d creating new mem space", current_pcb->pid); - // 分配新的内存空间分布结构体 - struct mm_struct *new_mms = (struct mm_struct *)kmalloc(sizeof(struct mm_struct), 0); - memset(new_mms, 0, sizeof(struct mm_struct)); - current_pcb->mm = new_mms; - - // 分配顶层页表, 并设置顶层页表的物理地址 - new_mms->pgd = (pml4t_t *)virt_2_phys(kmalloc(PAGE_4K_SIZE, 0)); - - // 由于高2K部分为内核空间,在接下来需要覆盖其数据,因此不用清零 - memset(phys_2_virt(new_mms->pgd), 0, PAGE_4K_SIZE / 2); - - // 拷贝内核空间的页表指针 - memcpy(phys_2_virt(new_mms->pgd) + 256, phys_2_virt(initial_proc[proc_current_cpu_id]) + 256, PAGE_4K_SIZE / 2); - } - - // 设置用户栈和用户堆的基地址 - unsigned long stack_start_addr = 0x6ffff0a00000UL; - const uint64_t brk_start_addr = 0x700000000000UL; - - process_switch_mm(current_pcb); - - // 为用户态程序设置地址边界 - if (!(current_pcb->flags & PF_KTHREAD)) - current_pcb->addr_limit = USER_MAX_LINEAR_ADDR; - - current_pcb->mm->code_addr_end = 0; - current_pcb->mm->data_addr_start = 0; - current_pcb->mm->data_addr_end = 0; - current_pcb->mm->rodata_addr_start = 0; - current_pcb->mm->rodata_addr_end = 0; - current_pcb->mm->bss_start = 0; - current_pcb->mm->bss_end = 0; - current_pcb->mm->brk_start = brk_start_addr; - current_pcb->mm->brk_end = brk_start_addr; - current_pcb->mm->stack_start = stack_start_addr; - - // 清除进程的vfork标志位 - current_pcb->flags &= ~PF_VFORK; - - // 加载elf格式的可执行文件 - int tmp = process_load_elf_file(regs, path); - - if (tmp < 0) - goto exec_failed; - - int argc = 0; - char **dst_argv = NULL; - // kdebug("stack_start_addr=%#018lx", stack_start_addr); - // 拷贝参数列表 - if (argv != NULL) - { - - // 目标程序的argv基地址指针,最大8个参数 - dst_argv = (char **)(stack_start_addr - (sizeof(char **) << 3)); - uint64_t str_addr = (uint64_t)dst_argv; - - for (argc = 0; argc < 8 && argv[argc] != NULL; ++argc) - { - - if (*argv[argc] == NULL) - break; - - // 测量参数的长度(最大1023) - int argv_len = strnlen_user(argv[argc], 1023) + 1; - strncpy((char *)(str_addr - argv_len), argv[argc], argv_len - 1); - str_addr -= argv_len; - dst_argv[argc] = (char *)str_addr; - // 字符串加上结尾字符 - ((char *)str_addr)[argv_len] = '\0'; - } - - // 重新设定栈基址,并预留空间防止越界 - - stack_start_addr = str_addr - 8; - } - - // kdebug("stack_start_addr=%#018lx", stack_start_addr); - // ==== 生成relibc所需的Stack结构体 - { - uint64_t *ptr_stack = (uint64_t *)(stack_start_addr - 8); - if (argc == 0) - *ptr_stack = 0; - else - *ptr_stack = (uint64_t)dst_argv; - ptr_stack--; - *ptr_stack = argc; - stack_start_addr -= 16; - } - - // 传递参数(旧版libc) - regs->rdi = argc; - regs->rsi = (uint64_t)dst_argv; - // 设置用户栈基地址 - current_pcb->mm->stack_start = stack_start_addr; - regs->rsp = regs->rbp = stack_start_addr; - // kdebug("execve ok"); - // 设置进程的段选择子为用户态可访问 - regs->cs = USER_CS | 3; - regs->ds = USER_DS | 3; - regs->ss = USER_DS | 0x3; - regs->rflags = 0x200246; - regs->rax = 1; - regs->es = 0; - - return 0; - -exec_failed:; - process_do_exit(tmp); -} -#pragma GCC pop_options - /** * @brief 初始化实时进程rt_pcb * @@ -527,43 +178,24 @@ ul initial_kernel_thread(ul arg) kinfo("initial proc running...\targ:%#018lx, vruntime=%d", arg, current_pcb->virtual_runtime); int val = 0; val = scm_enable_double_buffer(); - + io_mfence(); rs_init_stdio(); + io_mfence(); // block_io_scheduler_init(); ahci_init(); mount_root_fs(); + io_mfence(); rs_virtio_probe(); + io_mfence(); + // 使用单独的内核线程来初始化usb驱动程序 // 注释:由于目前usb驱动程序不完善,因此先将其注释掉 // int usb_pid = kernel_thread(usb_init, 0, 0); kinfo("LZ4 lib Version=%s", LZ4_versionString()); + io_mfence(); __rust_demo_func(); - // while (1) - // { - // /* code */ - // } - - // 对completion完成量进行测试 - // __test_completion(); - - // // 对一些组件进行单元测试 - uint64_t tpid[] = { - // ktest_start(ktest_test_bitree, 0), ktest_start(ktest_test_kfifo, 0), ktest_start(ktest_test_mutex, 0), - // ktest_start(ktest_test_idr, 0), - // usb_pid, - }; - - // kinfo("Waiting test thread exit..."); - // // 等待测试进程退出 - // for (int i = 0; i < sizeof(tpid) / sizeof(uint64_t); ++i) - // waitpid(tpid[i], NULL, NULL); - // kinfo("All test done."); - - // 测试实时进程 - - // struct process_control_block *test_rt1 = kthread_run_rt(&test, NULL, "test rt"); - // kdebug("process:rt test kthread is created!!!!"); + io_mfence(); // 准备切换到用户态 struct pt_regs *regs; @@ -588,8 +220,8 @@ ul initial_kernel_thread(ul arg) // 这里的设计思路和switch_to类似 加载用户态程序:shell.elf __asm__ __volatile__("movq %1, %%rsp \n\t" "pushq %2 \n\t" - "jmp do_execve \n\t" ::"D"(current_pcb->thread->rsp), - "m"(current_pcb->thread->rsp), "m"(current_pcb->thread->rip), "S"("/bin/shell.elf"), "c"(NULL), + "jmp rs_exec_init_process \n\t" ::"D"(current_pcb->thread->rsp), + "m"(current_pcb->thread->rsp), "m"(current_pcb->thread->rip), "c"(NULL), "d"(NULL) : "memory"); @@ -678,9 +310,15 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) * @brief 初始化进程模块 * ☆前置条件:已完成系统调用模块的初始化 */ +#pragma GCC push_options +#pragma GCC optimize("O0") void process_init() { kinfo("Initializing process..."); + // rs_test_buddy(); + io_mfence(); + rs_process_init(); + io_mfence(); initial_tss[proc_current_cpu_id].rsp0 = initial_thread.rbp; @@ -692,18 +330,23 @@ void process_init() list_init(&initial_proc_union.pcb.list); wait_queue_init(&initial_proc_union.pcb.wait_child_proc_exit, NULL); + io_mfence(); // 初始化init进程的signal相关的信息 initial_proc_init_signal(current_pcb); kdebug("Initial process to init files"); + io_mfence(); process_init_files(); kdebug("Initial process init files ok"); + io_mfence(); // 临时设置IDLE进程的的虚拟运行时间为0,防止下面的这些内核线程的虚拟运行时间出错 current_pcb->virtual_runtime = 0; + barrier(); kernel_thread(initial_kernel_thread, 10, CLONE_FS | CLONE_SIGNAL); // 初始化内核线程 barrier(); kthread_mechanism_init(); // 初始化kthread机制 + barrier(); initial_proc_union.pcb.state = PROC_RUNNING; initial_proc_union.pcb.preempt_count = 0; @@ -712,6 +355,7 @@ void process_init() // 将IDLE进程的虚拟运行时间设置为一个很大的数值 current_pcb->virtual_runtime = (1UL << 60); } +#pragma GCC pop_options /** * @brief 根据pid获取进程的pcb。存在对应的pcb时,返回对应的pcb的指针,否则返回NULL @@ -786,57 +430,7 @@ int process_wakeup_immediately(struct process_control_block *pcb) */ uint64_t process_exit_mm(struct process_control_block *pcb) { - if (pcb->flags & CLONE_VM) - return 0; - if (pcb->mm == NULL) - { - kdebug("pcb->mm==NULL"); - return 0; - } - if (pcb->mm->pgd == NULL) - { - kdebug("pcb->mm->pgd==NULL"); - return 0; - } - - // // 获取顶层页表 - pml4t_t *current_pgd = (pml4t_t *)phys_2_virt(pcb->mm->pgd); - - // 循环释放VMA中的内存 - struct vm_area_struct *vma = pcb->mm->vmas; - while (vma != NULL) - { - - struct vm_area_struct *cur_vma = vma; - vma = cur_vma->vm_next; - - uint64_t pa; - mm_unmap_vma(pcb->mm, cur_vma, &pa); - - uint64_t size = (cur_vma->vm_end - cur_vma->vm_start); - - // 释放内存 - switch (size) - { - case PAGE_4K_SIZE: - kfree(phys_2_virt(pa)); - break; - default: - break; - } - vm_area_del(cur_vma); - vm_area_free(cur_vma); - } - - // 释放顶层页表 - kfree(current_pgd); - if (unlikely(pcb->mm->vmas != NULL)) - { - kwarn("pcb.mm.vmas!=NULL"); - } - // 释放内存空间分布结构体 - kfree(pcb->mm); - + rs_drop_address_space(pcb); return 0; } @@ -857,9 +451,6 @@ void process_exit_thread(struct process_control_block *pcb) */ int process_release_pcb(struct process_control_block *pcb) { - // 释放子进程的页表 - // BUG 暂时注释process_exit_mm - // process_exit_mm(pcb); if ((pcb->flags & PF_KTHREAD)) // 释放内核线程的worker private结构体 free_kthread_struct(pcb); @@ -871,6 +462,8 @@ int process_release_pcb(struct process_control_block *pcb) process_exit_signal(pcb); rs_process_exit_fpstate(pcb); rs_procfs_unregister_pid(pcb->pid); + // 释放进程的地址空间 + process_exit_mm(pcb); // 释放当前pcb kfree(pcb); return 0; diff --git a/kernel/src/process/process.h b/kernel/src/process/process.h index 99a0652a..d09cc670 100644 --- a/kernel/src/process/process.h +++ b/kernel/src/process/process.h @@ -31,18 +31,18 @@ extern int process_exit_files(struct process_control_block *pcb); */ // 设置初始进程的tss -#define INITIAL_TSS \ - { \ - .reserved0 = 0, .rsp0 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), \ - .rsp1 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), \ - .rsp2 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), .reserved1 = 0, .ist1 = 0xffff800000007c00, \ - .ist2 = 0xffff800000007c00, .ist3 = 0xffff800000007c00, .ist4 = 0xffff800000007c00, \ - .ist5 = 0xffff800000007c00, .ist6 = 0xffff800000007c00, .ist7 = 0xffff800000007c00, .reserved2 = 0, \ - .reserved3 = 0, .io_map_base_addr = 0 \ +#define INITIAL_TSS \ + { \ + .reserved0 = 0, .rsp0 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), \ + .rsp1 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), \ + .rsp2 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), .reserved1 = 0, .ist1 = 0xffff800000007c00, \ + .ist2 = 0xffff800000007c00, .ist3 = 0xffff800000007c00, .ist4 = 0xffff800000007c00, \ + .ist5 = 0xffff800000007c00, .ist6 = 0xffff800000007c00, .ist7 = 0xffff800000007c00, .reserved2 = 0, \ + .reserved3 = 0, .io_map_base_addr = 0 \ } -#define GET_CURRENT_PCB \ - "movq %rsp, %rbx \n\t" \ +#define GET_CURRENT_PCB \ + "movq %rsp, %rbx \n\t" \ "andq $-32768, %rbx\n\t" /** @@ -51,23 +51,23 @@ extern int process_exit_files(struct process_control_block *pcb); * 然后调用__switch_to切换栈,配置其他信息,最后恢复下一个进程的rax rbp。 */ -#define switch_to(prev, next) \ - do \ - { \ - __asm__ __volatile__("pushq %%rbp \n\t" \ - "pushq %%rax \n\t" \ - "movq %%rsp, %0 \n\t" \ - "movq %2, %%rsp \n\t" \ - "leaq 2f(%%rip), %%rax \n\t" \ - "movq %%rax, %1 \n\t" \ - "pushq %3 \n\t" \ - "jmp __switch_to \n\t" \ - "2: \n\t" \ - "popq %%rax \n\t" \ - "popq %%rbp \n\t" \ - : "=m"(prev->thread->rsp), "=m"(prev->thread->rip) \ - : "m"(next->thread->rsp), "m"(next->thread->rip), "D"(prev), "S"(next) \ - : "memory", "rax"); \ +#define switch_to(prev, next) \ + do \ + { \ + __asm__ __volatile__("pushq %%rbp \n\t" \ + "pushq %%rax \n\t" \ + "movq %%rsp, %0 \n\t" \ + "movq %2, %%rsp \n\t" \ + "leaq 2f(%%rip), %%rax \n\t" \ + "movq %%rax, %1 \n\t" \ + "pushq %3 \n\t" \ + "jmp __switch_to \n\t" \ + "2: \n\t" \ + "popq %%rax \n\t" \ + "popq %%rbp \n\t" \ + : "=m"(prev->thread->rsp), "=m"(prev->thread->rip) \ + : "m"(next->thread->rsp), "m"(next->thread->rip), "D"(prev), "S"(next) \ + : "memory", "rax"); \ } while (0) /** @@ -112,17 +112,6 @@ int process_wakeup(struct process_control_block *pcb); */ int process_wakeup_immediately(struct process_control_block *pcb); -/** - * @brief 使当前进程去执行新的代码 - * - * @param regs 当前进程的寄存器 - * @param path 可执行程序的路径 - * @param argv 参数列表 - * @param envp 环境变量 - * @return ul 错误码 - */ -ul do_execve(struct pt_regs *regs, char *path, char *argv[], char *envp[]); - /** * @brief 释放进程的页表 * @@ -166,10 +155,11 @@ int process_release_pcb(struct process_control_block *pcb); * @param next 下一个进程的pcb * */ -#define process_switch_mm(next_pcb) \ - do \ - { \ - asm volatile("movq %0, %%cr3 \n\t" ::"r"(next_pcb->mm->pgd) : "memory"); \ +#define process_switch_mm(next_pcb) \ + do \ + { \ + asm volatile("movq %0, %%cr3 \n\t" ::"r"(next_pcb->mm->pgd) \ + : "memory"); \ } while (0) // flush_tlb(); diff --git a/kernel/src/process/process.rs b/kernel/src/process/process.rs index 4f718645..084ee52d 100644 --- a/kernel/src/process/process.rs +++ b/kernel/src/process/process.rs @@ -1,12 +1,13 @@ use core::{ ffi::c_void, + mem::ManuallyDrop, ptr::{null_mut, read_volatile, write_volatile}, }; use alloc::{boxed::Box, sync::Arc}; use crate::{ - arch::{asm::current::current_pcb, fpu::FpState}, + arch::asm::current::current_pcb, filesystem::vfs::{ file::{File, FileDescriptorVec, FileMode}, FileType, ROOT_INODE, @@ -16,6 +17,7 @@ use crate::{ PROC_UNINTERRUPTIBLE, }, libs::casting::DowncastArc, + mm::ucontext::AddressSpace, net::socket::SocketInode, sched::core::{cpu_executing, sched_enqueue}, smp::core::{smp_get_processor_id, smp_send_reschedule}, @@ -311,94 +313,41 @@ impl process_control_block { .expect("Not a socket inode"); return Some(socket); } -} -// =========== 导出到C的函数,在将来,进程管理模块被完全重构之后,需要删掉他们 BEGIN ============ - -/// @brief 初始化当前进程的文件描述符数组 -/// 请注意,如果当前进程已经有文件描述符数组,那么本操作将被禁止 -#[no_mangle] -pub extern "C" fn process_init_files() -> i32 { - let r = current_pcb().init_files(); - if r.is_ok() { - return 0; - } else { - return r.unwrap_err().to_posix_errno(); - } -} - -/// @brief 拷贝当前进程的文件描述符信息 -/// -/// @param clone_flags 克隆标志位 -/// @param pcb 新的进程的pcb -#[no_mangle] -pub extern "C" fn process_copy_files( - clone_flags: u64, - from: &'static process_control_block, -) -> i32 { - let r = current_pcb().copy_files(clone_flags, from); - if r.is_ok() { - return 0; - } else { - return r.unwrap_err().to_posix_errno(); - } -} - -/// @brief 回收进程的文件描述符数组 -/// -/// @param pcb 要被回收的进程的pcb -/// -/// @return i32 -#[no_mangle] -pub extern "C" fn process_exit_files(pcb: &'static mut process_control_block) -> i32 { - let r: Result<(), SystemError> = pcb.exit_files(); - if r.is_ok() { - return 0; - } else { - return r.unwrap_err().to_posix_errno(); - } -} - -/// @brief 复制当前进程的浮点状态 -#[allow(dead_code)] -#[no_mangle] -pub extern "C" fn rs_dup_fpstate() -> *mut c_void { - // 如果当前进程没有浮点状态,那么就返回一个默认的浮点状态 - if current_pcb().fp_state == null_mut() { - return Box::leak(Box::new(FpState::default())) as *mut FpState as usize as *mut c_void; - } else { - // 如果当前进程有浮点状态,那么就复制一个新的浮点状态 - let state = current_pcb().fp_state as usize as *mut FpState; - unsafe { - let s = state.as_ref().unwrap(); - let state: &mut FpState = Box::leak(Box::new(s.clone())); - - return state as *mut FpState as usize as *mut c_void; + /// 释放pcb中存储的地址空间的指针 + pub unsafe fn drop_address_space(&mut self) { + let p = self.address_space as *const AddressSpace; + if p.is_null() { + return; } + let p: Arc = Arc::from_raw(p); + drop(p); + self.address_space = null_mut(); } -} -/// @brief 释放进程的浮点状态所占用的内存 -#[no_mangle] -pub extern "C" fn rs_process_exit_fpstate(pcb: &'static mut process_control_block) { - if pcb.fp_state != null_mut() { - let state = pcb.fp_state as usize as *mut FpState; - unsafe { - drop(Box::from_raw(state)); + /// 设置pcb中存储的地址空间的指针 + /// + /// ## panic + /// 如果当前pcb已经有地址空间,那么panic + pub unsafe fn set_address_space(&mut self, address_space: Arc) { + assert!(self.address_space.is_null(), "Address space already set"); + self.address_space = Arc::into_raw(address_space) as *mut c_void; + } + + /// 获取当前进程的地址空间的指针 + pub fn address_space(&self) -> Option> { + let ptr = self.address_space as *const AddressSpace; + if ptr.is_null() { + return None; } + // 为了防止pcb中的指针被释放,这里需要将其包装一下,使得Arc的drop不会被调用 + let arc_wrapper = ManuallyDrop::new(unsafe { Arc::from_raw(ptr) }); + + let result = Arc::clone(&arc_wrapper); + return Some(result); } } -#[no_mangle] -pub extern "C" fn rs_init_stdio() -> i32 { - let r = init_stdio(); - if r.is_ok() { - return 0; - } else { - return r.unwrap_err().to_posix_errno(); - } -} -// =========== 以上为导出到C的函数,在将来,进程管理模块被完全重构之后,需要删掉他们 END ============ /// @brief 初始化pid=1的进程的stdio pub fn init_stdio() -> Result<(), SystemError> { diff --git a/kernel/src/sched/cfs.rs b/kernel/src/sched/cfs.rs index 9731e983..78d51ea0 100644 --- a/kernel/src/sched/cfs.rs +++ b/kernel/src/sched/cfs.rs @@ -58,24 +58,24 @@ impl CFSQueue { /// @brief 将pcb加入队列 pub fn enqueue(&mut self, pcb: &'static mut process_control_block) { - let mut rflags = 0u64; + let mut rflags = 0usize; self.lock.lock_irqsave(&mut rflags); // 如果进程是IDLE进程,那么就不加入队列 if pcb.pid == 0 { - self.lock.unlock_irqrestore(&rflags); + self.lock.unlock_irqrestore(rflags); return; } self.queue.insert(pcb.virtual_runtime, pcb); - self.lock.unlock_irqrestore(&rflags); + self.lock.unlock_irqrestore(rflags); } /// @brief 将pcb从调度队列中弹出,若队列为空,则返回IDLE进程的pcb pub fn dequeue(&mut self) -> &'static mut process_control_block { let res: &'static mut process_control_block; - let mut rflags = 0u64; + let mut rflags = 0usize; self.lock.lock_irqsave(&mut rflags); if !self.queue.is_empty() { // 队列不为空,返回下一个要执行的pcb @@ -84,7 +84,7 @@ impl CFSQueue { // 如果队列为空,则返回IDLE进程的pcb res = unsafe { self.idle_pcb.as_mut().unwrap() }; } - self.lock.unlock_irqrestore(&rflags); + self.lock.unlock_irqrestore(rflags); return res; } diff --git a/kernel/src/sched/core.c b/kernel/src/sched/core.c index 8f7a86f5..9d90fa71 100644 --- a/kernel/src/sched/core.c +++ b/kernel/src/sched/core.c @@ -8,7 +8,7 @@ */ void switch_proc(struct process_control_block *prev, struct process_control_block *proc) { - process_switch_mm(proc); + // process_switch_mm(proc); io_mfence(); switch_to(prev, proc); } \ No newline at end of file diff --git a/kernel/src/sched/core.rs b/kernel/src/sched/core.rs index 4aec5f0b..aa1fb17d 100644 --- a/kernel/src/sched/core.rs +++ b/kernel/src/sched/core.rs @@ -7,6 +7,7 @@ use crate::{ process_control_block, MAX_CPU_NUM, PF_NEED_MIGRATE, PROC_RUNNING, SCHED_FIFO, SCHED_NORMAL, SCHED_RR, }, + kinfo, process::process::process_cpu, syscall::SystemError, }; @@ -140,10 +141,12 @@ pub extern "C" fn sched_enqueue(pcb: &'static mut process_control_block, mut res #[allow(dead_code)] #[no_mangle] pub extern "C" fn sched_init() { + kinfo!("Initializing schedulers..."); unsafe { sched_cfs_init(); sched_rt_init(); } + kinfo!("Schedulers initialized"); } /// @brief 当时钟中断到达时,更新时间片 diff --git a/kernel/src/sched/rt.rs b/kernel/src/sched/rt.rs index 47e6801d..ea3ccad1 100644 --- a/kernel/src/sched/rt.rs +++ b/kernel/src/sched/rt.rs @@ -50,22 +50,22 @@ impl RTQueue { } /// @brief 将pcb加入队列 pub fn enqueue(&mut self, pcb: &'static mut process_control_block) { - let mut rflags = 0u64; + let mut rflags = 0usize; self.lock.lock_irqsave(&mut rflags); // 如果进程是IDLE进程,那么就不加入队列 if pcb.pid == 0 { - self.lock.unlock_irqrestore(&rflags); + self.lock.unlock_irqrestore(rflags); return; } self.queue.push_back(pcb); - self.lock.unlock_irqrestore(&rflags); + self.lock.unlock_irqrestore(rflags); } /// @brief 将pcb从调度队列头部取出,若队列为空,则返回None pub fn dequeue(&mut self) -> Option<&'static mut process_control_block> { let res: Option<&'static mut process_control_block>; - let mut rflags = 0u64; + let mut rflags = 0usize; self.lock.lock_irqsave(&mut rflags); if self.queue.len() > 0 { // 队列不为空,返回下一个要执行的pcb @@ -74,20 +74,20 @@ impl RTQueue { // 如果队列为空,则返回None res = None; } - self.lock.unlock_irqrestore(&rflags); + self.lock.unlock_irqrestore(rflags); return res; } pub fn enqueue_front(&mut self, pcb: &'static mut process_control_block) { - let mut rflags = 0u64; + let mut rflags = 0usize; self.lock.lock_irqsave(&mut rflags); // 如果进程是IDLE进程,那么就不加入队列 if pcb.pid == 0 { - self.lock.unlock_irqrestore(&rflags); + self.lock.unlock_irqrestore(rflags); return; } self.queue.push_front(pcb); - self.lock.unlock_irqrestore(&rflags); + self.lock.unlock_irqrestore(rflags); } pub fn get_rt_queue_size(&mut self) -> usize { return self.queue.len(); diff --git a/kernel/src/sched/syscall.rs b/kernel/src/sched/syscall.rs index 15cffc90..f62821fe 100644 --- a/kernel/src/sched/syscall.rs +++ b/kernel/src/sched/syscall.rs @@ -1,9 +1,6 @@ use crate::{ - arch::{ - asm::current::current_pcb, - context::switch_process, - interrupt::{cli, sti}, - }, + arch::{asm::current::current_pcb, context::switch_process, CurrentIrqArch}, + exception::InterruptArch, syscall::{Syscall, SystemError}, }; @@ -14,7 +11,8 @@ impl Syscall { /// 请注意,该系统调用不能由ring3的程序发起 #[inline(always)] pub fn sched(from_user: bool) -> Result { - cli(); + let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; + // 进行权限校验,拒绝用户态发起调度 if from_user { return Err(SystemError::EPERM); @@ -25,7 +23,7 @@ impl Syscall { if pcb.is_some() { switch_process(current_pcb(), pcb.unwrap()); } - sti(); + drop(irq_guard); return Ok(0); } } diff --git a/kernel/src/smp/c_adapter.rs b/kernel/src/smp/c_adapter.rs new file mode 100644 index 00000000..e1d8e544 --- /dev/null +++ b/kernel/src/smp/c_adapter.rs @@ -0,0 +1,13 @@ +use super::kick_cpu; + +#[no_mangle] +pub extern "C" fn rs_kick_cpu(cpu_id: usize) -> usize { + return kick_cpu(cpu_id) + .map(|_| 0usize) + .unwrap_or_else(|e| e.to_posix_errno() as usize); +} + +#[no_mangle] +pub unsafe extern "C" fn rs_smp_init_idle() { + crate::smp::init_smp_idle_process(); +} diff --git a/kernel/src/smp/mod.rs b/kernel/src/smp/mod.rs index 021f7b39..ee4a9698 100644 --- a/kernel/src/smp/mod.rs +++ b/kernel/src/smp/mod.rs @@ -1,9 +1,11 @@ use crate::{ - arch::interrupt::ipi::send_ipi, + arch::{asm::current::current_pcb, interrupt::ipi::send_ipi}, exception::ipi::{IpiKind, IpiTarget}, + mm::INITIAL_PROCESS_ADDRESS_SPACE, syscall::SystemError, }; +pub mod c_adapter; pub mod core; pub fn kick_cpu(cpu_id: usize) -> Result<(), SystemError> { @@ -13,9 +15,7 @@ pub fn kick_cpu(cpu_id: usize) -> Result<(), SystemError> { return Ok(()); } -#[no_mangle] -pub extern "C" fn rs_kick_cpu(cpu_id: usize) -> usize { - return kick_cpu(cpu_id) - .map(|_| 0usize) - .unwrap_or_else(|e| e.to_posix_errno() as usize); +/// 初始化AP核的idle进程 +pub unsafe fn init_smp_idle_process() { + current_pcb().set_address_space(INITIAL_PROCESS_ADDRESS_SPACE()); } diff --git a/kernel/src/smp/smp.c b/kernel/src/smp/smp.c index 316663df..3e8dfbca 100644 --- a/kernel/src/smp/smp.c +++ b/kernel/src/smp/smp.c @@ -14,6 +14,7 @@ #include "ipi.h" static void __smp_kick_cpu_handler(uint64_t irq_num, uint64_t param, struct pt_regs *regs); +static void __smp__flush_tlb_ipi_handler(uint64_t irq_num, uint64_t param, struct pt_regs *regs); static spinlock_t multi_core_starting_lock = {1}; // 多核启动锁 @@ -22,13 +23,22 @@ static uint32_t total_processor_num = 0; static int current_starting_cpu = 0; static int num_cpu_started = 1; +extern void rs_smp_init_idle(); + +// 在head.S中定义的,APU启动时,要加载的页表 +// 由于内存管理模块初始化的时候,重置了页表,因此我们要把当前的页表传给APU +extern uint64_t __APU_START_CR3; // kick cpu 功能所使用的中断向量号 #define KICK_CPU_IRQ_NUM 0xc8 +#define FLUSH_TLB_IRQ_NUM 0xc9 void smp_init() { spin_init(&multi_core_starting_lock); // 初始化多核启动锁 + // 设置多核启动时,要加载的页表 + __APU_START_CR3 = (uint64_t)get_CR3(); + ul tmp_vaddr[MAX_SUPPORTED_PROCESSOR_NUM] = {0}; apic_get_ics(ACPI_ICS_TYPE_PROCESSOR_LOCAL_APIC, tmp_vaddr, &total_processor_num); @@ -57,6 +67,7 @@ void smp_init() kdebug("total_processor_num=%d", total_processor_num); // 注册接收kick_cpu功能的处理函数。(向量号200) ipi_regiserIPI(KICK_CPU_IRQ_NUM, NULL, &__smp_kick_cpu_handler, NULL, NULL, "IPI kick cpu"); + ipi_regiserIPI(FLUSH_TLB_IRQ_NUM, NULL, &__smp__flush_tlb_ipi_handler, NULL, NULL, "IPI flush tlb"); int core_to_start = 0; // total_processor_num = 3; @@ -104,7 +115,7 @@ void smp_init() cpu_core_info[current_starting_cpu].tss_vaddr = (uint64_t)&initial_tss[current_starting_cpu]; memset(&initial_tss[current_starting_cpu], 0, sizeof(struct tss_struct)); - + // kdebug("core %d, set tss", current_starting_cpu); set_tss_descriptor(10 + (current_starting_cpu * 2), (void *)(cpu_core_info[current_starting_cpu].tss_vaddr)); io_mfence(); set_tss64( @@ -116,12 +127,14 @@ void smp_init() cpu_core_info[current_starting_cpu].ist_stack_start); io_mfence(); + // kdebug("core %d, to send start up", current_starting_cpu); // 连续发送两次start-up IPI ipi_send_IPI(DEST_PHYSICAL, IDLE, ICR_LEVEL_DE_ASSERT, EDGE_TRIGGER, 0x20, ICR_Start_up, ICR_No_Shorthand, proc_local_apic_structs[i]->local_apic_id); io_mfence(); ipi_send_IPI(DEST_PHYSICAL, IDLE, ICR_LEVEL_DE_ASSERT, EDGE_TRIGGER, 0x20, ICR_Start_up, ICR_No_Shorthand, proc_local_apic_structs[i]->local_apic_id); + // kdebug("core %d, send start up ok", current_starting_cpu); } io_mfence(); while (num_cpu_started != (core_to_start + 1)) @@ -130,12 +143,7 @@ void smp_init() kinfo("Cleaning page table remapping...\n"); // 由于ap处理器初始化过程需要用到0x00处的地址,因此初始化完毕后才取消内存地址的重映射 - uint64_t *global_CR3 = get_CR3(); - for (int i = 0; i < 256; ++i) - { - io_mfence(); - *(ul *)(phys_2_virt(global_CR3) + i) = 0UL; - } + rs_unmap_at_low_addr(); kdebug("init proc's preempt_count=%ld", current_pcb->preempt_count); kinfo("Successfully cleaned page table remapping!\n"); } @@ -149,8 +157,10 @@ void smp_ap_start() // 切换栈基地址 // uint64_t stack_start = (uint64_t)kmalloc(STACK_SIZE, 0) + STACK_SIZE; - __asm__ __volatile__("movq %0, %%rbp \n\t" ::"m"(cpu_core_info[current_starting_cpu].stack_start) : "memory"); - __asm__ __volatile__("movq %0, %%rsp \n\t" ::"m"(cpu_core_info[current_starting_cpu].stack_start) : "memory"); + __asm__ __volatile__("movq %0, %%rbp \n\t" ::"m"(cpu_core_info[current_starting_cpu].stack_start) + : "memory"); + __asm__ __volatile__("movq %0, %%rsp \n\t" ::"m"(cpu_core_info[current_starting_cpu].stack_start) + : "memory"); ksuccess("AP core %d successfully started!", current_starting_cpu); io_mfence(); @@ -164,7 +174,8 @@ void smp_ap_start() barrier(); current_pcb->state = PROC_RUNNING; current_pcb->flags = PF_KTHREAD; - current_pcb->mm = &initial_mm; + current_pcb->address_space = NULL; + rs_smp_init_idle(); list_init(¤t_pcb->list); current_pcb->addr_limit = KERNEL_BASE_LINEAR_ADDR; @@ -190,8 +201,9 @@ void smp_ap_start() preempt_disable(); // 由于ap处理器的pcb与bsp的不同,因此ap处理器放锁时,需要手动恢复preempt count io_mfence(); current_pcb->flags |= PF_NEED_SCHED; - sti(); + apic_timer_ap_core_init(); + sti(); sched(); while (1) @@ -222,6 +234,12 @@ static void __smp_kick_cpu_handler(uint64_t irq_num, uint64_t param, struct pt_r sched(); } +static void __smp__flush_tlb_ipi_handler(uint64_t irq_num, uint64_t param, struct pt_regs *regs) +{ + if (user_mode(regs)) + return; + flush_tlb(); +} /** * @brief 获取当前全部的cpu数目 diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 57e3750a..92e75dde 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -6,27 +6,30 @@ use core::{ use num_traits::{FromPrimitive, ToPrimitive}; use crate::{ - arch::cpu::cpu_reset, + arch::{cpu::cpu_reset, MMArch}, filesystem::vfs::{ file::FileMode, syscall::{SEEK_CUR, SEEK_END, SEEK_MAX, SEEK_SET}, MAX_PATHLEN, }, - include::bindings::bindings::{mm_stat_t, pid_t, verify_area, PAGE_2M_SIZE, PAGE_4K_SIZE}, + include::bindings::bindings::{pid_t, verify_area, PAGE_2M_SIZE, PAGE_4K_SIZE}, io::SeekFrom, kinfo, + libs::align::page_align_up, + mm::{MemoryManagementArch, VirtAddr}, net::syscall::SockAddr, time::{ - syscall::{PosixTimeZone, PosixTimeval, SYS_TIMEZONE}, + syscall::{PosixTimeZone, PosixTimeval}, TimeSpec, }, }; +pub mod user_access; + #[repr(i32)] #[derive(Debug, FromPrimitive, ToPrimitive, PartialEq, Eq, Clone)] #[allow(dead_code, non_camel_case_types)] pub enum SystemError { - /// 操作不被允许 Operation not permitted. EPERM = 1, /// 没有指定的文件或目录 No such file or directory. ENOENT = 2, @@ -331,9 +334,8 @@ pub const SYS_NANOSLEEP: usize = 18; /// todo: 该系统调用与Linux不一致,将来需要删除该系统调用!!! 删的时候记得改C版本的libc pub const SYS_CLOCK: usize = 19; pub const SYS_PIPE: usize = 20; - -/// todo: 该系统调用不是符合POSIX标准的,在将来需要删除!!! -pub const SYS_MSTAT: usize = 21; +/// 系统调用21曾经是SYS_MSTAT,但是现在已经废弃 +pub const __NOT_USED: usize = 21; pub const SYS_UNLINK_AT: usize = 22; pub const SYS_KILL: usize = 23; pub const SYS_SIGACTION: usize = 24; @@ -358,6 +360,9 @@ pub const SYS_ACCEPT: usize = 40; pub const SYS_GETSOCKNAME: usize = 41; pub const SYS_GETPEERNAME: usize = 42; pub const SYS_GETTIMEOFDAY: usize = 43; +pub const SYS_MMAP: usize = 44; +pub const SYS_MUNMAP: usize = 45; +pub const SYS_MPROTECT: usize = 46; #[derive(Debug)] pub struct Syscall; @@ -473,13 +478,13 @@ impl Syscall { } SYS_BRK => { - let new_brk = args[0]; - Self::brk(new_brk) + let new_brk = VirtAddr::new(args[0]); + Self::brk(new_brk).map(|vaddr| vaddr.data()) } SYS_SBRK => { let increment = args[0] as isize; - Self::sbrk(increment) + Self::sbrk(increment).map(|vaddr| vaddr.data()) } SYS_REBOOT => Self::reboot(), @@ -642,18 +647,6 @@ impl Syscall { } } - SYS_MSTAT => { - let dst = args[0] as *mut mm_stat_t; - if from_user - && unsafe { !verify_area(dst as u64, core::mem::size_of::() as u64) } - { - Err(SystemError::EFAULT) - } else if dst.is_null() { - Err(SystemError::EFAULT) - } else { - Self::mstat(dst, from_user) - } - } SYS_UNLINK_AT => { let dirfd = args[0] as i32; let pathname = args[1] as *const c_char; @@ -873,7 +866,7 @@ impl Syscall { } SYS_GETTIMEOFDAY => { let timeval = args[0] as *mut PosixTimeval; - let timezone_ptr = args[1] as *const PosixTimeZone; + let timezone_ptr = args[1] as *mut PosixTimeZone; let security_check = || { if unsafe { verify_area(timeval as u64, core::mem::size_of::() as u64) @@ -896,18 +889,49 @@ impl Syscall { if r.is_err() { Err(r.unwrap_err()) } else { - let timezone = if !timezone_ptr.is_null() { - &SYS_TIMEZONE - } else { - unsafe { timezone_ptr.as_ref().unwrap() } - }; if !timeval.is_null() { - Self::gettimeofday(timeval, timezone) + Self::gettimeofday(timeval, timezone_ptr) } else { Err(SystemError::EFAULT) } } } + SYS_MMAP => { + let len = page_align_up(args[1]); + if unsafe { !verify_area(args[0] as u64, len as u64) } { + Err(SystemError::EFAULT) + } else { + Self::mmap( + VirtAddr::new(args[0]), + len, + args[2], + args[3], + args[4] as i32, + args[5], + ) + } + } + SYS_MUNMAP => { + let addr = args[0]; + let len = page_align_up(args[1]); + if addr & MMArch::PAGE_SIZE != 0 { + // The addr argument is not a multiple of the page size + Err(SystemError::EINVAL) + } else { + Self::munmap(VirtAddr::new(addr), len) + } + } + SYS_MPROTECT => { + let addr = args[0]; + let len = page_align_up(args[1]); + if addr & MMArch::PAGE_SIZE != 0 { + // The addr argument is not a multiple of the page size + Err(SystemError::EINVAL) + } else { + Self::mprotect(VirtAddr::new(addr), len, args[2]) + } + } + _ => panic!("Unsupported syscall ID: {}", syscall_num), }; diff --git a/kernel/src/syscall/syscall.c b/kernel/src/syscall/syscall.c index 6bd038f7..64121e6e 100644 --- a/kernel/src/syscall/syscall.c +++ b/kernel/src/syscall/syscall.c @@ -73,102 +73,6 @@ ul do_put_string(char *s, uint32_t front_color, uint32_t background_color) return 0; } -/** - * @brief 将堆内存调整为arg0 - * - * @param arg0 新的堆区域的结束地址 - * @return uint64_t 错误码 - * - */ -uint64_t sys_do_brk(uint64_t newaddr) -{ - uint64_t new_brk = PAGE_2M_ALIGN(newaddr); - // kdebug("sys_brk input= %#010lx , new_brk= %#010lx bytes current_pcb->mm->brk_start=%#018lx - // current->end_brk=%#018lx", regs->r8, new_brk, current_pcb->mm->brk_start, current_pcb->mm->brk_end); - struct mm_struct *mm = current_pcb->mm; - if (new_brk < mm->brk_start || new_brk > new_brk >= current_pcb->addr_limit) - return mm->brk_end; - - if (mm->brk_end == new_brk) - return new_brk; - - int64_t offset; - if (new_brk >= current_pcb->mm->brk_end) - offset = (int64_t)(new_brk - current_pcb->mm->brk_end); - else - offset = -(int64_t)(current_pcb->mm->brk_end - new_brk); - - new_brk = mm_do_brk(current_pcb->mm->brk_end, offset); // 扩展堆内存空间 - - current_pcb->mm->brk_end = new_brk; - return mm->brk_end; -} - -/** - * @brief 将堆内存空间加上offset(注意,该系统调用只应在普通进程中调用,而不能是内核线程) - * - * @param incr offset偏移量 - * @return uint64_t the previous program break - */ -uint64_t sys_do_sbrk(int64_t incr) -{ - uint64_t retval = current_pcb->mm->brk_end; - if ((int64_t)incr > 0) - { - - uint64_t new_brk = PAGE_2M_ALIGN(retval + incr); - if (new_brk > current_pcb->addr_limit) // 堆地址空间超过限制 - { - kdebug("exceed mem limit, new_brk = %#018lx", new_brk); - return -ENOMEM; - } - } - else - { - if ((__int128_t)current_pcb->mm->brk_end + (__int128_t)incr < current_pcb->mm->brk_start) - return retval; - } - // kdebug("do brk"); - uint64_t new_brk = mm_do_brk(current_pcb->mm->brk_end, (int64_t)incr); // 调整堆内存空间 - // kdebug("do brk done, new_brk = %#018lx", new_brk); - current_pcb->mm->brk_end = new_brk; - return retval; -} - -/** - * @brief 执行新的程序 - * - * @param user_path(r8寄存器) 文件路径 - * @param argv(r9寄存器) 参数列表 - * @return uint64_t - */ -uint64_t c_sys_execve(char *user_path, char **argv, char **envp, struct pt_regs *regs) -{ - - int path_len = strnlen_user(user_path, PAGE_4K_SIZE); - - if (path_len >= PAGE_4K_SIZE) - return -ENAMETOOLONG; - else if (path_len <= 0) - return -EFAULT; - - char *path = (char *)kmalloc(path_len + 1, 0); - if (path == NULL) - return -ENOMEM; - - memset(path, 0, path_len + 1); - - // 拷贝文件路径 - strncpy_from_user(path, user_path, path_len); - path[path_len] = '\0'; - - // 执行新的程序 - uint64_t retval = do_execve(regs, path, argv, NULL); - - kfree(path); - return retval; -} - /** * @brief 等待进程退出 * diff --git a/kernel/src/syscall/syscall_num.h b/kernel/src/syscall/syscall_num.h index c076f461..809ab2de 100644 --- a/kernel/src/syscall/syscall_num.h +++ b/kernel/src/syscall/syscall_num.h @@ -30,7 +30,6 @@ #define SYS_CLOCK 19 // 获取当前cpu时间 #define SYS_PIPE 20 // 创建管道 -#define SYS_MSTAT 21 // 获取系统的内存状态信息 #define SYS_UNLINK_AT 22 // 删除文件夹/删除文件链接 #define SYS_KILL 23 // kill一个进程(向这个进程发出信号) #define SYS_SIGACTION 24 // 设置进程的信号处理动作 @@ -41,18 +40,20 @@ #define SYS_DUP2 29 #define SYS_SOCKET 30 // 创建一个socket -#define SYS_SETSOCKOPT 31 // 设置socket的选项 -#define SYS_GETSOCKOPT 32 // 获取socket的选项 -#define SYS_CONNECT 33 // 连接到一个socket -#define SYS_BIND 34 // 绑定一个socket -#define SYS_SENDTO 35 // 向一个socket发送数据 -#define SYS_RECVFROM 36 // 从一个socket接收数据 -#define SYS_RECVMSG 37 // 从一个socket接收消息 -#define SYS_LISTEN 38 // 监听一个socket -#define SYS_SHUTDOWN 39 // 关闭socket -#define SYS_ACCEPT 40 // 接受一个socket连接 -#define SYS_GETSOCKNAME 41 // 获取socket的名字 -#define SYS_GETPEERNAME 42 // 获取socket的对端名字 -#define SYS_GETTIMEOFDAY 43 // 获取当前时间 +#define SYS_SETSOCKOPT 31 // 设置socket的选项 +#define SYS_GETSOCKOPT 32 // 获取socket的选项 +#define SYS_CONNECT 33 // 连接到一个socket +#define SYS_BIND 34 // 绑定一个socket +#define SYS_SENDTO 35 // 向一个socket发送数据 +#define SYS_RECVFROM 36 // 从一个socket接收数据 +#define SYS_RECVMSG 37 // 从一个socket接收消息 +#define SYS_LISTEN 38 // 监听一个socket +#define SYS_SHUTDOWN 39 // 关闭socket +#define SYS_ACCEPT 40 // 接受一个socket连接 -#define SYS_AHCI_END_REQ 255 // AHCI DMA请求结束end_request的系统调用 \ No newline at end of file +#define SYS_GETSOCKNAME 41 // 获取socket的名字 +#define SYS_GETPEERNAME 42 // 获取socket的对端名字 +#define SYS_GETTIMEOFDAY 43 // 获取当前时间 +#define SYS_MMAP 44 // 内存映射 +#define SYS_MUNMAP 45 // 内存解除映射 +#define SYS_MPROTECT 46 // 内存保护 diff --git a/kernel/src/syscall/user_access.rs b/kernel/src/syscall/user_access.rs new file mode 100644 index 00000000..1078e417 --- /dev/null +++ b/kernel/src/syscall/user_access.rs @@ -0,0 +1,141 @@ +//! 这个文件用于放置一些内核态访问用户态数据的函数 +use core::mem::size_of; + +use alloc::{string::String, vec::Vec}; + +use crate::mm::{verify_area, VirtAddr}; + +use super::SystemError; + +/// 清空用户空间指定范围内的数据 +/// +/// ## 参数 +/// +/// - `dest`:用户空间的目标地址 +/// - `len`:要清空的数据长度 +/// +/// ## 返回值 +/// +/// 返回清空的数据长度 +/// +/// ## 错误 +/// +/// - `EFAULT`:目标地址不合法 +pub unsafe fn clear_user(dest: VirtAddr, len: usize) -> Result { + verify_area(dest, len).map_err(|_| SystemError::EFAULT)?; + + let p = dest.data() as *mut u8; + // 清空用户空间的数据 + p.write_bytes(0, len); + return Ok(len); +} + +pub unsafe fn copy_to_user(dest: VirtAddr, src: &[u8]) -> Result { + verify_area(dest, src.len()).map_err(|_| SystemError::EFAULT)?; + + let p = dest.data() as *mut u8; + // 拷贝数据 + p.copy_from_nonoverlapping(src.as_ptr(), src.len()); + return Ok(src.len()); +} + +/// 从用户空间拷贝数据到内核空间 +pub unsafe fn copy_from_user(dst: &mut [u8], src: VirtAddr) -> Result { + verify_area(src, dst.len()).map_err(|_| SystemError::EFAULT)?; + + let src: &[u8] = core::slice::from_raw_parts(src.data() as *const u8, dst.len()); + // 拷贝数据 + dst.copy_from_slice(&src); + + return Ok(dst.len()); +} + +/// 检查并从用户态拷贝一个 C 字符串。 +/// +/// 一旦遇到非法地址,就会返回错误 +/// +/// ## 参数 +/// +/// - `user`:用户态的 C 字符串指针 +/// - `max_length`:最大拷贝长度 +/// +/// ## 返回值 +/// +/// 返回拷贝的 C 字符串 +/// +/// ## 错误 +/// +/// - `EFAULT`:用户态地址不合法 +pub fn check_and_clone_cstr( + user: *const u8, + max_length: Option, +) -> Result { + if user.is_null() { + return Ok(String::new()); + } + + // 从用户态读取,直到遇到空字符 '\0' 或者达到最大长度 + let mut buffer = Vec::new(); + for i in 0.. { + if max_length.is_some() && max_length.as_ref().unwrap() <= &i { + break; + } + + let addr = unsafe { user.add(i) }; + let mut c = [0u8; 1]; + unsafe { + copy_from_user(&mut c, VirtAddr::new(addr as usize))?; + } + if c[0] == 0 { + break; + } + buffer.push(c[0]); + } + return Ok(String::from_utf8(buffer).map_err(|_| SystemError::EFAULT)?); +} + +/// 检查并从用户态拷贝一个 C 字符串数组 +/// +/// 一旦遇到空指针,就会停止拷贝. 一旦遇到非法地址,就会返回错误 +/// ## 参数 +/// +/// - `user`:用户态的 C 字符串指针数组 +/// +/// ## 返回值 +/// +/// 返回拷贝的 C 字符串数组 +/// +/// ## 错误 +/// +/// - `EFAULT`:用户态地址不合法 +pub fn check_and_clone_cstr_array(user: *const *const u8) -> Result, SystemError> { + if user.is_null() { + Ok(Vec::new()) + } else { + // kdebug!("check_and_clone_cstr_array: {:p}\n", user); + let mut buffer = Vec::new(); + for i in 0.. { + let addr = unsafe { user.add(i) }; + let str_ptr: *const u8; + // 读取这个地址的值(这个值也是一个指针) + unsafe { + let dst = [0usize; 1]; + let mut dst = core::mem::transmute::<[usize; 1], [u8; size_of::()]>(dst); + copy_from_user(&mut dst, VirtAddr::new(addr as usize))?; + let dst = core::mem::transmute::<[u8; size_of::()], [usize; 1]>(dst); + str_ptr = dst[0] as *const u8; + + // kdebug!("str_ptr: {:p}, addr:{addr:?}\n", str_ptr); + } + + if str_ptr.is_null() { + break; + } + // 读取这个指针指向的字符串 + let string = check_and_clone_cstr(str_ptr, None)?; + // 将字符串放入 buffer 中 + buffer.push(string); + } + return Ok(buffer); + } +} diff --git a/kernel/src/time/clocksource.rs b/kernel/src/time/clocksource.rs index 7a1a470f..917b68c2 100644 --- a/kernel/src/time/clocksource.rs +++ b/kernel/src/time/clocksource.rs @@ -625,7 +625,7 @@ pub fn clocksource_resume() { match ele.resume() { Ok(_) => continue, Err(_) => { - kdebug!("clocksource {:?} resume failed", data.name) + kdebug!("clocksource {:?} resume failed", data.name); } } } @@ -641,7 +641,7 @@ pub fn clocksource_suspend() { match ele.suspend() { Ok(_) => continue, Err(_) => { - kdebug!("clocksource {:?} suspend failed", data.name) + kdebug!("clocksource {:?} suspend failed", data.name); } } } diff --git a/kernel/src/time/jiffies.rs b/kernel/src/time/jiffies.rs index 57cea867..3ec9da29 100644 --- a/kernel/src/time/jiffies.rs +++ b/kernel/src/time/jiffies.rs @@ -3,7 +3,7 @@ use alloc::{ sync::{Arc, Weak}, }; -use crate::{kdebug, libs::spinlock::SpinLock, syscall::SystemError}; +use crate::{kerror, kinfo, libs::spinlock::SpinLock, syscall::SystemError}; use super::{ clocksource::{Clocksource, ClocksourceData, ClocksourceFlags, ClocksourceMask, CycleNum, HZ}, @@ -89,9 +89,13 @@ pub fn jiffies_init() { //注册jiffies let jiffies = clocksource_default_clock() as Arc; match jiffies.register() { - Ok(_) => kdebug!("jiffies_init sccessfully"), - Err(_) => kdebug!("jiffies_init failed, no default clock running"), - } + Ok(_) => { + kinfo!("jiffies_init sccessfully"); + } + Err(_) => { + kerror!("jiffies_init failed, no default clock running"); + } + }; } #[no_mangle] diff --git a/kernel/src/time/syscall.rs b/kernel/src/time/syscall.rs index 59372bd5..15cfa014 100644 --- a/kernel/src/time/syscall.rs +++ b/kernel/src/time/syscall.rs @@ -4,7 +4,6 @@ use core::{ }; use crate::{ - kdebug, syscall::{Syscall, SystemError}, time::{sleep::nanosleep, TimeSpec}, }; @@ -79,10 +78,10 @@ impl Syscall { pub fn gettimeofday( tv: *mut PosixTimeval, - _timezone: &PosixTimeZone, + timezone: *mut PosixTimeZone, ) -> Result { // TODO; 处理时区信息 - kdebug!("enter sys_do_gettimeofday"); + // kdebug!("enter sys_do_gettimeofday"); if tv == null_mut() { return Err(SystemError::EFAULT); } @@ -91,7 +90,13 @@ impl Syscall { (*tv).tv_sec = posix_time.tv_sec; (*tv).tv_usec = posix_time.tv_usec; } - kdebug!("exit sys_do_gettimeofday"); + + if !timezone.is_null() { + unsafe { + *timezone = SYS_TIMEZONE; + } + } + // kdebug!("exit sys_do_gettimeofday"); return Ok(0); } } diff --git a/kernel/src/time/timekeeping.rs b/kernel/src/time/timekeeping.rs index 966faa64..8b714819 100644 --- a/kernel/src/time/timekeeping.rs +++ b/kernel/src/time/timekeeping.rs @@ -5,7 +5,7 @@ use x86_64::align_up; use crate::{ arch::CurrentIrqArch, exception::InterruptArch, - kdebug, + kdebug, kinfo, libs::rwlock::RwLock, time::{jiffies::clocksource_default_clock, timekeep::ktime_get_real_ns, TimeSpec}, }; @@ -118,6 +118,7 @@ impl Timekeeper { timekeeper.cycle_interval = CycleNum(temp); timekeeper.xtime_interval = temp * clock_data.mult as u64; + // 这里可能存在下界溢出问题,debug模式下会报错panic timekeeper.xtime_remainder = (ntpinterval - timekeeper.xtime_interval) as i64; timekeeper.raw_interval = (timekeeper.xtime_interval >> clock_data.shift) as i64; timekeeper.xtime_nsec = 0; @@ -154,7 +155,7 @@ pub fn timekeeper_init() { /// /// * 'TimeSpec' - 时间戳 pub fn getnstimeofday() -> TimeSpec { - kdebug!("enter getnstimeofday"); + // kdebug!("enter getnstimeofday"); // let mut nsecs: u64 = 0;0 let mut _xtime = TimeSpec { @@ -201,6 +202,7 @@ pub fn do_gettimeofday() -> PosixTimeval { /// # 初始化timekeeping模块 pub fn timekeeping_init() { + kinfo!("Initializing timekeeping module..."); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; timekeeper_init(); @@ -229,7 +231,7 @@ pub fn timekeeping_init() { __ADDED_SEC.store(0, Ordering::SeqCst); drop(irq_guard); - kdebug!("timekeeping_init successfully"); + kinfo!("timekeeping_init successfully"); } /// # 使用当前时钟源增加wall time diff --git a/kernel/src/time/timer.h b/kernel/src/time/timer.h index a690149d..c27f3f86 100644 --- a/kernel/src/time/timer.h +++ b/kernel/src/time/timer.h @@ -12,4 +12,5 @@ extern int64_t rs_timer_get_first_expire(); extern uint64_t rs_timer_next_n_ms_jiffies(uint64_t expire_ms); extern int64_t rs_schedule_timeout(int64_t timeout); -extern uint64_t rs_clock(); \ No newline at end of file +extern uint64_t rs_clock(); +extern void rs_jiffies_init(); diff --git a/kernel/src/time/timer.rs b/kernel/src/time/timer.rs index 47ee96a2..b831adc2 100644 --- a/kernel/src/time/timer.rs +++ b/kernel/src/time/timer.rs @@ -17,7 +17,7 @@ use crate::{ InterruptArch, }, include::bindings::bindings::{process_control_block, process_wakeup, PROC_RUNNING}, - kdebug, kerror, + kdebug, kerror, kinfo, libs::spinlock::SpinLock, syscall::SystemError, }; @@ -105,7 +105,6 @@ impl Timer { break; } } - let mut temp_list: LinkedList> = timer_list.split_off(split_pos); timer_list.push_back(inner_guard.self_ref.upgrade().unwrap()); timer_list.append(&mut temp_list); @@ -216,7 +215,7 @@ pub fn timer_init() { softirq_vectors() .register_softirq(SoftirqNumber::TIMER, do_timer_softirq) .expect("Failed to register timer softirq"); - kdebug!("timer initiated successfully"); + kinfo!("timer initialized successfully"); } /// 计算接下来n毫秒对应的定时器时间片 diff --git a/user/apps/about/about.c b/user/apps/about/about.c index 7bc5d35b..b049a784 100644 --- a/user/apps/about/about.c +++ b/user/apps/about/about.c @@ -32,6 +32,9 @@ void print_copyright() printf(" If you find any problems during use, please visit:\n"); put_string(" https://bbs.DragonOS.org\n", COLOR_ORANGE, COLOR_BLACK); printf("\n"); + printf(" Join our development community:\n"); + put_string(" https://DragonOS.zulipchat.com\n", COLOR_ORANGE, COLOR_BLACK); + printf("\n"); } int main() diff --git a/user/apps/shell/cmd.c b/user/apps/shell/cmd.c index 64cacb8d..87b06049 100644 --- a/user/apps/shell/cmd.c +++ b/user/apps/shell/cmd.c @@ -21,10 +21,21 @@ char *shell_current_path = NULL; * */ struct built_in_cmd_t shell_cmds[] = { - {"cd", shell_cmd_cd}, {"cat", shell_cmd_cat}, {"exec", shell_cmd_exec}, {"ls", shell_cmd_ls}, - {"mkdir", shell_cmd_mkdir}, {"pwd", shell_cmd_pwd}, {"rm", shell_cmd_rm}, {"rmdir", shell_cmd_rmdir}, - {"reboot", shell_cmd_reboot}, {"touch", shell_cmd_touch}, {"about", shell_cmd_about}, {"free", shell_cmd_free}, - {"help", shell_help}, {"pipe", shell_pipe_test}, {"kill", shell_cmd_kill}, + {"cd", shell_cmd_cd}, + {"cat", shell_cmd_cat}, + {"exec", shell_cmd_exec}, + {"ls", shell_cmd_ls}, + {"mkdir", shell_cmd_mkdir}, + {"pwd", shell_cmd_pwd}, + {"rm", shell_cmd_rm}, + {"rmdir", shell_cmd_rmdir}, + {"reboot", shell_cmd_reboot}, + {"touch", shell_cmd_touch}, + {"about", shell_cmd_about}, + {"free", shell_cmd_free}, + {"help", shell_help}, + {"pipe", shell_pipe_test}, + {"kill", shell_cmd_kill}, }; // 总共的内建命令数量 @@ -610,7 +621,7 @@ int parse_command(char *buf, int *argc, char ***argv) while (index < INPUT_BUFFER_SIZE && buf[index] == ' ') ++index; // 如果去除前导空格后第一项为0x00,则归为空命令 - if(!buf[index]) + if (!buf[index]) return -1; // 计算参数数量 @@ -622,12 +633,12 @@ int parse_command(char *buf, int *argc, char ***argv) if (buf[i] != ' ' && (buf[i + 1] == ' ' || buf[i + 1] == '\0')) ++(*argc); } - + // printf("\nargc=%d\n", *argc); // 为指向每个指令的指针分配空间 - *argv = (char **)malloc(sizeof(char **) * (*argc)); - memset(*argv, 0, sizeof(char **) * (*argc)); + *argv = (char **)malloc(sizeof(char **) * (*argc + 1)); + memset(*argv, 0, sizeof(char **) * (*argc + 1)); // 将每个命令都单独提取出来 for (int i = 0; i < *argc && index < INPUT_BUFFER_SIZE; ++i) { diff --git a/user/apps/test_relibc/Makefile b/user/apps/test_relibc/Makefile index 02e62ad6..064ea638 100644 --- a/user/apps/test_relibc/Makefile +++ b/user/apps/test_relibc/Makefile @@ -21,4 +21,4 @@ main.o: main.c $(CC) $(CFLAGS) -c main.c -o main.o clean: - rm -f *.o \ No newline at end of file + rm -f *.o diff --git a/user/libs/libc/src/malloc.c b/user/libs/libc/src/malloc.c index b0d05a9c..d1c02ade 100644 --- a/user/libs/libc/src/malloc.c +++ b/user/libs/libc/src/malloc.c @@ -156,6 +156,7 @@ static int malloc_enlarge(int64_t size) // 在新分配的内存的底部放置header // printf("managed addr = %#018lx\n", brk_managed_addr); malloc_mem_chunk_t *new_ck = (malloc_mem_chunk_t *)brk_managed_addr; + memset(new_ck, 0, sizeof(malloc_mem_chunk_t)); new_ck->length = brk_max_addr - brk_managed_addr; // printf("new_ck->start_addr=%#018lx\tbrk_max_addr=%#018lx\tbrk_managed_addr=%#018lx\n", (uint64_t)new_ck, brk_max_addr, brk_managed_addr); new_ck->prev = NULL; diff --git a/user/libs/libc/src/unistd.c b/user/libs/libc/src/unistd.c index 2c969172..8c79fde1 100644 --- a/user/libs/libc/src/unistd.c +++ b/user/libs/libc/src/unistd.c @@ -70,7 +70,8 @@ pid_t fork(void) * * @return int 如果失败返回负数 */ -int pipe(int fd[2]){ +int pipe(int fd[2]) +{ return (int)syscall_invoke(SYS_PIPE, fd, 0, 0, 0, 0, 0, 0, 0); } /** @@ -95,7 +96,8 @@ pid_t vfork(void) uint64_t brk(uint64_t end_brk) { uint64_t x = (uint64_t)syscall_invoke(SYS_BRK, (uint64_t)end_brk, 0, 0, 0, 0, 0, 0, 0); - if (x < end_brk){ + if (x < end_brk) + { errno = -ENOMEM; return -1; } @@ -217,7 +219,7 @@ pid_t getpid(void) int dup(int fd) { - return syscall_invoke(SYS_DUP, fd, 0, 0, 0, 0, 0, 0, 0); + return syscall_invoke(SYS_DUP, fd, 0, 0, 0, 0, 0, 0, 0); } int dup2(int ofd, int nfd)