From f210422fd5f8a56534010651f02cea0f7eff3d2b Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Sat, 6 Jul 2024 23:07:31 +0800 Subject: [PATCH 01/17] doc: update README Signed-off-by: Zone.N --- README.md | 51 ++++++++++++++++----------------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index a72d643d9..dcc114b74 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ # SimpleKernel -intr branch +memory branch ## 关键词 @@ -113,69 +113,50 @@ intr branch - riscv64 - 1. 对 CSR 寄存器的抽象 - 2. 寄存器状态打印 - 3. 基于 Direct 的中断处理 - 4. 中断注册函数 - 5. 时钟中断 - + 1. + - X86_64 - 1. cpu 抽象 - 2. 8259A pic 控制器抽象 - 3. 8253/8254 timer 控制器抽象 - 4. gdt 初始化 - 5. 中断处理流程 - 6. 中断注册函数 - 7. 时钟中断 + 1. - TODO - riscv64 PLIC - x86_64 APIC ## 已支持特性 - [x] [BUILD] 使用 CMake 的构建系统 - - [x] [BUILD] 使用 gdb remote 调试 - - [x] [BUILD] 第三方资源集成 - - [x] [COMMON] C++ 全局对象的构造 - - [x] [COMMON] C++ 静态局部对象构造 - - [x] [COMMON] C 栈保护支持 - - [x] [COMMON] printf 支持 - - [x] [COMMON] 简单的 C++ 异常支持 - - [x] [COMMON] 带颜色的字符串输出 - - [x] [x86_64] 基于 gnuefi 的 bootloader - - [x] [x86_64] 基于 serial 的基本输出 - - [x] [x86_64] 物理内存信息探测 - - [x] [x86_64] 显示缓冲区探测 - - [x] [x86_64] 调用栈回溯 - - [x] [riscv64] gp 寄存器的初始化 - - [x] [riscv64] 基于 opensbi 的基本输出 - - [x] [riscv64] device tree 硬件信息解析 - - [x] [riscv64] ns16550a 串口驱动 - - [x] [riscv64] 调用栈回溯(仅打印地址) - - [ ] [aarch64] 基于 gnuefi 的 bootloader(调试中) + - [x] [riscv64] 对 CSR 寄存器的抽象 + - [x] [riscv64] 寄存器状态打印 + - [x] [riscv64] 基于 Direct 的中断处理 + - [x] [riscv64] 中断注册函数 + - [x] [riscv64] 时钟中断 + - [x] [x86_64] cpu 抽象 + - [x] [x86_64] 8259A pic 控制器抽象 + - [x] [x86_64] 8253/8254 timer 控制器抽象 + - [x] [x86_64] gdt 初始化 + - [x] [x86_64] 中断处理流程 + - [x] [x86_64] 中断注册函数 + - [x] [x86_64] 时钟中断 ## 使用的第三方资源 From 9868ed9a8151bc7953755ba9850a76150d545c3e Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Sun, 7 Jul 2024 06:45:28 +0800 Subject: [PATCH 02/17] feat(libc): bit operations Signed-off-by: Zone.N --- src/kernel/CMakeLists.txt | 1 + src/kernel/libc/CMakeLists.txt | 1 + src/kernel/libc/bit.c | 194 +++++ test/unit_test/CMakeLists.txt | 2 + test/unit_test/libc_bit_test.cpp | 1201 ++++++++++++++++++++++++++++++ 5 files changed, 1399 insertions(+) create mode 100644 src/kernel/libc/bit.c create mode 100644 test/unit_test/libc_bit_test.cpp diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index e7d4008ac..b400b25ec 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/driver) add_executable(${PROJECT_NAME} main.cpp + firstfit_allocator.cpp ) # 添加头文件 diff --git a/src/kernel/libc/CMakeLists.txt b/src/kernel/libc/CMakeLists.txt index dd1bfb139..d37f2559b 100644 --- a/src/kernel/libc/CMakeLists.txt +++ b/src/kernel/libc/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(${PROJECT_NAME} OBJECT ${PROJECT_SOURCE_DIR}/libc.c ${PROJECT_SOURCE_DIR}/string.c ${PROJECT_SOURCE_DIR}/stdio.c + ${PROJECT_SOURCE_DIR}/bit.c ) # 添加定义 diff --git a/src/kernel/libc/bit.c b/src/kernel/libc/bit.c new file mode 100644 index 000000000..5219d80cf --- /dev/null +++ b/src/kernel/libc/bit.c @@ -0,0 +1,194 @@ + +/** + * @file bit.c + * @brief 位操作相关函数 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2023-03-31 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2023-03-31Zone.N迁移到 doxygen + *
+ */ + +#include + +// These functions return the number of leading 0-bits in a, starting at the +// most significant bit position. If a is zero, the result is undefined. +int __clzsi2(unsigned int a) { + if (a == 0) { + return 32; + } + int count = 0; + while ((a & 0x80000000) == 0) { + a <<= 1; + count++; + } + return count; +} + +int __clzdi2(unsigned long a) { + if (a == 0) { + return 64; + } + int count = 0; + while ((a & 0x8000000000000000UL) == 0) { + a <<= 1; + count++; + } + return count; +} + +int __clzti2(unsigned long long a) { + if (a == 0) { + return 64; + } + int count = 0; + while ((a & 0x8000000000000000ULL) == 0) { + a <<= 1; + count++; + } + return count; +} + +// These functions return the number of trailing 0-bits in a, starting at the +// least significant bit position. If a is zero, the result is undefined. +int __ctzsi2(unsigned int a) { + if (a == 0) { + return 32; // Return 32 if no bits are set + } + int count = 0; + while ((a & 1) == 0) { + a >>= 1; + count++; + } + return count; +} + +int __ctzdi2(unsigned long a) { + if (a == 0) { + return 64; + } + int count = 0; + while ((a & 1) == 0) { + a >>= 1; + count++; + } + return count; +} + +int __ctzti2(unsigned long long a) { + if (a == 0) { + return 64; + } + int count = 0; + while ((a & 1) == 0) { + a >>= 1; + count++; + } + return count; +} + +// These functions return the index of the least significant 1-bit in a, or the +// value zero if a is zero. The least significant bit is index one. +int __ffsdi2(unsigned long a) { + if (a == 0) { + return 0; + } + int index = 1; + while ((a & 1) == 0) { + a >>= 1; + index++; + } + return index; +} + +int __ffsti2(unsigned long long a) { + if (a == 0) { + return 0; + } + int index = 1; + while ((a & 1) == 0) { + a >>= 1; + index++; + } + return index; +} + +// These functions return the value zero if the number of bits set in a is even, +// and the value one otherwise. +int __paritysi2(unsigned int a) { + a ^= a >> 16; + a ^= a >> 8; + a ^= a >> 4; + a ^= a >> 2; + a ^= a >> 1; + return a & 1; +} + +int __paritydi2(unsigned long a) { + a ^= a >> 32; + a ^= a >> 16; + a ^= a >> 8; + a ^= a >> 4; + a ^= a >> 2; + a ^= a >> 1; + return a & 1; +} + +int __parityti2(unsigned long long a) { + a ^= a >> 32; + a ^= a >> 16; + a ^= a >> 8; + a ^= a >> 4; + a ^= a >> 2; + a ^= a >> 1; + return a & 1; +} + +// These functions return the number of bits set in a. +int __popcountsi2(unsigned int a) { + int count = 0; + while (a) { + a &= a - 1; + count++; + } + return count; +} + +int __popcountdi2(unsigned long a) { + int count = 0; + while (a) { + a &= a - 1; + count++; + } + return count; +} + +int __popcountti2(unsigned long long a) { + int count = 0; + while (a) { + a &= a - 1; + count++; + } + return count; +} + +// These functions return the a byteswapped. +int32_t __bswapsi2(int32_t a) { + return (((a & 0xFF000000) >> 24) | ((a & 0x00FF0000) >> 8) | + ((a & 0x0000FF00) << 8) | ((a & 0x000000FF) << 24)); +} + +int64_t __bswapdi2(int64_t a) { + return ( + ((a & 0xFF00000000000000ULL) >> 56) | + ((a & 0x00FF000000000000ULL) >> 40) | + ((a & 0x0000FF0000000000ULL) >> 24) | ((a & 0x000000FF00000000ULL) >> 8) | + ((a & 0x00000000FF000000ULL) << 8) | ((a & 0x0000000000FF0000ULL) << 24) | + ((a & 0x000000000000FF00ULL) << 40) | + ((a & 0x00000000000000FFULL) << 56)); +} diff --git a/test/unit_test/CMakeLists.txt b/test/unit_test/CMakeLists.txt index d72b3fcb3..c3af0fcc4 100644 --- a/test/unit_test/CMakeLists.txt +++ b/test/unit_test/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(${PROJECT_NAME} aarch64_cpu_test.cpp kernel_elf_test.cpp kernel_fdt_test.cpp + libc_bit_test.cpp ) add_header_arch(${PROJECT_NAME}) @@ -35,6 +36,7 @@ target_link_options(${PROJECT_NAME} PRIVATE target_link_libraries(${PROJECT_NAME} PRIVATE ${DEFAULT_TEST_LINK_LIB} ${dtc_BINARY_DIR}/libfdt/libfdt.a + libc ) add_dependencies(${PROJECT_NAME} diff --git a/test/unit_test/libc_bit_test.cpp b/test/unit_test/libc_bit_test.cpp new file mode 100644 index 000000000..2f07208f3 --- /dev/null +++ b/test/unit_test/libc_bit_test.cpp @@ -0,0 +1,1201 @@ + +/** + * @file aarch64_cpu_test.cpp + * @brief aarch64 cpu 相关测试 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2023-09-02 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2023-09-02Zone.N创建文件 + *
+ */ + +#include + +extern "C" int __clzsi2(unsigned int a); +extern "C" int __clzdi2(unsigned long a); +extern "C" int __clzti2(unsigned long long a); + +extern "C" int __ctzsi2(unsigned int a); +extern "C" int __ctzdi2(unsigned long a); +extern "C" int __ctzti2(unsigned long long a); + +extern "C" int __ffsdi2(unsigned long a); +extern "C" int __ffsti2(unsigned long long a); + +extern "C" int __paritysi2(unsigned int a); +extern "C" int __paritydi2(unsigned long a); +extern "C" int __parityti2(unsigned long long a); + +extern "C" int __popcountsi2(unsigned int a); +extern "C" int __popcountdi2(unsigned long a); +extern "C" int __popcountti2(unsigned long long a); + +extern "C" int32_t __bswapsi2(int32_t a); +extern "C" int64_t __bswapdi2(int64_t a); + +TEST(BitTest, __clzsi2Test) { + EXPECT_EQ(__clzsi2(0x00000000), 32); + EXPECT_EQ(__clzsi2(0x00000001), 31); + EXPECT_EQ(__clzsi2(0x00000002), 30); + EXPECT_EQ(__clzsi2(0x00000004), 29); + EXPECT_EQ(__clzsi2(0x00000008), 28); + EXPECT_EQ(__clzsi2(0x00000010), 27); + EXPECT_EQ(__clzsi2(0x00000020), 26); + EXPECT_EQ(__clzsi2(0x00000040), 25); + EXPECT_EQ(__clzsi2(0x00000080), 24); + EXPECT_EQ(__clzsi2(0x00000100), 23); + EXPECT_EQ(__clzsi2(0x00000200), 22); + EXPECT_EQ(__clzsi2(0x00000400), 21); + EXPECT_EQ(__clzsi2(0x00000800), 20); + EXPECT_EQ(__clzsi2(0x00001000), 19); + EXPECT_EQ(__clzsi2(0x00002000), 18); + EXPECT_EQ(__clzsi2(0x00004000), 17); + EXPECT_EQ(__clzsi2(0x00008000), 16); + EXPECT_EQ(__clzsi2(0x00010000), 15); + EXPECT_EQ(__clzsi2(0x00020000), 14); + EXPECT_EQ(__clzsi2(0x00040000), 13); + EXPECT_EQ(__clzsi2(0x00080000), 12); + EXPECT_EQ(__clzsi2(0x00100000), 11); + EXPECT_EQ(__clzsi2(0x00200000), 10); + EXPECT_EQ(__clzsi2(0x00400000), 9); + EXPECT_EQ(__clzsi2(0x00800000), 8); + EXPECT_EQ(__clzsi2(0x01000000), 7); + EXPECT_EQ(__clzsi2(0x02000000), 6); + EXPECT_EQ(__clzsi2(0x04000000), 5); + EXPECT_EQ(__clzsi2(0x08000000), 4); + EXPECT_EQ(__clzsi2(0x10000000), 3); + EXPECT_EQ(__clzsi2(0x20000000), 2); + EXPECT_EQ(__clzsi2(0x40000000), 1); + EXPECT_EQ(__clzsi2(0x80000000), 0); + + EXPECT_EQ(__clzsi2(0b00000000000000000000000000000000), 32); + EXPECT_EQ(__clzsi2(0b01000000001111000000000000000000), 1); + EXPECT_EQ(__clzsi2(0b00100000000000000000000000000000), 2); + EXPECT_EQ(__clzsi2(0b00010000000000001111000110000000), 3); + EXPECT_EQ(__clzsi2(0b00001001001000111000000000000000), 4); +} + +TEST(BitTest, __clzdi2Test) { + EXPECT_EQ(__clzdi2(0x0000000000000000), 64); + EXPECT_EQ(__clzdi2(0x0000000000000001), 63); + EXPECT_EQ(__clzdi2(0x0000000000000002), 62); + EXPECT_EQ(__clzdi2(0x0000000000000004), 61); + EXPECT_EQ(__clzdi2(0x0000000000000008), 60); + EXPECT_EQ(__clzdi2(0x0000000000000010), 59); + EXPECT_EQ(__clzdi2(0x0000000000000020), 58); + EXPECT_EQ(__clzdi2(0x0000000000000040), 57); + EXPECT_EQ(__clzdi2(0x0000000000000080), 56); + EXPECT_EQ(__clzdi2(0x0000000000000100), 55); + EXPECT_EQ(__clzdi2(0x0000000000000200), 54); + EXPECT_EQ(__clzdi2(0x0000000000000400), 53); + EXPECT_EQ(__clzdi2(0x0000000000000800), 52); + EXPECT_EQ(__clzdi2(0x0000000000001000), 51); + EXPECT_EQ(__clzdi2(0x0000000000002000), 50); + EXPECT_EQ(__clzdi2(0x0000000000004000), 49); + EXPECT_EQ(__clzdi2(0x0000000000008000), 48); + EXPECT_EQ(__clzdi2(0x0000000000010000), 47); + EXPECT_EQ(__clzdi2(0x0000000000020000), 46); + EXPECT_EQ(__clzdi2(0x0000000000040000), 45); + EXPECT_EQ(__clzdi2(0x0000000000080000), 44); + EXPECT_EQ(__clzdi2(0x0000000000100000), 43); + EXPECT_EQ(__clzdi2(0x0000000000200000), 42); + EXPECT_EQ(__clzdi2(0x0000000000400000), 41); + EXPECT_EQ(__clzdi2(0x0000000000800000), 40); + EXPECT_EQ(__clzdi2(0x0000000001000000), 39); + EXPECT_EQ(__clzdi2(0x0000000002000000), 38); + EXPECT_EQ(__clzdi2(0x0000000004000000), 37); + EXPECT_EQ(__clzdi2(0x0000000008000000), 36); + EXPECT_EQ(__clzdi2(0x0000000010000000), 35); + EXPECT_EQ(__clzdi2(0x0000000020000000), 34); + EXPECT_EQ(__clzdi2(0x0000000040000000), 33); + EXPECT_EQ(__clzdi2(0x0000000080000000), 32); + EXPECT_EQ(__clzdi2(0x0000000100000000), 31); + EXPECT_EQ(__clzdi2(0x0000000200000000), 30); + EXPECT_EQ(__clzdi2(0x0000000400000000), 29); + EXPECT_EQ(__clzdi2(0x0000000800000000), 28); + EXPECT_EQ(__clzdi2(0x0000001000000000), 27); + EXPECT_EQ(__clzdi2(0x0000002000000000), 26); + EXPECT_EQ(__clzdi2(0x0000004000000000), 25); + EXPECT_EQ(__clzdi2(0x0000008000000000), 24); + EXPECT_EQ(__clzdi2(0x0000010000000000), 23); + EXPECT_EQ(__clzdi2(0x0000020000000000), 22); + EXPECT_EQ(__clzdi2(0x0000040000000000), 21); + EXPECT_EQ(__clzdi2(0x0000080000000000), 20); + EXPECT_EQ(__clzdi2(0x0000100000000000), 19); + EXPECT_EQ(__clzdi2(0x0000200000000000), 18); + EXPECT_EQ(__clzdi2(0x0000400000000000), 17); + EXPECT_EQ(__clzdi2(0x0000800000000000), 16); + EXPECT_EQ(__clzdi2(0x0001000000000000), 15); + EXPECT_EQ(__clzdi2(0x0002000000000000), 14); + EXPECT_EQ(__clzdi2(0x0004000000000000), 13); + EXPECT_EQ(__clzdi2(0x0008000000000000), 12); + EXPECT_EQ(__clzdi2(0x0010000000000000), 11); + EXPECT_EQ(__clzdi2(0x0020000000000000), 10); + EXPECT_EQ(__clzdi2(0x0040000000000000), 9); + EXPECT_EQ(__clzdi2(0x0080000000000000), 8); + EXPECT_EQ(__clzdi2(0x0100000000000000), 7); + EXPECT_EQ(__clzdi2(0x0200000000000000), 6); + EXPECT_EQ(__clzdi2(0x0400000000000000), 5); + EXPECT_EQ(__clzdi2(0x0800000000000000), 4); + EXPECT_EQ(__clzdi2(0x1000000000000000), 3); + EXPECT_EQ(__clzdi2(0x2000000000000000), 2); + EXPECT_EQ(__clzdi2(0x4000000000000000), 1); + EXPECT_EQ(__clzdi2(0x8000000000000000), 0); + + EXPECT_EQ( + __clzdi2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 64); + EXPECT_EQ( + __clzdi2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __clzdi2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 2); + EXPECT_EQ( + __clzdi2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 3); + EXPECT_EQ( + __clzdi2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 4); +} + +TEST(BitTest, __clzti2Test) { + EXPECT_EQ(__clzti2(0x0000000000000000), 64); + EXPECT_EQ(__clzti2(0x0000000000000001), 63); + EXPECT_EQ(__clzti2(0x0000000000000002), 62); + EXPECT_EQ(__clzti2(0x0000000000000004), 61); + EXPECT_EQ(__clzti2(0x0000000000000008), 60); + EXPECT_EQ(__clzti2(0x0000000000000010), 59); + EXPECT_EQ(__clzti2(0x0000000000000020), 58); + EXPECT_EQ(__clzti2(0x0000000000000040), 57); + EXPECT_EQ(__clzti2(0x0000000000000080), 56); + EXPECT_EQ(__clzti2(0x0000000000000100), 55); + EXPECT_EQ(__clzti2(0x0000000000000200), 54); + EXPECT_EQ(__clzti2(0x0000000000000400), 53); + EXPECT_EQ(__clzti2(0x0000000000000800), 52); + EXPECT_EQ(__clzti2(0x0000000000001000), 51); + EXPECT_EQ(__clzti2(0x0000000000002000), 50); + EXPECT_EQ(__clzti2(0x0000000000004000), 49); + EXPECT_EQ(__clzti2(0x0000000000008000), 48); + EXPECT_EQ(__clzti2(0x0000000000010000), 47); + EXPECT_EQ(__clzti2(0x0000000000020000), 46); + EXPECT_EQ(__clzti2(0x0000000000040000), 45); + EXPECT_EQ(__clzti2(0x0000000000080000), 44); + EXPECT_EQ(__clzti2(0x0000000000100000), 43); + EXPECT_EQ(__clzti2(0x0000000000200000), 42); + EXPECT_EQ(__clzti2(0x0000000000400000), 41); + EXPECT_EQ(__clzti2(0x0000000000800000), 40); + EXPECT_EQ(__clzti2(0x0000000001000000), 39); + EXPECT_EQ(__clzti2(0x0000000002000000), 38); + EXPECT_EQ(__clzti2(0x0000000004000000), 37); + EXPECT_EQ(__clzti2(0x0000000008000000), 36); + EXPECT_EQ(__clzti2(0x0000000010000000), 35); + EXPECT_EQ(__clzti2(0x0000000020000000), 34); + EXPECT_EQ(__clzti2(0x0000000040000000), 33); + EXPECT_EQ(__clzti2(0x0000000080000000), 32); + EXPECT_EQ(__clzti2(0x0000000100000000), 31); + EXPECT_EQ(__clzti2(0x0000000200000000), 30); + EXPECT_EQ(__clzti2(0x0000000400000000), 29); + EXPECT_EQ(__clzti2(0x0000000800000000), 28); + EXPECT_EQ(__clzti2(0x0000001000000000), 27); + EXPECT_EQ(__clzti2(0x0000002000000000), 26); + EXPECT_EQ(__clzti2(0x0000004000000000), 25); + EXPECT_EQ(__clzti2(0x0000008000000000), 24); + EXPECT_EQ(__clzti2(0x0000010000000000), 23); + EXPECT_EQ(__clzti2(0x0000020000000000), 22); + EXPECT_EQ(__clzti2(0x0000040000000000), 21); + EXPECT_EQ(__clzti2(0x0000080000000000), 20); + EXPECT_EQ(__clzti2(0x0000100000000000), 19); + EXPECT_EQ(__clzti2(0x0000200000000000), 18); + EXPECT_EQ(__clzti2(0x0000400000000000), 17); + EXPECT_EQ(__clzti2(0x0000800000000000), 16); + EXPECT_EQ(__clzti2(0x0001000000000000), 15); + EXPECT_EQ(__clzti2(0x0002000000000000), 14); + EXPECT_EQ(__clzti2(0x0004000000000000), 13); + EXPECT_EQ(__clzti2(0x0008000000000000), 12); + EXPECT_EQ(__clzti2(0x0010000000000000), 11); + EXPECT_EQ(__clzti2(0x0020000000000000), 10); + EXPECT_EQ(__clzti2(0x0040000000000000), 9); + EXPECT_EQ(__clzti2(0x0080000000000000), 8); + EXPECT_EQ(__clzti2(0x0100000000000000), 7); + EXPECT_EQ(__clzti2(0x0200000000000000), 6); + EXPECT_EQ(__clzti2(0x0400000000000000), 5); + EXPECT_EQ(__clzti2(0x0800000000000000), 4); + EXPECT_EQ(__clzti2(0x1000000000000000), 3); + EXPECT_EQ(__clzti2(0x2000000000000000), 2); + EXPECT_EQ(__clzti2(0x4000000000000000), 1); + EXPECT_EQ(__clzti2(0x8000000000000000), 0); + + EXPECT_EQ( + __clzti2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 64); + EXPECT_EQ( + __clzti2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __clzti2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 2); + EXPECT_EQ( + __clzti2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 3); + EXPECT_EQ( + __clzti2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 4); +} + +TEST(BitTest, __ctzsi2Test) { + EXPECT_EQ(__ctzsi2(0x00000000), 32); + EXPECT_EQ(__ctzsi2(0x00000001), 0); + EXPECT_EQ(__ctzsi2(0x00000002), 1); + EXPECT_EQ(__ctzsi2(0x00000004), 2); + EXPECT_EQ(__ctzsi2(0x00000008), 3); + EXPECT_EQ(__ctzsi2(0x00000010), 4); + EXPECT_EQ(__ctzsi2(0x00000020), 5); + EXPECT_EQ(__ctzsi2(0x00000040), 6); + EXPECT_EQ(__ctzsi2(0x00000080), 7); + EXPECT_EQ(__ctzsi2(0x00000100), 8); + EXPECT_EQ(__ctzsi2(0x00000200), 9); + EXPECT_EQ(__ctzsi2(0x00000400), 10); + EXPECT_EQ(__ctzsi2(0x00000800), 11); + EXPECT_EQ(__ctzsi2(0x00001000), 12); + EXPECT_EQ(__ctzsi2(0x00002000), 13); + EXPECT_EQ(__ctzsi2(0x00004000), 14); + EXPECT_EQ(__ctzsi2(0x00008000), 15); + EXPECT_EQ(__ctzsi2(0x00010000), 16); + EXPECT_EQ(__ctzsi2(0x00020000), 17); + EXPECT_EQ(__ctzsi2(0x00040000), 18); + EXPECT_EQ(__ctzsi2(0x00080000), 19); + EXPECT_EQ(__ctzsi2(0x00100000), 20); + EXPECT_EQ(__ctzsi2(0x00200000), 21); + EXPECT_EQ(__ctzsi2(0x00400000), 22); + EXPECT_EQ(__ctzsi2(0x00800000), 23); + EXPECT_EQ(__ctzsi2(0x01000000), 24); + EXPECT_EQ(__ctzsi2(0x02000000), 25); + EXPECT_EQ(__ctzsi2(0x04000000), 26); + EXPECT_EQ(__ctzsi2(0x08000000), 27); + EXPECT_EQ(__ctzsi2(0x10000000), 28); + EXPECT_EQ(__ctzsi2(0x20000000), 29); + EXPECT_EQ(__ctzsi2(0x40000000), 30); + EXPECT_EQ(__ctzsi2(0x80000000), 31); + + EXPECT_EQ(__ctzsi2(0b00000000000000000000000000000000), 32); + EXPECT_EQ(__ctzsi2(0b01000000001111000000000000000000), 18); + EXPECT_EQ(__ctzsi2(0b00100000000000000000000000000000), 29); + EXPECT_EQ(__ctzsi2(0b00010000000000001111000110000000), 7); + EXPECT_EQ(__ctzsi2(0b00001001001000111000000000000000), 15); +} + +TEST(BitTest, __ctzdi2Test) { + EXPECT_EQ(__ctzdi2(0x0000000000000000), 64); + EXPECT_EQ(__ctzdi2(0x0000000000000001), 0); + EXPECT_EQ(__ctzdi2(0x0000000000000002), 1); + EXPECT_EQ(__ctzdi2(0x0000000000000004), 2); + EXPECT_EQ(__ctzdi2(0x0000000000000008), 3); + EXPECT_EQ(__ctzdi2(0x0000000000000010), 4); + EXPECT_EQ(__ctzdi2(0x0000000000000020), 5); + EXPECT_EQ(__ctzdi2(0x0000000000000040), 6); + EXPECT_EQ(__ctzdi2(0x0000000000000080), 7); + EXPECT_EQ(__ctzdi2(0x0000000000000100), 8); + EXPECT_EQ(__ctzdi2(0x0000000000000200), 9); + EXPECT_EQ(__ctzdi2(0x0000000000000400), 10); + EXPECT_EQ(__ctzdi2(0x0000000000000800), 11); + EXPECT_EQ(__ctzdi2(0x0000000000001000), 12); + EXPECT_EQ(__ctzdi2(0x0000000000002000), 13); + EXPECT_EQ(__ctzdi2(0x0000000000004000), 14); + EXPECT_EQ(__ctzdi2(0x0000000000008000), 15); + EXPECT_EQ(__ctzdi2(0x0000000000010000), 16); + EXPECT_EQ(__ctzdi2(0x0000000000020000), 17); + EXPECT_EQ(__ctzdi2(0x0000000000040000), 18); + EXPECT_EQ(__ctzdi2(0x0000000000080000), 19); + EXPECT_EQ(__ctzdi2(0x0000000000100000), 20); + EXPECT_EQ(__ctzdi2(0x0000000000200000), 21); + EXPECT_EQ(__ctzdi2(0x0000000000400000), 22); + EXPECT_EQ(__ctzdi2(0x0000000000800000), 23); + EXPECT_EQ(__ctzdi2(0x0000000001000000), 24); + EXPECT_EQ(__ctzdi2(0x0000000002000000), 25); + EXPECT_EQ(__ctzdi2(0x0000000004000000), 26); + EXPECT_EQ(__ctzdi2(0x0000000008000000), 27); + EXPECT_EQ(__ctzdi2(0x0000000010000000), 28); + EXPECT_EQ(__ctzdi2(0x0000000020000000), 29); + EXPECT_EQ(__ctzdi2(0x0000000040000000), 30); + EXPECT_EQ(__ctzdi2(0x0000000080000000), 31); + EXPECT_EQ(__ctzdi2(0x0000000100000000), 32); + EXPECT_EQ(__ctzdi2(0x0000000200000000), 33); + EXPECT_EQ(__ctzdi2(0x0000000400000000), 34); + EXPECT_EQ(__ctzdi2(0x0000000800000000), 35); + EXPECT_EQ(__ctzdi2(0x0000001000000000), 36); + EXPECT_EQ(__ctzdi2(0x0000002000000000), 37); + EXPECT_EQ(__ctzdi2(0x0000004000000000), 38); + EXPECT_EQ(__ctzdi2(0x0000008000000000), 39); + EXPECT_EQ(__ctzdi2(0x0000010000000000), 40); + EXPECT_EQ(__ctzdi2(0x0000020000000000), 41); + EXPECT_EQ(__ctzdi2(0x0000040000000000), 42); + EXPECT_EQ(__ctzdi2(0x0000080000000000), 43); + EXPECT_EQ(__ctzdi2(0x0000100000000000), 44); + EXPECT_EQ(__ctzdi2(0x0000200000000000), 45); + EXPECT_EQ(__ctzdi2(0x0000400000000000), 46); + EXPECT_EQ(__ctzdi2(0x0000800000000000), 47); + EXPECT_EQ(__ctzdi2(0x0001000000000000), 48); + EXPECT_EQ(__ctzdi2(0x0002000000000000), 49); + EXPECT_EQ(__ctzdi2(0x0004000000000000), 50); + EXPECT_EQ(__ctzdi2(0x0008000000000000), 51); + EXPECT_EQ(__ctzdi2(0x0010000000000000), 52); + EXPECT_EQ(__ctzdi2(0x0020000000000000), 53); + EXPECT_EQ(__ctzdi2(0x0040000000000000), 54); + EXPECT_EQ(__ctzdi2(0x0080000000000000), 55); + EXPECT_EQ(__ctzdi2(0x0100000000000000), 56); + EXPECT_EQ(__ctzdi2(0x0200000000000000), 57); + EXPECT_EQ(__ctzdi2(0x0400000000000000), 58); + EXPECT_EQ(__ctzdi2(0x0800000000000000), 59); + EXPECT_EQ(__ctzdi2(0x1000000000000000), 60); + EXPECT_EQ(__ctzdi2(0x2000000000000000), 61); + EXPECT_EQ(__ctzdi2(0x4000000000000000), 62); + EXPECT_EQ(__ctzdi2(0x8000000000000000), 63); + + EXPECT_EQ( + __ctzdi2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 64); + EXPECT_EQ( + __ctzdi2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 50); + EXPECT_EQ( + __ctzdi2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 61); + EXPECT_EQ( + __ctzdi2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 39); + EXPECT_EQ( + __ctzdi2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 47); +} + +TEST(BitTest, __ctzti2Test) { + EXPECT_EQ(__ctzti2(0x0000000000000000), 64); + EXPECT_EQ(__ctzti2(0x0000000000000001), 0); + EXPECT_EQ(__ctzti2(0x0000000000000002), 1); + EXPECT_EQ(__ctzti2(0x0000000000000004), 2); + EXPECT_EQ(__ctzti2(0x0000000000000008), 3); + EXPECT_EQ(__ctzti2(0x0000000000000010), 4); + EXPECT_EQ(__ctzti2(0x0000000000000020), 5); + EXPECT_EQ(__ctzti2(0x0000000000000040), 6); + EXPECT_EQ(__ctzti2(0x0000000000000080), 7); + EXPECT_EQ(__ctzti2(0x0000000000000100), 8); + EXPECT_EQ(__ctzti2(0x0000000000000200), 9); + EXPECT_EQ(__ctzti2(0x0000000000000400), 10); + EXPECT_EQ(__ctzti2(0x0000000000000800), 11); + EXPECT_EQ(__ctzti2(0x0000000000001000), 12); + EXPECT_EQ(__ctzti2(0x0000000000002000), 13); + EXPECT_EQ(__ctzti2(0x0000000000004000), 14); + EXPECT_EQ(__ctzti2(0x0000000000008000), 15); + EXPECT_EQ(__ctzti2(0x0000000000010000), 16); + EXPECT_EQ(__ctzti2(0x0000000000020000), 17); + EXPECT_EQ(__ctzti2(0x0000000000040000), 18); + EXPECT_EQ(__ctzti2(0x0000000000080000), 19); + EXPECT_EQ(__ctzti2(0x0000000000100000), 20); + EXPECT_EQ(__ctzti2(0x0000000000200000), 21); + EXPECT_EQ(__ctzti2(0x0000000000400000), 22); + EXPECT_EQ(__ctzti2(0x0000000000800000), 23); + EXPECT_EQ(__ctzti2(0x0000000001000000), 24); + EXPECT_EQ(__ctzti2(0x0000000002000000), 25); + EXPECT_EQ(__ctzti2(0x0000000004000000), 26); + EXPECT_EQ(__ctzti2(0x0000000008000000), 27); + EXPECT_EQ(__ctzti2(0x0000000010000000), 28); + EXPECT_EQ(__ctzti2(0x0000000020000000), 29); + EXPECT_EQ(__ctzti2(0x0000000040000000), 30); + EXPECT_EQ(__ctzti2(0x0000000080000000), 31); + EXPECT_EQ(__ctzti2(0x0000000100000000), 32); + EXPECT_EQ(__ctzti2(0x0000000200000000), 33); + EXPECT_EQ(__ctzti2(0x0000000400000000), 34); + EXPECT_EQ(__ctzti2(0x0000000800000000), 35); + EXPECT_EQ(__ctzti2(0x0000001000000000), 36); + EXPECT_EQ(__ctzti2(0x0000002000000000), 37); + EXPECT_EQ(__ctzti2(0x0000004000000000), 38); + EXPECT_EQ(__ctzti2(0x0000008000000000), 39); + EXPECT_EQ(__ctzti2(0x0000010000000000), 40); + EXPECT_EQ(__ctzti2(0x0000020000000000), 41); + EXPECT_EQ(__ctzti2(0x0000040000000000), 42); + EXPECT_EQ(__ctzti2(0x0000080000000000), 43); + EXPECT_EQ(__ctzti2(0x0000100000000000), 44); + EXPECT_EQ(__ctzti2(0x0000200000000000), 45); + EXPECT_EQ(__ctzti2(0x0000400000000000), 46); + EXPECT_EQ(__ctzti2(0x0000800000000000), 47); + EXPECT_EQ(__ctzti2(0x0001000000000000), 48); + EXPECT_EQ(__ctzti2(0x0002000000000000), 49); + EXPECT_EQ(__ctzti2(0x0004000000000000), 50); + EXPECT_EQ(__ctzti2(0x0008000000000000), 51); + EXPECT_EQ(__ctzti2(0x0010000000000000), 52); + EXPECT_EQ(__ctzti2(0x0020000000000000), 53); + EXPECT_EQ(__ctzti2(0x0040000000000000), 54); + EXPECT_EQ(__ctzti2(0x0080000000000000), 55); + EXPECT_EQ(__ctzti2(0x0100000000000000), 56); + EXPECT_EQ(__ctzti2(0x0200000000000000), 57); + EXPECT_EQ(__ctzti2(0x0400000000000000), 58); + EXPECT_EQ(__ctzti2(0x0800000000000000), 59); + EXPECT_EQ(__ctzti2(0x1000000000000000), 60); + EXPECT_EQ(__ctzti2(0x2000000000000000), 61); + EXPECT_EQ(__ctzti2(0x4000000000000000), 62); + EXPECT_EQ(__ctzti2(0x8000000000000000), 63); + + EXPECT_EQ( + __ctzti2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 64); + EXPECT_EQ( + __ctzti2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 50); + EXPECT_EQ( + __ctzti2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 61); + EXPECT_EQ( + __ctzti2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 39); + EXPECT_EQ( + __ctzti2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 47); +} + +TEST(BitTest, __ffsdi2Test) { + EXPECT_EQ(__ffsdi2(0x0000000000000000), 0); + EXPECT_EQ(__ffsdi2(0x0000000000000001), 1); + EXPECT_EQ(__ffsdi2(0x0000000000000002), 2); + EXPECT_EQ(__ffsdi2(0x0000000000000004), 3); + EXPECT_EQ(__ffsdi2(0x0000000000000008), 4); + EXPECT_EQ(__ffsdi2(0x0000000000000010), 5); + EXPECT_EQ(__ffsdi2(0x0000000000000020), 6); + EXPECT_EQ(__ffsdi2(0x0000000000000040), 7); + EXPECT_EQ(__ffsdi2(0x0000000000000080), 8); + EXPECT_EQ(__ffsdi2(0x0000000000000100), 9); + EXPECT_EQ(__ffsdi2(0x0000000000000200), 10); + EXPECT_EQ(__ffsdi2(0x0000000000000400), 11); + EXPECT_EQ(__ffsdi2(0x0000000000000800), 12); + EXPECT_EQ(__ffsdi2(0x0000000000001000), 13); + EXPECT_EQ(__ffsdi2(0x0000000000002000), 14); + EXPECT_EQ(__ffsdi2(0x0000000000004000), 15); + EXPECT_EQ(__ffsdi2(0x0000000000008000), 16); + EXPECT_EQ(__ffsdi2(0x0000000000010000), 17); + EXPECT_EQ(__ffsdi2(0x0000000000020000), 18); + EXPECT_EQ(__ffsdi2(0x0000000000040000), 19); + EXPECT_EQ(__ffsdi2(0x0000000000080000), 20); + EXPECT_EQ(__ffsdi2(0x0000000000100000), 21); + EXPECT_EQ(__ffsdi2(0x0000000000200000), 22); + EXPECT_EQ(__ffsdi2(0x0000000000400000), 23); + EXPECT_EQ(__ffsdi2(0x0000000000800000), 24); + EXPECT_EQ(__ffsdi2(0x0000000001000000), 25); + EXPECT_EQ(__ffsdi2(0x0000000002000000), 26); + EXPECT_EQ(__ffsdi2(0x0000000004000000), 27); + EXPECT_EQ(__ffsdi2(0x0000000008000000), 28); + EXPECT_EQ(__ffsdi2(0x0000000010000000), 29); + EXPECT_EQ(__ffsdi2(0x0000000020000000), 30); + EXPECT_EQ(__ffsdi2(0x0000000040000000), 31); + EXPECT_EQ(__ffsdi2(0x0000000080000000), 32); + EXPECT_EQ(__ffsdi2(0x0000000100000000), 33); + EXPECT_EQ(__ffsdi2(0x0000000200000000), 34); + EXPECT_EQ(__ffsdi2(0x0000000400000000), 35); + EXPECT_EQ(__ffsdi2(0x0000000800000000), 36); + EXPECT_EQ(__ffsdi2(0x0000001000000000), 37); + EXPECT_EQ(__ffsdi2(0x0000002000000000), 38); + EXPECT_EQ(__ffsdi2(0x0000004000000000), 39); + EXPECT_EQ(__ffsdi2(0x0000008000000000), 40); + EXPECT_EQ(__ffsdi2(0x0000010000000000), 41); + EXPECT_EQ(__ffsdi2(0x0000020000000000), 42); + EXPECT_EQ(__ffsdi2(0x0000040000000000), 43); + EXPECT_EQ(__ffsdi2(0x0000080000000000), 44); + EXPECT_EQ(__ffsdi2(0x0000100000000000), 45); + EXPECT_EQ(__ffsdi2(0x0000200000000000), 46); + EXPECT_EQ(__ffsdi2(0x0000400000000000), 47); + EXPECT_EQ(__ffsdi2(0x0000800000000000), 48); + EXPECT_EQ(__ffsdi2(0x0001000000000000), 49); + EXPECT_EQ(__ffsdi2(0x0002000000000000), 50); + EXPECT_EQ(__ffsdi2(0x0004000000000000), 51); + EXPECT_EQ(__ffsdi2(0x0008000000000000), 52); + EXPECT_EQ(__ffsdi2(0x0010000000000000), 53); + EXPECT_EQ(__ffsdi2(0x0020000000000000), 54); + EXPECT_EQ(__ffsdi2(0x0040000000000000), 55); + EXPECT_EQ(__ffsdi2(0x0080000000000000), 56); + EXPECT_EQ(__ffsdi2(0x0100000000000000), 57); + EXPECT_EQ(__ffsdi2(0x0200000000000000), 58); + EXPECT_EQ(__ffsdi2(0x0400000000000000), 59); + EXPECT_EQ(__ffsdi2(0x0800000000000000), 60); + EXPECT_EQ(__ffsdi2(0x1000000000000000), 61); + EXPECT_EQ(__ffsdi2(0x2000000000000000), 62); + EXPECT_EQ(__ffsdi2(0x4000000000000000), 63); + EXPECT_EQ(__ffsdi2(0x8000000000000000), 64); + + EXPECT_EQ( + __ffsdi2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 0); + EXPECT_EQ( + __ffsdi2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 51); + EXPECT_EQ( + __ffsdi2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 62); + EXPECT_EQ( + __ffsdi2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 40); + EXPECT_EQ( + __ffsdi2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 48); +} + +TEST(BitTest, __ffsti2Test) { + EXPECT_EQ(__ffsti2(0x0000000000000000), 0); + EXPECT_EQ(__ffsti2(0x0000000000000001), 1); + EXPECT_EQ(__ffsti2(0x0000000000000002), 2); + EXPECT_EQ(__ffsti2(0x0000000000000004), 3); + EXPECT_EQ(__ffsti2(0x0000000000000008), 4); + EXPECT_EQ(__ffsti2(0x0000000000000010), 5); + EXPECT_EQ(__ffsti2(0x0000000000000020), 6); + EXPECT_EQ(__ffsti2(0x0000000000000040), 7); + EXPECT_EQ(__ffsti2(0x0000000000000080), 8); + EXPECT_EQ(__ffsti2(0x0000000000000100), 9); + EXPECT_EQ(__ffsti2(0x0000000000000200), 10); + EXPECT_EQ(__ffsti2(0x0000000000000400), 11); + EXPECT_EQ(__ffsti2(0x0000000000000800), 12); + EXPECT_EQ(__ffsti2(0x0000000000001000), 13); + EXPECT_EQ(__ffsti2(0x0000000000002000), 14); + EXPECT_EQ(__ffsti2(0x0000000000004000), 15); + EXPECT_EQ(__ffsti2(0x0000000000008000), 16); + EXPECT_EQ(__ffsti2(0x0000000000010000), 17); + EXPECT_EQ(__ffsti2(0x0000000000020000), 18); + EXPECT_EQ(__ffsti2(0x0000000000040000), 19); + EXPECT_EQ(__ffsti2(0x0000000000080000), 20); + EXPECT_EQ(__ffsti2(0x0000000000100000), 21); + EXPECT_EQ(__ffsti2(0x0000000000200000), 22); + EXPECT_EQ(__ffsti2(0x0000000000400000), 23); + EXPECT_EQ(__ffsti2(0x0000000000800000), 24); + EXPECT_EQ(__ffsti2(0x0000000001000000), 25); + EXPECT_EQ(__ffsti2(0x0000000002000000), 26); + EXPECT_EQ(__ffsti2(0x0000000004000000), 27); + EXPECT_EQ(__ffsti2(0x0000000008000000), 28); + EXPECT_EQ(__ffsti2(0x0000000010000000), 29); + EXPECT_EQ(__ffsti2(0x0000000020000000), 30); + EXPECT_EQ(__ffsti2(0x0000000040000000), 31); + EXPECT_EQ(__ffsti2(0x0000000080000000), 32); + EXPECT_EQ(__ffsti2(0x0000000100000000), 33); + EXPECT_EQ(__ffsti2(0x0000000200000000), 34); + EXPECT_EQ(__ffsti2(0x0000000400000000), 35); + EXPECT_EQ(__ffsti2(0x0000000800000000), 36); + EXPECT_EQ(__ffsti2(0x0000001000000000), 37); + EXPECT_EQ(__ffsti2(0x0000002000000000), 38); + EXPECT_EQ(__ffsti2(0x0000004000000000), 39); + EXPECT_EQ(__ffsti2(0x0000008000000000), 40); + EXPECT_EQ(__ffsti2(0x0000010000000000), 41); + EXPECT_EQ(__ffsti2(0x0000020000000000), 42); + EXPECT_EQ(__ffsti2(0x0000040000000000), 43); + EXPECT_EQ(__ffsti2(0x0000080000000000), 44); + EXPECT_EQ(__ffsti2(0x0000100000000000), 45); + EXPECT_EQ(__ffsti2(0x0000200000000000), 46); + EXPECT_EQ(__ffsti2(0x0000400000000000), 47); + EXPECT_EQ(__ffsti2(0x0000800000000000), 48); + EXPECT_EQ(__ffsti2(0x0001000000000000), 49); + EXPECT_EQ(__ffsti2(0x0002000000000000), 50); + EXPECT_EQ(__ffsti2(0x0004000000000000), 51); + EXPECT_EQ(__ffsti2(0x0008000000000000), 52); + EXPECT_EQ(__ffsti2(0x0010000000000000), 53); + EXPECT_EQ(__ffsti2(0x0020000000000000), 54); + EXPECT_EQ(__ffsti2(0x0040000000000000), 55); + EXPECT_EQ(__ffsti2(0x0080000000000000), 56); + EXPECT_EQ(__ffsti2(0x0100000000000000), 57); + EXPECT_EQ(__ffsti2(0x0200000000000000), 58); + EXPECT_EQ(__ffsti2(0x0400000000000000), 59); + EXPECT_EQ(__ffsti2(0x0800000000000000), 60); + EXPECT_EQ(__ffsti2(0x1000000000000000), 61); + EXPECT_EQ(__ffsti2(0x2000000000000000), 62); + EXPECT_EQ(__ffsti2(0x4000000000000000), 63); + EXPECT_EQ(__ffsti2(0x8000000000000000), 64); + + EXPECT_EQ( + __ffsti2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 0); + EXPECT_EQ( + __ffsti2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 51); + EXPECT_EQ( + __ffsti2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 62); + EXPECT_EQ( + __ffsti2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 40); + EXPECT_EQ( + __ffsti2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 48); +} + +TEST(BitTest, __paritysi2Test) { + EXPECT_EQ(__paritysi2(0x00000000), 0); + EXPECT_EQ(__paritysi2(0x00000001), 1); + EXPECT_EQ(__paritysi2(0x00000002), 1); + EXPECT_EQ(__paritysi2(0x00000004), 1); + EXPECT_EQ(__paritysi2(0x00000008), 1); + EXPECT_EQ(__paritysi2(0x00000010), 1); + EXPECT_EQ(__paritysi2(0x00000020), 1); + EXPECT_EQ(__paritysi2(0x00000040), 1); + EXPECT_EQ(__paritysi2(0x00000080), 1); + EXPECT_EQ(__paritysi2(0x00000100), 1); + EXPECT_EQ(__paritysi2(0x00000200), 1); + EXPECT_EQ(__paritysi2(0x00000400), 1); + EXPECT_EQ(__paritysi2(0x00000800), 1); + EXPECT_EQ(__paritysi2(0x00001000), 1); + EXPECT_EQ(__paritysi2(0x00002000), 1); + EXPECT_EQ(__paritysi2(0x00004000), 1); + EXPECT_EQ(__paritysi2(0x00008000), 1); + EXPECT_EQ(__paritysi2(0x00010000), 1); + EXPECT_EQ(__paritysi2(0x00020000), 1); + EXPECT_EQ(__paritysi2(0x00040000), 1); + EXPECT_EQ(__paritysi2(0x00080000), 1); + EXPECT_EQ(__paritysi2(0x00100000), 1); + EXPECT_EQ(__paritysi2(0x00200000), 1); + EXPECT_EQ(__paritysi2(0x00400000), 1); + EXPECT_EQ(__paritysi2(0x00800000), 1); + EXPECT_EQ(__paritysi2(0x01000000), 1); + EXPECT_EQ(__paritysi2(0x02000000), 1); + EXPECT_EQ(__paritysi2(0x04000000), 1); + EXPECT_EQ(__paritysi2(0x08000000), 1); + EXPECT_EQ(__paritysi2(0x10000000), 1); + EXPECT_EQ(__paritysi2(0x20000000), 1); + EXPECT_EQ(__paritysi2(0x40000000), 1); + EXPECT_EQ(__paritysi2(0x80000000), 1); + + EXPECT_EQ(__paritysi2(0b00000000000000000000000000000000), 0); + EXPECT_EQ(__paritysi2(0b01000000001111000000000000000000), 1); + EXPECT_EQ(__paritysi2(0b00100000000000000000000000000000), 1); + EXPECT_EQ(__paritysi2(0b00010000000000001111000110000000), 1); + EXPECT_EQ(__paritysi2(0b00001001001000111000000000000000), 0); +} + +TEST(BitTest, __paritydi2Test) { + EXPECT_EQ(__paritydi2(0x0000000000000000), 0); + EXPECT_EQ(__paritydi2(0x0000000000000001), 1); + EXPECT_EQ(__paritydi2(0x0000000000000002), 1); + EXPECT_EQ(__paritydi2(0x0000000000000004), 1); + EXPECT_EQ(__paritydi2(0x0000000000000008), 1); + EXPECT_EQ(__paritydi2(0x0000000000000010), 1); + EXPECT_EQ(__paritydi2(0x0000000000000020), 1); + EXPECT_EQ(__paritydi2(0x0000000000000040), 1); + EXPECT_EQ(__paritydi2(0x0000000000000080), 1); + EXPECT_EQ(__paritydi2(0x0000000000000100), 1); + EXPECT_EQ(__paritydi2(0x0000000000000200), 1); + EXPECT_EQ(__paritydi2(0x0000000000000400), 1); + EXPECT_EQ(__paritydi2(0x0000000000000800), 1); + EXPECT_EQ(__paritydi2(0x0000000000001000), 1); + EXPECT_EQ(__paritydi2(0x0000000000002000), 1); + EXPECT_EQ(__paritydi2(0x0000000000004000), 1); + EXPECT_EQ(__paritydi2(0x0000000000008000), 1); + EXPECT_EQ(__paritydi2(0x0000000000010000), 1); + EXPECT_EQ(__paritydi2(0x0000000000020000), 1); + EXPECT_EQ(__paritydi2(0x0000000000040000), 1); + EXPECT_EQ(__paritydi2(0x0000000000080000), 1); + EXPECT_EQ(__paritydi2(0x0000000000100000), 1); + EXPECT_EQ(__paritydi2(0x0000000000200000), 1); + EXPECT_EQ(__paritydi2(0x0000000000400000), 1); + EXPECT_EQ(__paritydi2(0x0000000000800000), 1); + EXPECT_EQ(__paritydi2(0x0000000001000000), 1); + EXPECT_EQ(__paritydi2(0x0000000002000000), 1); + EXPECT_EQ(__paritydi2(0x0000000004000000), 1); + EXPECT_EQ(__paritydi2(0x0000000008000000), 1); + EXPECT_EQ(__paritydi2(0x0000000010000000), 1); + EXPECT_EQ(__paritydi2(0x0000000020000000), 1); + EXPECT_EQ(__paritydi2(0x0000000040000000), 1); + EXPECT_EQ(__paritydi2(0x0000000080000000), 1); + EXPECT_EQ(__paritydi2(0x0000000100000000), 1); + EXPECT_EQ(__paritydi2(0x0000000200000000), 1); + EXPECT_EQ(__paritydi2(0x0000000400000000), 1); + EXPECT_EQ(__paritydi2(0x0000000800000000), 1); + EXPECT_EQ(__paritydi2(0x0000001000000000), 1); + EXPECT_EQ(__paritydi2(0x0000002000000000), 1); + EXPECT_EQ(__paritydi2(0x0000004000000000), 1); + EXPECT_EQ(__paritydi2(0x0000008000000000), 1); + EXPECT_EQ(__paritydi2(0x0000010000000000), 1); + EXPECT_EQ(__paritydi2(0x0000020000000000), 1); + EXPECT_EQ(__paritydi2(0x0000040000000000), 1); + EXPECT_EQ(__paritydi2(0x0000080000000000), 1); + EXPECT_EQ(__paritydi2(0x0000100000000000), 1); + EXPECT_EQ(__paritydi2(0x0000200000000000), 1); + EXPECT_EQ(__paritydi2(0x0000400000000000), 1); + EXPECT_EQ(__paritydi2(0x0000800000000000), 1); + EXPECT_EQ(__paritydi2(0x0001000000000000), 1); + EXPECT_EQ(__paritydi2(0x0002000000000000), 1); + EXPECT_EQ(__paritydi2(0x0004000000000000), 1); + EXPECT_EQ(__paritydi2(0x0008000000000000), 1); + EXPECT_EQ(__paritydi2(0x0010000000000000), 1); + EXPECT_EQ(__paritydi2(0x0020000000000000), 1); + EXPECT_EQ(__paritydi2(0x0040000000000000), 1); + EXPECT_EQ(__paritydi2(0x0080000000000000), 1); + EXPECT_EQ(__paritydi2(0x0100000000000000), 1); + EXPECT_EQ(__paritydi2(0x0200000000000000), 1); + EXPECT_EQ(__paritydi2(0x0400000000000000), 1); + EXPECT_EQ(__paritydi2(0x0800000000000000), 1); + EXPECT_EQ(__paritydi2(0x1000000000000000), 1); + EXPECT_EQ(__paritydi2(0x2000000000000000), 1); + EXPECT_EQ(__paritydi2(0x4000000000000000), 1); + EXPECT_EQ(__paritydi2(0x8000000000000000), 1); + + EXPECT_EQ( + __paritydi2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 0); + EXPECT_EQ( + __paritydi2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __paritydi2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __paritydi2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __paritydi2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 0); +} + +TEST(BitTest, __parityti2Test) { + EXPECT_EQ(__parityti2(0x0000000000000000), 0); + EXPECT_EQ(__parityti2(0x0000000000000001), 1); + EXPECT_EQ(__parityti2(0x0000000000000002), 1); + EXPECT_EQ(__parityti2(0x0000000000000004), 1); + EXPECT_EQ(__parityti2(0x0000000000000008), 1); + EXPECT_EQ(__parityti2(0x0000000000000010), 1); + EXPECT_EQ(__parityti2(0x0000000000000020), 1); + EXPECT_EQ(__parityti2(0x0000000000000040), 1); + EXPECT_EQ(__parityti2(0x0000000000000080), 1); + EXPECT_EQ(__parityti2(0x0000000000000100), 1); + EXPECT_EQ(__parityti2(0x0000000000000200), 1); + EXPECT_EQ(__parityti2(0x0000000000000400), 1); + EXPECT_EQ(__parityti2(0x0000000000000800), 1); + EXPECT_EQ(__parityti2(0x0000000000001000), 1); + EXPECT_EQ(__parityti2(0x0000000000002000), 1); + EXPECT_EQ(__parityti2(0x0000000000004000), 1); + EXPECT_EQ(__parityti2(0x0000000000008000), 1); + EXPECT_EQ(__parityti2(0x0000000000010000), 1); + EXPECT_EQ(__parityti2(0x0000000000020000), 1); + EXPECT_EQ(__parityti2(0x0000000000040000), 1); + EXPECT_EQ(__parityti2(0x0000000000080000), 1); + EXPECT_EQ(__parityti2(0x0000000000100000), 1); + EXPECT_EQ(__parityti2(0x0000000000200000), 1); + EXPECT_EQ(__parityti2(0x0000000000400000), 1); + EXPECT_EQ(__parityti2(0x0000000000800000), 1); + EXPECT_EQ(__parityti2(0x0000000001000000), 1); + EXPECT_EQ(__parityti2(0x0000000002000000), 1); + EXPECT_EQ(__parityti2(0x0000000004000000), 1); + EXPECT_EQ(__parityti2(0x0000000008000000), 1); + EXPECT_EQ(__parityti2(0x0000000010000000), 1); + EXPECT_EQ(__parityti2(0x0000000020000000), 1); + EXPECT_EQ(__parityti2(0x0000000040000000), 1); + EXPECT_EQ(__parityti2(0x0000000080000000), 1); + EXPECT_EQ(__parityti2(0x0000000100000000), 1); + EXPECT_EQ(__parityti2(0x0000000200000000), 1); + EXPECT_EQ(__parityti2(0x0000000400000000), 1); + EXPECT_EQ(__parityti2(0x0000000800000000), 1); + EXPECT_EQ(__parityti2(0x0000001000000000), 1); + EXPECT_EQ(__parityti2(0x0000002000000000), 1); + EXPECT_EQ(__parityti2(0x0000004000000000), 1); + EXPECT_EQ(__parityti2(0x0000008000000000), 1); + EXPECT_EQ(__parityti2(0x0000010000000000), 1); + EXPECT_EQ(__parityti2(0x0000020000000000), 1); + EXPECT_EQ(__parityti2(0x0000040000000000), 1); + EXPECT_EQ(__parityti2(0x0000080000000000), 1); + EXPECT_EQ(__parityti2(0x0000100000000000), 1); + EXPECT_EQ(__parityti2(0x0000200000000000), 1); + EXPECT_EQ(__parityti2(0x0000400000000000), 1); + EXPECT_EQ(__parityti2(0x0000800000000000), 1); + EXPECT_EQ(__parityti2(0x0001000000000000), 1); + EXPECT_EQ(__parityti2(0x0002000000000000), 1); + EXPECT_EQ(__parityti2(0x0004000000000000), 1); + EXPECT_EQ(__parityti2(0x0008000000000000), 1); + EXPECT_EQ(__parityti2(0x0010000000000000), 1); + EXPECT_EQ(__parityti2(0x0020000000000000), 1); + EXPECT_EQ(__parityti2(0x0040000000000000), 1); + EXPECT_EQ(__parityti2(0x0080000000000000), 1); + EXPECT_EQ(__parityti2(0x0100000000000000), 1); + EXPECT_EQ(__parityti2(0x0200000000000000), 1); + EXPECT_EQ(__parityti2(0x0400000000000000), 1); + EXPECT_EQ(__parityti2(0x0800000000000000), 1); + EXPECT_EQ(__parityti2(0x1000000000000000), 1); + EXPECT_EQ(__parityti2(0x2000000000000000), 1); + EXPECT_EQ(__parityti2(0x4000000000000000), 1); + EXPECT_EQ(__parityti2(0x8000000000000000), 1); + + EXPECT_EQ( + __parityti2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 0); + EXPECT_EQ( + __parityti2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __parityti2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __parityti2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __parityti2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 0); +} + +TEST(BitTest, __popcountsi2Test) { + EXPECT_EQ(__popcountsi2(0x00000000), 0); + EXPECT_EQ(__popcountsi2(0x00000001), 1); + EXPECT_EQ(__popcountsi2(0x00000002), 1); + EXPECT_EQ(__popcountsi2(0x00000004), 1); + EXPECT_EQ(__popcountsi2(0x00000008), 1); + EXPECT_EQ(__popcountsi2(0x00000010), 1); + EXPECT_EQ(__popcountsi2(0x00000020), 1); + EXPECT_EQ(__popcountsi2(0x00000040), 1); + EXPECT_EQ(__popcountsi2(0x00000080), 1); + EXPECT_EQ(__popcountsi2(0x00000100), 1); + EXPECT_EQ(__popcountsi2(0x00000200), 1); + EXPECT_EQ(__popcountsi2(0x00000400), 1); + EXPECT_EQ(__popcountsi2(0x00000800), 1); + EXPECT_EQ(__popcountsi2(0x00001000), 1); + EXPECT_EQ(__popcountsi2(0x00002000), 1); + EXPECT_EQ(__popcountsi2(0x00004000), 1); + EXPECT_EQ(__popcountsi2(0x00008000), 1); + EXPECT_EQ(__popcountsi2(0x00010000), 1); + EXPECT_EQ(__popcountsi2(0x00020000), 1); + EXPECT_EQ(__popcountsi2(0x00040000), 1); + EXPECT_EQ(__popcountsi2(0x00080000), 1); + EXPECT_EQ(__popcountsi2(0x00100000), 1); + EXPECT_EQ(__popcountsi2(0x00200000), 1); + EXPECT_EQ(__popcountsi2(0x00400000), 1); + EXPECT_EQ(__popcountsi2(0x00800000), 1); + EXPECT_EQ(__popcountsi2(0x01000000), 1); + EXPECT_EQ(__popcountsi2(0x02000000), 1); + EXPECT_EQ(__popcountsi2(0x04000000), 1); + EXPECT_EQ(__popcountsi2(0x08000000), 1); + EXPECT_EQ(__popcountsi2(0x10000000), 1); + EXPECT_EQ(__popcountsi2(0x20000000), 1); + EXPECT_EQ(__popcountsi2(0x40000000), 1); + EXPECT_EQ(__popcountsi2(0x80000000), 1); + + EXPECT_EQ(__popcountsi2(0b00000000000000000000000000000000), 0); + EXPECT_EQ(__popcountsi2(0b01000000001111000000000000000000), 5); + EXPECT_EQ(__popcountsi2(0b00100000000000000000000000000000), 1); + EXPECT_EQ(__popcountsi2(0b00010000000000001111000110000000), 7); + EXPECT_EQ(__popcountsi2(0b00001001001000111000000000000000), 6); +} + +TEST(BitTest, __popcountdi2Test) { + EXPECT_EQ(__popcountdi2(0x0000000000000000), 0); + EXPECT_EQ(__popcountdi2(0x0000000000000001), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000002), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000004), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000008), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000010), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000020), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000040), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000080), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000100), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000200), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000400), 1); + EXPECT_EQ(__popcountdi2(0x0000000000000800), 1); + EXPECT_EQ(__popcountdi2(0x0000000000001000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000002000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000004000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000008000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000010000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000020000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000040000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000080000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000100000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000200000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000400000), 1); + EXPECT_EQ(__popcountdi2(0x0000000000800000), 1); + EXPECT_EQ(__popcountdi2(0x0000000001000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000002000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000004000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000008000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000010000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000020000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000040000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000080000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000100000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000200000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000400000000), 1); + EXPECT_EQ(__popcountdi2(0x0000000800000000), 1); + EXPECT_EQ(__popcountdi2(0x0000001000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000002000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000004000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000008000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000010000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000020000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000040000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000080000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000100000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000200000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000400000000000), 1); + EXPECT_EQ(__popcountdi2(0x0000800000000000), 1); + EXPECT_EQ(__popcountdi2(0x0001000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0002000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0004000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0008000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0010000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0020000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0040000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0080000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0100000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0200000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0400000000000000), 1); + EXPECT_EQ(__popcountdi2(0x0800000000000000), 1); + EXPECT_EQ(__popcountdi2(0x1000000000000000), 1); + EXPECT_EQ(__popcountdi2(0x2000000000000000), 1); + EXPECT_EQ(__popcountdi2(0x4000000000000000), 1); + EXPECT_EQ(__popcountdi2(0x8000000000000000), 1); + + EXPECT_EQ( + __popcountdi2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 0); + EXPECT_EQ( + __popcountdi2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 5); + EXPECT_EQ( + __popcountdi2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __popcountdi2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 7); + EXPECT_EQ( + __popcountdi2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 6); +} + +TEST(BitTest, __popcountti2Test) { + EXPECT_EQ(__popcountti2(0x0000000000000000), 0); + EXPECT_EQ(__popcountti2(0x0000000000000001), 1); + EXPECT_EQ(__popcountti2(0x0000000000000002), 1); + EXPECT_EQ(__popcountti2(0x0000000000000004), 1); + EXPECT_EQ(__popcountti2(0x0000000000000008), 1); + EXPECT_EQ(__popcountti2(0x0000000000000010), 1); + EXPECT_EQ(__popcountti2(0x0000000000000020), 1); + EXPECT_EQ(__popcountti2(0x0000000000000040), 1); + EXPECT_EQ(__popcountti2(0x0000000000000080), 1); + EXPECT_EQ(__popcountti2(0x0000000000000100), 1); + EXPECT_EQ(__popcountti2(0x0000000000000200), 1); + EXPECT_EQ(__popcountti2(0x0000000000000400), 1); + EXPECT_EQ(__popcountti2(0x0000000000000800), 1); + EXPECT_EQ(__popcountti2(0x0000000000001000), 1); + EXPECT_EQ(__popcountti2(0x0000000000002000), 1); + EXPECT_EQ(__popcountti2(0x0000000000004000), 1); + EXPECT_EQ(__popcountti2(0x0000000000008000), 1); + EXPECT_EQ(__popcountti2(0x0000000000010000), 1); + EXPECT_EQ(__popcountti2(0x0000000000020000), 1); + EXPECT_EQ(__popcountti2(0x0000000000040000), 1); + EXPECT_EQ(__popcountti2(0x0000000000080000), 1); + EXPECT_EQ(__popcountti2(0x0000000000100000), 1); + EXPECT_EQ(__popcountti2(0x0000000000200000), 1); + EXPECT_EQ(__popcountti2(0x0000000000400000), 1); + EXPECT_EQ(__popcountti2(0x0000000000800000), 1); + EXPECT_EQ(__popcountti2(0x0000000001000000), 1); + EXPECT_EQ(__popcountti2(0x0000000002000000), 1); + EXPECT_EQ(__popcountti2(0x0000000004000000), 1); + EXPECT_EQ(__popcountti2(0x0000000008000000), 1); + EXPECT_EQ(__popcountti2(0x0000000010000000), 1); + EXPECT_EQ(__popcountti2(0x0000000020000000), 1); + EXPECT_EQ(__popcountti2(0x0000000040000000), 1); + EXPECT_EQ(__popcountti2(0x0000000080000000), 1); + EXPECT_EQ(__popcountti2(0x0000000100000000), 1); + EXPECT_EQ(__popcountti2(0x0000000200000000), 1); + EXPECT_EQ(__popcountti2(0x0000000400000000), 1); + EXPECT_EQ(__popcountti2(0x0000000800000000), 1); + EXPECT_EQ(__popcountti2(0x0000001000000000), 1); + EXPECT_EQ(__popcountti2(0x0000002000000000), 1); + EXPECT_EQ(__popcountti2(0x0000004000000000), 1); + EXPECT_EQ(__popcountti2(0x0000008000000000), 1); + EXPECT_EQ(__popcountti2(0x0000010000000000), 1); + EXPECT_EQ(__popcountti2(0x0000020000000000), 1); + EXPECT_EQ(__popcountti2(0x0000040000000000), 1); + EXPECT_EQ(__popcountti2(0x0000080000000000), 1); + EXPECT_EQ(__popcountti2(0x0000100000000000), 1); + EXPECT_EQ(__popcountti2(0x0000200000000000), 1); + EXPECT_EQ(__popcountti2(0x0000400000000000), 1); + EXPECT_EQ(__popcountti2(0x0000800000000000), 1); + EXPECT_EQ(__popcountti2(0x0001000000000000), 1); + EXPECT_EQ(__popcountti2(0x0002000000000000), 1); + EXPECT_EQ(__popcountti2(0x0004000000000000), 1); + EXPECT_EQ(__popcountti2(0x0008000000000000), 1); + EXPECT_EQ(__popcountti2(0x0010000000000000), 1); + EXPECT_EQ(__popcountti2(0x0020000000000000), 1); + EXPECT_EQ(__popcountti2(0x0040000000000000), 1); + EXPECT_EQ(__popcountti2(0x0080000000000000), 1); + EXPECT_EQ(__popcountti2(0x0100000000000000), 1); + EXPECT_EQ(__popcountti2(0x0200000000000000), 1); + EXPECT_EQ(__popcountti2(0x0400000000000000), 1); + EXPECT_EQ(__popcountti2(0x0800000000000000), 1); + EXPECT_EQ(__popcountti2(0x1000000000000000), 1); + EXPECT_EQ(__popcountti2(0x2000000000000000), 1); + EXPECT_EQ(__popcountti2(0x4000000000000000), 1); + EXPECT_EQ(__popcountti2(0x8000000000000000), 1); + + EXPECT_EQ( + __popcountti2( + 0b0000000000000000000000000000000000000000000000000000000000000000), + 0); + EXPECT_EQ( + __popcountti2( + 0b0100000000111100000000000000000000000000000000000000000000000000), + 5); + EXPECT_EQ( + __popcountti2( + 0b0010000000000000000000000000000000000000000000000000000000000000), + 1); + EXPECT_EQ( + __popcountti2( + 0b0001000000000000111100011000000000000000000000000000000000000000), + 7); + EXPECT_EQ( + __popcountti2( + 0b0000100100100011100000000000000000000000000000000000000000000000), + 6); +} + +TEST(BitTest, __bswapsi2Test) { + EXPECT_EQ(__bswapsi2(0x00000000), 0x00000000); + EXPECT_EQ(__bswapsi2(0x00000001), 0x01000000); + EXPECT_EQ(__bswapsi2(0x00000002), 0x02000000); + EXPECT_EQ(__bswapsi2(0x00000004), 0x04000000); + EXPECT_EQ(__bswapsi2(0x00000008), 0x08000000); + EXPECT_EQ(__bswapsi2(0x00000010), 0x10000000); + EXPECT_EQ(__bswapsi2(0x00000020), 0x20000000); + EXPECT_EQ(__bswapsi2(0x00000040), 0x40000000); + EXPECT_EQ(__bswapsi2(0x00000080), 0x80000000); + EXPECT_EQ(__bswapsi2(0x00000100), 0x00010000); + EXPECT_EQ(__bswapsi2(0x00000200), 0x00020000); + EXPECT_EQ(__bswapsi2(0x00000400), 0x00040000); + EXPECT_EQ(__bswapsi2(0x00000800), 0x00080000); + EXPECT_EQ(__bswapsi2(0x00001000), 0x00100000); + EXPECT_EQ(__bswapsi2(0x00002000), 0x00200000); + EXPECT_EQ(__bswapsi2(0x00004000), 0x00400000); + EXPECT_EQ(__bswapsi2(0x00008000), 0x00800000); + EXPECT_EQ(__bswapsi2(0x00010000), 0x00000100); + EXPECT_EQ(__bswapsi2(0x00020000), 0x00000200); + EXPECT_EQ(__bswapsi2(0x00040000), 0x00000400); + EXPECT_EQ(__bswapsi2(0x00080000), 0x00000800); + EXPECT_EQ(__bswapsi2(0x00100000), 0x00001000); + EXPECT_EQ(__bswapsi2(0x00200000), 0x00002000); + EXPECT_EQ(__bswapsi2(0x00400000), 0x00004000); + EXPECT_EQ(__bswapsi2(0x00800000), 0x00008000); + EXPECT_EQ(__bswapsi2(0x01000000), 0x00000001); + EXPECT_EQ(__bswapsi2(0x02000000), 0x00000002); + EXPECT_EQ(__bswapsi2(0x04000000), 0x00000004); + EXPECT_EQ(__bswapsi2(0x08000000), 0x00000008); + EXPECT_EQ(__bswapsi2(0x10000000), 0x00000010); + EXPECT_EQ(__bswapsi2(0x20000000), 0x00000020); + EXPECT_EQ(__bswapsi2(0x40000000), 0x00000040); + EXPECT_EQ(__bswapsi2(0x80000000), 0x00000080); +} + +TEST(BitTest, __bswapdi2Test) { + EXPECT_EQ(__bswapdi2(0x0000000000000000), 0x0000000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000001), 0x0100000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000002), 0x0200000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000004), 0x0400000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000008), 0x0800000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000010), 0x1000000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000020), 0x2000000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000040), 0x4000000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000080), 0x8000000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000100), 0x0001000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000200), 0x0002000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000400), 0x0004000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000000800), 0x0008000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000001000), 0x0010000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000002000), 0x0020000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000004000), 0x0040000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000008000), 0x0080000000000000); + EXPECT_EQ(__bswapdi2(0x0000000000010000), 0x0000010000000000); + EXPECT_EQ(__bswapdi2(0x0000000000020000), 0x0000020000000000); + EXPECT_EQ(__bswapdi2(0x0000000000040000), 0x0000040000000000); + EXPECT_EQ(__bswapdi2(0x0000000000080000), 0x0000080000000000); + EXPECT_EQ(__bswapdi2(0x0000000000100000), 0x0000100000000000); + EXPECT_EQ(__bswapdi2(0x0000000000200000), 0x0000200000000000); + EXPECT_EQ(__bswapdi2(0x0000000000400000), 0x0000400000000000); + EXPECT_EQ(__bswapdi2(0x0000000000800000), 0x0000800000000000); + EXPECT_EQ(__bswapdi2(0x0000000001000000), 0x0000000100000000); + EXPECT_EQ(__bswapdi2(0x0000000002000000), 0x0000000200000000); + EXPECT_EQ(__bswapdi2(0x0000000004000000), 0x0000000400000000); + EXPECT_EQ(__bswapdi2(0x0000000008000000), 0x0000000800000000); + EXPECT_EQ(__bswapdi2(0x0000000010000000), 0x0000001000000000); + EXPECT_EQ(__bswapdi2(0x0000000020000000), 0x0000002000000000); + EXPECT_EQ(__bswapdi2(0x0000000040000000), 0x0000004000000000); + EXPECT_EQ(__bswapdi2(0x0000000080000000), 0x0000008000000000); + EXPECT_EQ(__bswapdi2(0x0000000100000000), 0x0000000001000000); + EXPECT_EQ(__bswapdi2(0x0000000200000000), 0x0000000002000000); + EXPECT_EQ(__bswapdi2(0x0000000400000000), 0x0000000004000000); + EXPECT_EQ(__bswapdi2(0x0000000800000000), 0x0000000008000000); + EXPECT_EQ(__bswapdi2(0x0000001000000000), 0x0000000010000000); + EXPECT_EQ(__bswapdi2(0x0000002000000000), 0x0000000020000000); + EXPECT_EQ(__bswapdi2(0x0000004000000000), 0x0000000040000000); + EXPECT_EQ(__bswapdi2(0x0000008000000000), 0x0000000080000000); + EXPECT_EQ(__bswapdi2(0x0000010000000000), 0x0000000000010000); + EXPECT_EQ(__bswapdi2(0x0000020000000000), 0x0000000000020000); + EXPECT_EQ(__bswapdi2(0x0000040000000000), 0x0000000000040000); + EXPECT_EQ(__bswapdi2(0x0000080000000000), 0x0000000000080000); + EXPECT_EQ(__bswapdi2(0x0000100000000000), 0x0000000000100000); + EXPECT_EQ(__bswapdi2(0x0000200000000000), 0x0000000000200000); + EXPECT_EQ(__bswapdi2(0x0000400000000000), 0x0000000000400000); + EXPECT_EQ(__bswapdi2(0x0000800000000000), 0x0000000000800000); + EXPECT_EQ(__bswapdi2(0x0001000000000000), 0x0000000000000100); + EXPECT_EQ(__bswapdi2(0x0002000000000000), 0x0000000000000200); + EXPECT_EQ(__bswapdi2(0x0004000000000000), 0x0000000000000400); + EXPECT_EQ(__bswapdi2(0x0008000000000000), 0x0000000000000800); + EXPECT_EQ(__bswapdi2(0x0010000000000000), 0x0000000000001000); + EXPECT_EQ(__bswapdi2(0x0020000000000000), 0x0000000000002000); + EXPECT_EQ(__bswapdi2(0x0040000000000000), 0x0000000000004000); + EXPECT_EQ(__bswapdi2(0x0080000000000000), 0x0000000000008000); + EXPECT_EQ(__bswapdi2(0x0100000000000000), 0x0000000000000001); + EXPECT_EQ(__bswapdi2(0x0200000000000000), 0x0000000000000002); + EXPECT_EQ(__bswapdi2(0x0400000000000000), 0x0000000000000004); + EXPECT_EQ(__bswapdi2(0x0800000000000000), 0x0000000000000008); + EXPECT_EQ(__bswapdi2(0x1000000000000000), 0x0000000000000010); + EXPECT_EQ(__bswapdi2(0x2000000000000000), 0x0000000000000020); + EXPECT_EQ(__bswapdi2(0x4000000000000000), 0x0000000000000040); + EXPECT_EQ(__bswapdi2(0x8000000000000000), 0x0000000000000080); +} From c876f6a041766f942048178957d5196ae4c3ef3a Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Sun, 7 Jul 2024 17:39:20 +0800 Subject: [PATCH 03/17] feat(memory): add mem Signed-off-by: Zone.N --- src/kernel/CMakeLists.txt | 1 + src/kernel/firstfit_allocator.cpp | 129 ++++++++++++ src/kernel/include/kernel.h | 4 +- src/kernel/include/memory/allocator_base.hpp | 100 +++++++++ .../include/memory/firstfit_allocator.h | 88 ++++++++ .../include/memory/physical_memory_manager.h | 196 ++++++++++++++++++ src/kernel/main.cpp | 2 + src/kernel/physical_memory_manager.cpp | 192 +++++++++++++++++ test/unit_test/CMakeLists.txt | 3 + test/unit_test/firstfit_allocator_test.cpp | 49 +++++ 10 files changed, 763 insertions(+), 1 deletion(-) create mode 100644 src/kernel/firstfit_allocator.cpp create mode 100644 src/kernel/include/memory/allocator_base.hpp create mode 100644 src/kernel/include/memory/firstfit_allocator.h create mode 100644 src/kernel/include/memory/physical_memory_manager.h create mode 100644 src/kernel/physical_memory_manager.cpp create mode 100644 test/unit_test/firstfit_allocator_test.cpp diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index b400b25ec..06abfd4f9 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/driver) add_executable(${PROJECT_NAME} main.cpp firstfit_allocator.cpp + physical_memory_manager.cpp ) # 添加头文件 diff --git a/src/kernel/firstfit_allocator.cpp b/src/kernel/firstfit_allocator.cpp new file mode 100644 index 000000000..8e9039b34 --- /dev/null +++ b/src/kernel/firstfit_allocator.cpp @@ -0,0 +1,129 @@ + +/** + * @file firstfit_allocator.cpp + * @brief firstfit 内存分配器实现 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#include "memory/firstfit_allocator.h" + +#include +#include +#include + +#include "kernel_log.hpp" + +FirstFitAllocator::FirstFitAllocator(const char* name, uint64_t addr, + size_t pages_count) + : AllocatorBase(name, addr, pages_count) { + if (addr % PAGE_SIZE != 0) { + Err("addr not aligned. 0x%lX\n", addr); + throw; + } + printf("%s: 0x%p(0x%X pages) init.\n", name_, addr_, length_); +} + +uint64_t FirstFitAllocator::Alloc(size_t pages_count) { + uint64_t res_addr = 0; + // 在位图中寻找连续 pages_count 的位置 + auto [is_found, idx] = Find(pages_count, false); + if (is_found == false) { + Warn("NO ENOUGH MEM %d.\n", pages_count); + return res_addr; + } + // 遍历区域 + for (auto i = idx; i < idx + pages_count; i++) { + // 置位,说明已使用 + map[i] = 1; + } + // 计算实际地址 + // 分配器起始地址+页长度*第几页 + res_addr = addr_ + (PAGE_SIZE * idx); + // 更新统计信息 + free_length_ -= pages_count; + used_length_ += pages_count; + return res_addr; +} + +bool FirstFitAllocator::AllocAt(uint64_t addr, size_t pages_count) { + // 页对齐 + if (addr % PAGE_SIZE != 0) { + Warn("addr not aligned 0x%lX.\n", addr); + return false; + } + // 申请地址超出范围 + if (addr < addr_ || addr > addr_ || + addr + pages_count * PAGE_SIZE > addr_ + length_ * PAGE_SIZE) { + Warn("out of range 0x%lX %d.\n", addr, pages_count); + return false; + } + // 计算 addr 在 map 中的索引 + size_t idx = (addr - addr_) / PAGE_SIZE; + // 遍历 + for (auto i = idx; i < idx + pages_count; i++) { + // 如果在范围内有已经分配的内存,返回 false + if (map[i] == true) { + return false; + } + } + // 到这里说明范围内没有已使用内存 + // 再次遍历 + for (auto i = idx; i < idx + pages_count; i++) { + // 置位 + map[i] = 1; + } + // 更新统计信息 + free_length_ -= pages_count; + used_length_ += pages_count; + return true; +} + +void FirstFitAllocator::Free(uint64_t addr, size_t pages_count) { + // 页对齐 + if (addr % PAGE_SIZE != 0) { + Warn("addr not aligned 0x%lX.\n", addr); + return; + } + // 申请地址超出范围 + if (addr < addr_ || addr > addr_ || + addr + pages_count * PAGE_SIZE > addr_ + length_ * PAGE_SIZE) { + Warn("out of range 0x%lX %d.\n", addr, pages_count); + return; + } + // 计算 addr 在 map 中的索引 + size_t idx = (addr - addr_) / PAGE_SIZE; + for (auto i = idx; i < idx + pages_count; i++) { + map[i] = 0; + } + // 更新统计信息 + free_length_ += pages_count; + used_length_ -= pages_count; +} + +std::pair FirstFitAllocator::Find(size_t pages_count, + bool val) const { + size_t count = 0; + size_t idx = 0; + // 遍历位图 + for (uint64_t i = 0; i < length_; i++) { + if (map[i] != val) { + count = 0; + idx = i + 1; + } else { + count++; + } + if (count == pages_count) { + return {true, idx}; + } + } + return {false, 0}; +} diff --git a/src/kernel/include/kernel.h b/src/kernel/include/kernel.h index 32994a3ce..03b987fa9 100644 --- a/src/kernel/include/kernel.h +++ b/src/kernel/include/kernel.h @@ -87,7 +87,7 @@ extern "C" [[maybe_unused]] [[noreturn]] void _start(uint32_t argc, /** * @brief 内核入口 - * @param argc + * @param argc * riscv64: 启动核 id * x86_64: 参数个数 * @param argv 参数指针 @@ -99,6 +99,8 @@ uint32_t main(uint32_t argc, uint8_t* argv); uint32_t InterruptInit(uint32_t argc, uint8_t* argv); +uint32_t PhysicalMemoryInit(uint32_t argc, uint8_t* argv); + /// 保存内核基本信息 [[maybe_unused]] static Singleton kBasicInfo; diff --git a/src/kernel/include/memory/allocator_base.hpp b/src/kernel/include/memory/allocator_base.hpp new file mode 100644 index 000000000..8a0e09306 --- /dev/null +++ b/src/kernel/include/memory/allocator_base.hpp @@ -0,0 +1,100 @@ + +/** + * @file allocator_base.hpp + * @brief 内存分配器基类 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#ifndef SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_ALLOCATOR_BASE_HPP_ +#define SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_ALLOCATOR_BASE_HPP_ + +#include +#include + +/** + * @brief 内存分配器基类 + */ +class AllocatorBase { + public: + /** + * @brief 构造函数 + * @param name 分配器名 + * @param addr 要管理的内存开始地址 + * @param length 要管理的内存长度,单位由派生类型指定 + */ + explicit AllocatorBase(const char *name, uint64_t addr, size_t length) { + name_ = name; + addr_ = addr; + length_ = length; + free_length_ = length; + used_length_ = 0; + } + + /// @name 构造/析构函数 + /// @{ + AllocatorBase() = default; + AllocatorBase(const AllocatorBase &) = default; + AllocatorBase(AllocatorBase &&) = default; + auto operator=(const AllocatorBase &) -> AllocatorBase & = default; + auto operator=(AllocatorBase &&) -> AllocatorBase & = default; + virtual ~AllocatorBase() = default; + /// @} + + /** + * @brief 分配 length 内存 + * @param length 要申请的内存长度 + * @return uint64_t 分配到的地址 + */ + virtual uint64_t Alloc(size_t length) = 0; + + /** + * @brief 在指定地址分配 length 长度 + * @param addr 指定的地址 + * @param length 长度 + * @return true 成功 + * @return false 失败 + */ + virtual bool AllocAt(uint64_t addr, size_t length) = 0; + + /** + * @brief 释放内存 + * @param addr 地址 + * @param length 长度 + */ + virtual void Free(uint64_t addr, size_t length) = 0; + + /** + * @brief 已使用内存数量 + * @return size_t 数量 + */ + virtual size_t GetUsedCount() const { return used_length_; } + + /** + * @brief 空闲内存数量 + * @return size_t 数量 + */ + virtual size_t GetFreeCount() const { return free_length_; } + + protected: + /// 分配器名称 + const char *name_; + /// 当前管理的内存区域地址 + uint64_t addr_; + /// 当前管理的内存区域长度 + size_t length_; + /// 空闲内存数量 + size_t free_length_; + /// 已使用内存数量 + size_t used_length_; +}; + +#endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_ALLOCATOR_BASE_HPP_ */ diff --git a/src/kernel/include/memory/firstfit_allocator.h b/src/kernel/include/memory/firstfit_allocator.h new file mode 100644 index 000000000..0c088b16a --- /dev/null +++ b/src/kernel/include/memory/firstfit_allocator.h @@ -0,0 +1,88 @@ + +/** + * @file firstfit_allocator.h + * @brief firstfit 内存分配器头文件 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#ifndef SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_BASE_H_ +#define SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_BASE_H_ + +#include +#include +#include + +#include "memory/allocator_base.hpp" + +/** + * @brief 使用 first fit 算法的分配器 + */ +class FirstFitAllocator : AllocatorBase { + public: + /** + * @brief 构造函数 + * @param name 分配器名称 + * @param addr 开始地址 + * @param pages_count 页数量 + */ + explicit FirstFitAllocator(const char *name, uint64_t addr, + size_t pages_count); + + /// @name 构造/析构函数 + /// @{ + FirstFitAllocator() = default; + FirstFitAllocator(const FirstFitAllocator &) = default; + FirstFitAllocator(FirstFitAllocator &&) = default; + auto operator=(const FirstFitAllocator &) -> FirstFitAllocator & = default; + auto operator=(FirstFitAllocator &&) -> FirstFitAllocator & = default; + ~FirstFitAllocator() = default; + /// @} + + /** + * @brief 分配长度为 pages_count 页的内存 + * @param pages_count 页数 + * @return uint64_t 分配的内存起点地址 + */ + uint64_t Alloc(size_t pages_count) override; + + /** + * @brief 在 addr 处分配长度为 pages_count 页的内存 + * @param addr 指定的地址 + * @param pages_count 页数 + * @return true 成功 + * @return false 失败 + */ + bool AllocAt(uint64_t addr, size_t pages_count) override; + + /** + * @brief 释放 addr 处 pages_count 页的内存 + * @param addr 要释放内存起点地址 + * @param pages_count 页数 + */ + void Free(uint64_t addr, size_t pages_count) override; + + private: + static constexpr const uint64_t PAGE_SIZE = 0x1000; + + /// 位图,每一位表示一页内存,1 表示已使用,0 表示未使用 + std::bitset map; + + /** + * @brief 寻找连续 pages_count 个 val 位,返回开始索引 + * @param pages_count 连续页数量 + * @param val 要寻找的页状态 + * @return std::pair 找到返回 ,失败返回 + */ + std::pair Find(size_t pages_count, bool val) const; +}; + +#endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_BASE_H_ */ diff --git a/src/kernel/include/memory/physical_memory_manager.h b/src/kernel/include/memory/physical_memory_manager.h new file mode 100644 index 000000000..aae121304 --- /dev/null +++ b/src/kernel/include/memory/physical_memory_manager.h @@ -0,0 +1,196 @@ + +/** + * @file physical_memory_manager.h + * @brief 物理内存管理 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#ifndef SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_PHYSICAL_MEMORY_MANAGER_H_ +#define SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_PHYSICAL_MEMORY_MANAGER_H_ + +#include +#include + +#include "memory/allocator_base.hpp" +#include "singleton.hpp" + +/** + * @brief 物理内存管理接口 + * 对物理内存的管理来说 + * 1. 管理所有的物理内存,不论是否被机器保留/无法访问 + * 2. 内存开始地址与长度由 bootloader 给出: x86 下为 grub, riscv 下为 opensbi + * 3. + * 不关心内存是否被使用,但是默认的物理内存分配空间从内核结束后开始 + * 如果由体系结构需要分配内核开始前内存空间的,则尽量避免 + * 4. 最管理单位为页 + */ +class PhysicalMemoryManager { + public: + /** + * @brief 构造函数 + * @param name 分配器名 + * @param addr 要管理的内存开始地址 + * @param length 要管理的内存长度,单位由派生类型指定 + */ + explicit PhysicalMemoryManager(const char *name, uint64_t addr, + size_t length) { + ; + } + + /// @name 构造/析构函数 + /// @{ + PhysicalMemoryManager() = default; + PhysicalMemoryManager(const PhysicalMemoryManager &) = default; + PhysicalMemoryManager(PhysicalMemoryManager &&) = default; + auto operator=(const PhysicalMemoryManager &) -> PhysicalMemoryManager & = + default; + auto operator=(PhysicalMemoryManager &&) -> PhysicalMemoryManager & = default; + ~PhysicalMemoryManager() = default; + /// @} + + /** + * @brief 初始化 + * @return true 成功 + * @return false 失败 + * @todo 移动到构造函数去 + */ + bool init(void); + + /** + * @brief 获取物理内存长度 + * @return size_t 物理内存长度 + */ + size_t get_pmm_length(void) const; + + /** + * @brief 获取内核空间起始地址 + * @return uintptr_t 内核空间起始地址 + */ + uintptr_t get_kernel_space_start(void) const; + + /** + * @brief 获取内核空间大小,单位为 byte + * @return size_t 内核空间大小 + */ + size_t get_kernel_space_length(void) const; + + /** + * @brief 获取非内核空间起始地址 + * @return uintptr_t 非内核空间起始地址 + */ + uintptr_t get_non_kernel_space_start(void) const; + + /** + * @brief 获取非内核空间大小,单位为 byte + * @return size_t 非内核空间大小 + */ + size_t get_non_kernel_space_length(void) const; + + /** + * @brief 获取当前已使用页数 + * @return size_t 已使用页数 + */ + size_t get_used_pages_count(void) const; + + /** + * @brief 获取当前空闲页 + * @return size_t 空闲页数 + */ + size_t get_free_pages_count(void) const; + + /** + * @brief 分配一页 + * @return uintptr_t 分配的内存起始地址 + */ + uintptr_t alloc_page(void); + + /** + * @brief 分配多页 + * @param _len 页数 + * @return uintptr_t 分配的内存起始地址 + */ + uintptr_t alloc_pages(size_t _len); + + /** + * @brief 分配以指定地址开始的 _len 页 + * @param _addr 指定的地址 + * @param _len 页数 + * @return true 成功 + * @return false 失败 + */ + bool alloc_pages(uintptr_t _addr, size_t _len); + + /** + * @brief 在内核空间申请一页 + * @return uintptr_t 分配的内存起始地址 + */ + uintptr_t alloc_page_kernel(void); + + /** + * @brief 在内核空间分配 _len 页 + * @param _len 页数 + * @return uintptr_t 分配到的内存起始地址 + */ + uintptr_t alloc_pages_kernel(size_t _len); + + /** + * @brief 在内核空间分配以指定地址开始的 _len 页 + * @param _addr 指定的地址 + * @param _len 页数 + * @return true 成功 + * @return false 失败 + */ + bool alloc_pages_kernel(uintptr_t _addr, size_t _len); + + /** + * @brief 回收一页 + * @param _addr 要回收的地址 + */ + void free_page(uintptr_t _addr); + + /** + * @brief 回收多页 + * @param _addr 要回收的地址 + * @param _len 页数 + */ + void free_pages(uintptr_t _addr, size_t _len); + + private: + /// 物理内存开始地址 + uintptr_t addr_; + /// 物理内存页数 + size_t pages_count; + /// 内核空间起始地址 + uintptr_t kernel_addr; + /// 内核页数 + size_t kernel_pages_count; + /// 用户空间起始地址 + uintptr_t user_start; + /// 用户空间页数 + size_t user_pages_count; + + /// 内核空间不会位于内存中间,导致出现非内核空间被切割为两部分的情况 + /// 物理内存分配器,分配内核空间 + AllocatorBase *kernel_allocator; + /// 物理内存分配器,分配用户空间 + AllocatorBase *user_allocator; + + /** + * @brief 将 multiboot2/dtb 信息移动到内核空间 + */ + void move_boot_info(void); +}; + +/// 全局物理内存管理器 +[[maybe_unused]] static Singleton kPhysicalMemoryManager; + +#endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_PHYSICAL_MEMORY_MANAGER_H_ */ diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 03c2972c3..4618790f7 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -48,6 +48,8 @@ uint32_t main(uint32_t argc, uint8_t *argv) { InterruptInit(argc, argv); + PhysicalMemoryInit(argc, argv); + // 进入死循环 while (1) { for (uint64_t i = 0; i < 99999999; i++) { diff --git a/src/kernel/physical_memory_manager.cpp b/src/kernel/physical_memory_manager.cpp new file mode 100644 index 000000000..2d93877de --- /dev/null +++ b/src/kernel/physical_memory_manager.cpp @@ -0,0 +1,192 @@ + +/** + * @file physical_memory_manager.cpp + * @brief 物理内存管理 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#include "memory/physical_memory_manager.h" + +#include "kernel_log.hpp" +// #include "boot_info.h" +// #include "cassert" +// #include "common.h" +#include +#include + +#include "memory/firstfit_allocator.h" +// #include "resource.h" + +// bool PhysicalMemoryManager::init(void) { +// // 获取物理内存信息 +// resource_t mem_info = BOOT_INFO::get_memory(); +// // 设置物理地址的起点与长度 +// start = mem_info.mem.addr; +// length = mem_info.mem.len; +// // 计算页数 +// total_pages = length / COMMON::PAGE_SIZE; +// // 内核空间地址开始 +// kernel_space_start = COMMON::KERNEL_START_ADDR; +// // 长度手动指定 +// kernel_space_length = COMMON::KERNEL_SPACE_SIZE; +// // 非内核空间在内核空间结束后 +// non_kernel_space_start = +// COMMON::KERNEL_START_ADDR + COMMON::KERNEL_SPACE_SIZE; +// // 长度为总长度减去内核长度 +// non_kernel_space_length = length - kernel_space_length; + +// // 创建分配器 +// // 内核空间 +// static FIRSTFIT first_fit_allocator_kernel( +// "First Fit Allocator(kernel space)", kernel_space_start, +// kernel_space_length / COMMON::PAGE_SIZE); +// kernel_space_allocator = (ALLOCATOR*)&first_fit_allocator_kernel; +// // 非内核空间 +// static FIRSTFIT first_fit_allocator( +// "First Fit Allocator", non_kernel_space_start, +// non_kernel_space_length / COMMON::PAGE_SIZE); +// allocator = (ALLOCATOR*)&first_fit_allocator; + +// // 内核实际占用页数 这里也算了 0~1M 的 reserved 内存 +// size_t kernel_pages = +// (COMMON::ALIGN(COMMON::KERNEL_END_ADDR, COMMON::PAGE_SIZE) - +// COMMON::ALIGN(COMMON::KERNEL_START_ADDR, COMMON::PAGE_SIZE)) / +// COMMON::PAGE_SIZE; +// // 将内核已使用部分划分出来 +// if (alloc_pages_kernel(COMMON::KERNEL_START_ADDR, kernel_pages) == true) { +// // 将 multiboot2/dtb 信息移动到内核空间 +// move_boot_info(); +// info("pmm init.\n"); +// return true; +// } else { +// assert(0); +// return false; +// } +// } + +// size_t PhysicalMemoryManager::get_pmm_length(void) const { return length; } + +// uintptr_t PhysicalMemoryManager::get_kernel_space_start(void) const { +// return kernel_space_start; +// } + +// size_t PhysicalMemoryManager::get_kernel_space_length(void) const { +// return kernel_space_length; +// } + +// uintptr_t PhysicalMemoryManager::get_non_kernel_space_start(void) const { +// return non_kernel_space_start; +// } + +// size_t PhysicalMemoryManager::get_non_kernel_space_length(void) const { +// return non_kernel_space_length; +// } + +// size_t PhysicalMemoryManager::get_used_pages_count(void) const { +// size_t ret = +// kernel_space_allocator->get_used_count() + allocator->get_used_count(); +// return ret; +// } + +// size_t PhysicalMemoryManager::get_free_pages_count(void) const { +// size_t ret = +// kernel_space_allocator->get_free_count() + allocator->get_free_count(); +// return ret; +// } + +// uintptr_t PhysicalMemoryManager::alloc_page(void) { +// uintptr_t ret = allocator->alloc(1); +// return ret; +// } + +// uintptr_t PhysicalMemoryManager::alloc_pages(size_t _len) { +// uintptr_t ret = allocator->alloc(_len); +// return ret; +// } + +// bool PhysicalMemoryManager::alloc_pages(uintptr_t _addr, size_t _len) { +// bool ret = allocator->alloc(_addr, _len); +// return ret; +// } + +// uintptr_t PhysicalMemoryManager::alloc_page_kernel(void) { +// uintptr_t ret = kernel_space_allocator->alloc(1); +// return ret; +// } + +// uintptr_t PhysicalMemoryManager::alloc_pages_kernel(size_t _len) { +// uintptr_t ret = kernel_space_allocator->alloc(_len); +// return ret; +// } + +// bool PhysicalMemoryManager::alloc_pages_kernel(uintptr_t _addr, size_t _len) +// { +// bool ret = kernel_space_allocator->alloc(_addr, _len); +// return ret; +// } + +// void PhysicalMemoryManager::free_page(uintptr_t _addr) { +// // 判断应该使用哪个分配器 +// if (_addr >= kernel_space_start && +// _addr < kernel_space_start + kernel_space_length) { +// kernel_space_allocator->free(_addr, 1); +// } else if (_addr >= non_kernel_space_start && +// _addr < non_kernel_space_start + non_kernel_space_length) { +// allocator->free(_addr, 1); +// } else { +// // 如果都不是说明有问题 +// assert(0); +// } +// return; +// } + +// void PhysicalMemoryManager::free_pages(uintptr_t _addr, size_t _len) { +// // 判断应该使用哪个分配器 +// if (_addr >= kernel_space_start && +// _addr < kernel_space_start + kernel_space_length) { +// kernel_space_allocator->free(_addr, _len); +// } else if (_addr >= non_kernel_space_start && +// _addr < non_kernel_space_start + non_kernel_space_length) { +// allocator->free(_addr, _len); +// } +// // 如果都不是说明有问题 +// else { +// assert(0); +// } +// return; +// } + +// // 将启动信息移动到内核空间 +// void PhysicalMemoryManager::move_boot_info(void) { +// // 计算 multiboot2 信息需要多少页 +// size_t pages = BOOT_INFO::boot_info_size / COMMON::PAGE_SIZE; +// if (BOOT_INFO::boot_info_size % COMMON::PAGE_SIZE != 0) { +// pages++; +// } +// // 申请空间 +// uintptr_t new_addr = get_instance().alloc_pages_kernel(pages); +// // 复制过来,完成后以前的内存就可以使用了 +// memcpy((void*)new_addr, (void*)BOOT_INFO::boot_info_addr, +// pages * COMMON::PAGE_SIZE); +// // 设置地址 +// BOOT_INFO::boot_info_addr = (uintptr_t)new_addr; +// // 重新初始化 +// BOOT_INFO::init(); +// return; +// } + +uint32_t PhysicalMemoryInit(uint32_t argc, uint8_t* argv) { + // 初始化物理内存管理器 + kPhysicalMemoryManager.GetInstance(); + + Info("Hello PhysicalMemoryInit\n"); +} diff --git a/test/unit_test/CMakeLists.txt b/test/unit_test/CMakeLists.txt index c3af0fcc4..13073b8c6 100644 --- a/test/unit_test/CMakeLists.txt +++ b/test/unit_test/CMakeLists.txt @@ -19,6 +19,9 @@ add_executable(${PROJECT_NAME} kernel_elf_test.cpp kernel_fdt_test.cpp libc_bit_test.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/../../src/kernel/firstfit_allocator.cpp + firstfit_allocator_test.cpp ) add_header_arch(${PROJECT_NAME}) diff --git a/test/unit_test/firstfit_allocator_test.cpp b/test/unit_test/firstfit_allocator_test.cpp new file mode 100644 index 000000000..7ba4b45db --- /dev/null +++ b/test/unit_test/firstfit_allocator_test.cpp @@ -0,0 +1,49 @@ + +/** + * @file firstfit_allocator_test.cpp + * @brief firstfit_allocator 相关测试 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2023-09-02 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2023-09-02Zone.N创建文件 + *
+ */ + +#include "memory/firstfit_allocator.h" + +#include + +TEST(FirstFitAllocatorTest, AllocAtTest) { + auto allocator = FirstFitAllocator("AllocAtTest", 0, 0x1000); + EXPECT_EQ(allocator.AllocAt(0, 0x1001), 0); + EXPECT_EQ(allocator.AllocAt(0, 0x1000), 1); + EXPECT_EQ(allocator.AllocAt(0, 0x1000), 0); + allocator.Free(0, 0x1000); + EXPECT_EQ(allocator.AllocAt(0, 0xFFF), 1); + EXPECT_EQ(allocator.AllocAt(0x1000, 1), 0); +} + +TEST(FirstFitAllocatorTest, AllocTest) { + auto allocator = FirstFitAllocator("AllocTest", 0x1000, 0x1000); + auto addr1 = allocator.Alloc(0x1001); + EXPECT_EQ(addr1, 0); + + auto addr2 = allocator.Alloc(0x1000); + EXPECT_EQ(addr2, 0x1000); + + auto addr3 = allocator.Alloc(0x1000); + EXPECT_EQ(addr3, 0); + + allocator.Free(addr2, 0x1000); + + auto addr4 = allocator.Alloc(0xFFF); + EXPECT_EQ(addr4, 0x1000); + + auto addr5 = allocator.Alloc(1); + EXPECT_EQ(addr5, 0x1000 + 0xFFF * 0x1000); +} From 4b7f4b51b42ed4ab0f547748b4d3a32313650f14 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Mon, 8 Jul 2024 19:12:05 +0800 Subject: [PATCH 04/17] feat(memory): update Signed-off-by: Zone.N --- src/kernel/CMakeLists.txt | 1 + .../include/memory/physical_memory_manager.h | 123 ++++---- src/kernel/physical_memory_manager.cpp | 298 ++++++++---------- src/project_config.h | 6 + tools/project_config.h.in | 6 + 5 files changed, 205 insertions(+), 229 deletions(-) diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index 06abfd4f9..6fa82505a 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(${PROJECT_NAME} ) # 添加头文件 +add_header_project(${PROJECT_NAME}) add_header_libc(${PROJECT_NAME}) add_header_libcxx(${PROJECT_NAME}) add_header_arch(${PROJECT_NAME}) diff --git a/src/kernel/include/memory/physical_memory_manager.h b/src/kernel/include/memory/physical_memory_manager.h index aae121304..74663627a 100644 --- a/src/kernel/include/memory/physical_memory_manager.h +++ b/src/kernel/include/memory/physical_memory_manager.h @@ -37,14 +37,10 @@ class PhysicalMemoryManager { public: /** * @brief 构造函数 - * @param name 分配器名 - * @param addr 要管理的内存开始地址 - * @param length 要管理的内存长度,单位由派生类型指定 + * @param addr 物理地址起点 + * @param pages_count 物理页数 */ - explicit PhysicalMemoryManager(const char *name, uint64_t addr, - size_t length) { - ; - } + explicit PhysicalMemoryManager(uint64_t addr, size_t pages_count); /// @name 构造/析构函数 /// @{ @@ -58,136 +54,129 @@ class PhysicalMemoryManager { /// @} /** - * @brief 初始化 - * @return true 成功 - * @return false 失败 - * @todo 移动到构造函数去 - */ - bool init(void); - - /** - * @brief 获取物理内存长度 - * @return size_t 物理内存长度 + * @brief 获取物理内存页数 + * @return size_t 物理内存页数 */ - size_t get_pmm_length(void) const; + size_t GetPagesCount() const; /** * @brief 获取内核空间起始地址 - * @return uintptr_t 内核空间起始地址 + * @return uint64_t 内核空间起始地址 */ - uintptr_t get_kernel_space_start(void) const; + uint64_t GetKernelSpaceAddr() const; /** - * @brief 获取内核空间大小,单位为 byte - * @return size_t 内核空间大小 + * @brief 获取内核空间页数 + * @return size_t 内核空间页数 */ - size_t get_kernel_space_length(void) const; + size_t GetKernelSpacePagesCount() const; /** - * @brief 获取非内核空间起始地址 - * @return uintptr_t 非内核空间起始地址 + * @brief 获取用户间起始地址 + * @return uint64_t 用户间起始地址 */ - uintptr_t get_non_kernel_space_start(void) const; + uint64_t GetUserSpaceAddr() const; /** - * @brief 获取非内核空间大小,单位为 byte - * @return size_t 非内核空间大小 + * @brief 获取用户间页数 + * @return size_t 用户间页数 */ - size_t get_non_kernel_space_length(void) const; + size_t GetUserSpacePagesCount() const; /** * @brief 获取当前已使用页数 - * @return size_t 已使用页数 + * @return size_t 已使用页数 */ - size_t get_used_pages_count(void) const; + size_t GetUsedPagesCount() const; /** - * @brief 获取当前空闲页 - * @return size_t 空闲页数 + * @brief 获取当前空闲页数 + * @return size_t 空闲页数 */ - size_t get_free_pages_count(void) const; + size_t GetFreePagesCount() const; /** * @brief 分配一页 - * @return uintptr_t 分配的内存起始地址 + * @return uint64_t 分配的内存起始地址 */ - uintptr_t alloc_page(void); + uint64_t AllocUserPage(); /** * @brief 分配多页 * @param _len 页数 - * @return uintptr_t 分配的内存起始地址 + * @return uint64_t 分配的内存起始地址 */ - uintptr_t alloc_pages(size_t _len); + uint64_t AllocUserPages(size_t _len); /** * @brief 分配以指定地址开始的 _len 页 - * @param _addr 指定的地址 + * @param addr 指定的地址 * @param _len 页数 * @return true 成功 * @return false 失败 */ - bool alloc_pages(uintptr_t _addr, size_t _len); + bool AllocUserPagesAt(uint64_t addr, size_t _len); /** * @brief 在内核空间申请一页 - * @return uintptr_t 分配的内存起始地址 + * @return uint64_t 分配的内存起始地址 */ - uintptr_t alloc_page_kernel(void); + uint64_t AllocKernelPage(); /** - * @brief 在内核空间分配 _len 页 - * @param _len 页数 - * @return uintptr_t 分配到的内存起始地址 + * @brief 在内核空间分配 pages_count 页 + * @param pages_count 页数 + * @return uint64_t 分配到的内存起始地址 */ - uintptr_t alloc_pages_kernel(size_t _len); + uint64_t AllocKernelPages(size_t pages_count); /** * @brief 在内核空间分配以指定地址开始的 _len 页 - * @param _addr 指定的地址 - * @param _len 页数 - * @return true 成功 - * @return false 失败 + * @param addr 指定的地址 + * @param pages_count 页数 + * @return true 成功 + * @return false 失败 */ - bool alloc_pages_kernel(uintptr_t _addr, size_t _len); + bool AllocKernelPagesAt(uint64_t addr, size_t pages_count); /** * @brief 回收一页 - * @param _addr 要回收的地址 + * @param addr 要回收的地址 */ - void free_page(uintptr_t _addr); + void FreePage(uint64_t addr); /** * @brief 回收多页 - * @param _addr 要回收的地址 - * @param _len 页数 + * @param addr 要回收的地址 + * @param pages_count 页数 */ - void free_pages(uintptr_t _addr, size_t _len); + void FreePages(uint64_t addr, size_t pages_count); private: /// 物理内存开始地址 - uintptr_t addr_; + uint64_t addr_; /// 物理内存页数 - size_t pages_count; + size_t pages_count_; /// 内核空间起始地址 - uintptr_t kernel_addr; + uint64_t kernel_addr_; /// 内核页数 - size_t kernel_pages_count; + size_t kernel_pages_count_; /// 用户空间起始地址 - uintptr_t user_start; + uint64_t user_start_; /// 用户空间页数 - size_t user_pages_count; + size_t user_pages_count_; - /// 内核空间不会位于内存中间,导致出现非内核空间被切割为两部分的情况 + /// 内核空间不会位于内存中间,导致出现用户间被切割为两部分的情况 /// 物理内存分配器,分配内核空间 - AllocatorBase *kernel_allocator; + AllocatorBase *kernel_allocator_; /// 物理内存分配器,分配用户空间 - AllocatorBase *user_allocator; + AllocatorBase *user_allocator_; /** - * @brief 将 multiboot2/dtb 信息移动到内核空间 + * @brief 将 elf 与 dtb + * 信息移动到内核空间,位于内核结束后的下一页,分别占用一页 */ - void move_boot_info(void); + void MoveElfDtb(); }; /// 全局物理内存管理器 diff --git a/src/kernel/physical_memory_manager.cpp b/src/kernel/physical_memory_manager.cpp index 2d93877de..3c69caf40 100644 --- a/src/kernel/physical_memory_manager.cpp +++ b/src/kernel/physical_memory_manager.cpp @@ -16,177 +16,151 @@ #include "memory/physical_memory_manager.h" -#include "kernel_log.hpp" -// #include "boot_info.h" -// #include "cassert" -// #include "common.h" #include #include +#include "basic_info.hpp" +#include "kernel_elf.hpp" +#include "kernel_fdt.hpp" +#include "kernel_log.hpp" #include "memory/firstfit_allocator.h" +#include "project_config.h" // #include "resource.h" -// bool PhysicalMemoryManager::init(void) { -// // 获取物理内存信息 -// resource_t mem_info = BOOT_INFO::get_memory(); -// // 设置物理地址的起点与长度 -// start = mem_info.mem.addr; -// length = mem_info.mem.len; -// // 计算页数 -// total_pages = length / COMMON::PAGE_SIZE; -// // 内核空间地址开始 -// kernel_space_start = COMMON::KERNEL_START_ADDR; -// // 长度手动指定 -// kernel_space_length = COMMON::KERNEL_SPACE_SIZE; -// // 非内核空间在内核空间结束后 -// non_kernel_space_start = -// COMMON::KERNEL_START_ADDR + COMMON::KERNEL_SPACE_SIZE; -// // 长度为总长度减去内核长度 -// non_kernel_space_length = length - kernel_space_length; - -// // 创建分配器 -// // 内核空间 -// static FIRSTFIT first_fit_allocator_kernel( -// "First Fit Allocator(kernel space)", kernel_space_start, -// kernel_space_length / COMMON::PAGE_SIZE); -// kernel_space_allocator = (ALLOCATOR*)&first_fit_allocator_kernel; -// // 非内核空间 -// static FIRSTFIT first_fit_allocator( -// "First Fit Allocator", non_kernel_space_start, -// non_kernel_space_length / COMMON::PAGE_SIZE); -// allocator = (ALLOCATOR*)&first_fit_allocator; - -// // 内核实际占用页数 这里也算了 0~1M 的 reserved 内存 -// size_t kernel_pages = -// (COMMON::ALIGN(COMMON::KERNEL_END_ADDR, COMMON::PAGE_SIZE) - -// COMMON::ALIGN(COMMON::KERNEL_START_ADDR, COMMON::PAGE_SIZE)) / -// COMMON::PAGE_SIZE; -// // 将内核已使用部分划分出来 -// if (alloc_pages_kernel(COMMON::KERNEL_START_ADDR, kernel_pages) == true) { -// // 将 multiboot2/dtb 信息移动到内核空间 -// move_boot_info(); -// info("pmm init.\n"); -// return true; -// } else { -// assert(0); -// return false; -// } -// } - -// size_t PhysicalMemoryManager::get_pmm_length(void) const { return length; } - -// uintptr_t PhysicalMemoryManager::get_kernel_space_start(void) const { -// return kernel_space_start; -// } - -// size_t PhysicalMemoryManager::get_kernel_space_length(void) const { -// return kernel_space_length; -// } - -// uintptr_t PhysicalMemoryManager::get_non_kernel_space_start(void) const { -// return non_kernel_space_start; -// } - -// size_t PhysicalMemoryManager::get_non_kernel_space_length(void) const { -// return non_kernel_space_length; -// } - -// size_t PhysicalMemoryManager::get_used_pages_count(void) const { -// size_t ret = -// kernel_space_allocator->get_used_count() + allocator->get_used_count(); -// return ret; -// } - -// size_t PhysicalMemoryManager::get_free_pages_count(void) const { -// size_t ret = -// kernel_space_allocator->get_free_count() + allocator->get_free_count(); -// return ret; -// } - -// uintptr_t PhysicalMemoryManager::alloc_page(void) { -// uintptr_t ret = allocator->alloc(1); -// return ret; -// } - -// uintptr_t PhysicalMemoryManager::alloc_pages(size_t _len) { -// uintptr_t ret = allocator->alloc(_len); -// return ret; -// } - -// bool PhysicalMemoryManager::alloc_pages(uintptr_t _addr, size_t _len) { -// bool ret = allocator->alloc(_addr, _len); -// return ret; -// } - -// uintptr_t PhysicalMemoryManager::alloc_page_kernel(void) { -// uintptr_t ret = kernel_space_allocator->alloc(1); -// return ret; -// } - -// uintptr_t PhysicalMemoryManager::alloc_pages_kernel(size_t _len) { -// uintptr_t ret = kernel_space_allocator->alloc(_len); -// return ret; -// } - -// bool PhysicalMemoryManager::alloc_pages_kernel(uintptr_t _addr, size_t _len) -// { -// bool ret = kernel_space_allocator->alloc(_addr, _len); -// return ret; -// } - -// void PhysicalMemoryManager::free_page(uintptr_t _addr) { -// // 判断应该使用哪个分配器 -// if (_addr >= kernel_space_start && -// _addr < kernel_space_start + kernel_space_length) { -// kernel_space_allocator->free(_addr, 1); -// } else if (_addr >= non_kernel_space_start && -// _addr < non_kernel_space_start + non_kernel_space_length) { -// allocator->free(_addr, 1); -// } else { -// // 如果都不是说明有问题 -// assert(0); -// } -// return; -// } - -// void PhysicalMemoryManager::free_pages(uintptr_t _addr, size_t _len) { -// // 判断应该使用哪个分配器 -// if (_addr >= kernel_space_start && -// _addr < kernel_space_start + kernel_space_length) { -// kernel_space_allocator->free(_addr, _len); -// } else if (_addr >= non_kernel_space_start && -// _addr < non_kernel_space_start + non_kernel_space_length) { -// allocator->free(_addr, _len); -// } -// // 如果都不是说明有问题 -// else { -// assert(0); -// } -// return; -// } - -// // 将启动信息移动到内核空间 -// void PhysicalMemoryManager::move_boot_info(void) { -// // 计算 multiboot2 信息需要多少页 -// size_t pages = BOOT_INFO::boot_info_size / COMMON::PAGE_SIZE; -// if (BOOT_INFO::boot_info_size % COMMON::PAGE_SIZE != 0) { -// pages++; -// } -// // 申请空间 -// uintptr_t new_addr = get_instance().alloc_pages_kernel(pages); -// // 复制过来,完成后以前的内存就可以使用了 -// memcpy((void*)new_addr, (void*)BOOT_INFO::boot_info_addr, -// pages * COMMON::PAGE_SIZE); -// // 设置地址 -// BOOT_INFO::boot_info_addr = (uintptr_t)new_addr; -// // 重新初始化 -// BOOT_INFO::init(); -// return; -// } +PhysicalMemoryManager::PhysicalMemoryManager(uint64_t addr, size_t pages_count) + : addr_(addr), pages_count_(pages_count) { + // // 内核空间地址开始 + // kernel_addr_ = kBasicInfo.GetInstance().kernel_addr; + // // 长度手动指定 + // kernel_pages_count_ = kKernelSpaceSize / kPageSize; + // // 非内核空间在内核空间结束后 + // user_start_ = kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; + // // 长度为总长度减去内核长度 + // user_pages_count_ = pages_count_ - kernel_pages_count_; + + // // 创建分配器 + // // 内核空间 + // static FirstFitAllocator first_fit_allocator_kernel( + // "First Fit Allocator(Kernel space)", kernel_addr_, kernel_pages_count_); + // kernel_allocator_ = (AllocatorBase*)&first_fit_allocator_kernel; + // // 用户空间 + // static FirstFitAllocator first_fit_allocator( + // "First Fit Allocator(User space)", user_start_, user_pages_count_); + // user_allocator_ = (AllocatorBase*)&first_fit_allocator; + + // // 内核占用页数 + // auto kernel_pages = kBasicInfo.GetInstance().kernel_size / kPageSize; + // if (kBasicInfo.GetInstance().kernel_size % kPageSize != 0) { + // kernel_pages++; + // } + // // 将内核已使用部进行分配 + // AllocKernelPagesAt(kBasicInfo.GetInstance().kernel_addr, kernel_pages); + // MoveElfDtb(); +} + +size_t PhysicalMemoryManager::GetPagesCount() const { return pages_count_; } + +uint64_t PhysicalMemoryManager::GetKernelSpaceAddr() const { + return kernel_addr_; +} + +size_t PhysicalMemoryManager::GetKernelSpacePagesCount() const { + return kernel_pages_count_; +} + +uint64_t PhysicalMemoryManager::GetUserSpaceAddr() const { return user_start_; } + +size_t PhysicalMemoryManager::GetUserSpacePagesCount() const { + return user_pages_count_; +} + +size_t PhysicalMemoryManager::GetUsedPagesCount() const { + return kernel_allocator_->GetUsedCount() + user_allocator_->GetUsedCount(); +} + +size_t PhysicalMemoryManager::GetFreePagesCount() const { + return kernel_allocator_->GetFreeCount() + user_allocator_->GetFreeCount(); +} + +uint64_t PhysicalMemoryManager::AllocUserPage() { + return user_allocator_->Alloc(1); +} + +uint64_t PhysicalMemoryManager::AllocUserPages(size_t pages_count) { + return user_allocator_->Alloc(pages_count); +} + +bool PhysicalMemoryManager::AllocUserPagesAt(uint64_t addr, + size_t pages_count) { + bool ret = user_allocator_->AllocAt(addr, pages_count); + return ret; +} + +uint64_t PhysicalMemoryManager::AllocKernelPage() { + return kernel_allocator_->Alloc(1); +} + +uint64_t PhysicalMemoryManager::AllocKernelPages(size_t pages_count) { + return kernel_allocator_->Alloc(pages_count); +} + +bool PhysicalMemoryManager::AllocKernelPagesAt(uint64_t addr, + size_t pages_count) { + return kernel_allocator_->AllocAt(addr, pages_count); +} + +void PhysicalMemoryManager::FreePage(uint64_t addr) { + // 判断应该使用哪个分配器 + if (addr >= kernel_addr_ && addr < kernel_addr_ + kernel_pages_count_) { + kernel_allocator_->Free(addr, 1); + } else if (addr >= user_start_ && addr < user_start_ + user_pages_count_) { + user_allocator_->Free(addr, 1); + } +} + +void PhysicalMemoryManager::FreePages(uint64_t addr, size_t pages_count) { + // 判断应该使用哪个分配器 + if (addr >= kernel_addr_ && addr < kernel_addr_ + kernel_pages_count_) { + kernel_allocator_->Free(addr, pages_count); + } else if (addr >= user_start_ && addr < user_start_ + user_pages_count_) { + user_allocator_->Free(addr, pages_count); + } +} + +void PhysicalMemoryManager::MoveElfDtb() { + auto old_elf_addr = kBasicInfo.GetInstance().elf_addr; + // auto old_dtb_addr = kBasicInfo.GetInstance().dtb_addr; + // 计算需要多少页 + auto elf_pages = kBasicInfo.GetInstance().elf_size / kPageSize; + if (kBasicInfo.GetInstance().elf_size % kPageSize != 0) { + elf_pages++; + } + // auto dtb_pages = kBasicInfo.GetInstance().dtb_size / kPageSize; + // if (kBasicInfo.GetInstance().dtb_size % kPageSize != 0) { + // elf_pages++; + // } + // 申请空间 + auto new_elf_addr = AllocKernelPages(elf_pages); + // auto new_dtb_addr = AllocKernelPages(dtb_pages); + // 复制过来,完成后以前的内存就可以使用了 + memcpy((void*)old_elf_addr, (void*)new_elf_addr, elf_pages * kPageSize); + // memcpy((void*)old_dtb_addr, (void*)new_dtb_addr, dtb_pages * kPageSize); + // 更新 kBasicInfo 信息 + kBasicInfo.GetInstance().elf_addr = new_elf_addr; + // kBasicInfo.GetInstance().dtb_addr = new_dtb_addr; + // 重新初始化 + if (kBasicInfo.GetInstance().elf_size != 0) { + kKernelElf.GetInstance() = KernelElf(kBasicInfo.GetInstance().elf_addr, + kBasicInfo.GetInstance().elf_size); + } + // kKernelFdt.GetInstance() = KernelFdt(kBasicInfo.GetInstance().dtb_addr); +} uint32_t PhysicalMemoryInit(uint32_t argc, uint8_t* argv) { // 初始化物理内存管理器 - kPhysicalMemoryManager.GetInstance(); + kPhysicalMemoryManager.GetInstance() = + PhysicalMemoryManager(kBasicInfo.GetInstance().physical_memory_addr, + kBasicInfo.GetInstance().physical_memory_size); Info("Hello PhysicalMemoryInit\n"); } diff --git a/src/project_config.h b/src/project_config.h index 38fda52e2..5d60b2124 100644 --- a/src/project_config.h +++ b/src/project_config.h @@ -21,4 +21,10 @@ #define KERNEL_NAME (wchar_t *)L"kernel.elf" +/// 使用 4KB 页大小 +#define kPageSize (0x1000) + +/// 内核空间设为 64MB +#define kKernelSpaceSize (0x4000000) + #endif /* SIMPLEKERNEL_SRC_PROJECT_CONFIG_H_ */ diff --git a/tools/project_config.h.in b/tools/project_config.h.in index 3b6c748f0..572b43d21 100644 --- a/tools/project_config.h.in +++ b/tools/project_config.h.in @@ -21,4 +21,10 @@ #define KERNEL_NAME (wchar_t *)L"@KERNEL_ELF_OUTPUT_NAME@" +/// 使用 4KB 页大小 +#define kPageSize (0x1000) + +/// 内核空间设为 64MB +#define kKernelSpaceSize (0x4000000) + #endif /* SIMPLEKERNEL_SRC_PROJECT_CONFIG_H_ */ From adf23682761fac680b85cbe4cb1e6255f8bffb17 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Mon, 8 Jul 2024 20:32:15 +0800 Subject: [PATCH 05/17] working on pmm Signed-off-by: Zone.N --- src/kernel/physical_memory_manager.cpp | 97 +++++++++++++------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/src/kernel/physical_memory_manager.cpp b/src/kernel/physical_memory_manager.cpp index 3c69caf40..0d0428b1e 100644 --- a/src/kernel/physical_memory_manager.cpp +++ b/src/kernel/physical_memory_manager.cpp @@ -29,33 +29,33 @@ PhysicalMemoryManager::PhysicalMemoryManager(uint64_t addr, size_t pages_count) : addr_(addr), pages_count_(pages_count) { - // // 内核空间地址开始 - // kernel_addr_ = kBasicInfo.GetInstance().kernel_addr; - // // 长度手动指定 - // kernel_pages_count_ = kKernelSpaceSize / kPageSize; - // // 非内核空间在内核空间结束后 - // user_start_ = kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; - // // 长度为总长度减去内核长度 - // user_pages_count_ = pages_count_ - kernel_pages_count_; - - // // 创建分配器 - // // 内核空间 - // static FirstFitAllocator first_fit_allocator_kernel( - // "First Fit Allocator(Kernel space)", kernel_addr_, kernel_pages_count_); - // kernel_allocator_ = (AllocatorBase*)&first_fit_allocator_kernel; - // // 用户空间 - // static FirstFitAllocator first_fit_allocator( - // "First Fit Allocator(User space)", user_start_, user_pages_count_); - // user_allocator_ = (AllocatorBase*)&first_fit_allocator; - - // // 内核占用页数 - // auto kernel_pages = kBasicInfo.GetInstance().kernel_size / kPageSize; - // if (kBasicInfo.GetInstance().kernel_size % kPageSize != 0) { - // kernel_pages++; - // } - // // 将内核已使用部进行分配 - // AllocKernelPagesAt(kBasicInfo.GetInstance().kernel_addr, kernel_pages); - // MoveElfDtb(); + // 内核空间地址开始 + kernel_addr_ = kBasicInfo.GetInstance().kernel_addr; + // 长度手动指定 + kernel_pages_count_ = kKernelSpaceSize / kPageSize; + // 非内核空间在内核空间结束后 + user_start_ = kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; + // 长度为总长度减去内核长度 + user_pages_count_ = pages_count_ - kernel_pages_count_; + + // 创建分配器 + // 内核空间 + static FirstFitAllocator first_fit_allocator_kernel( + "First Fit Allocator(Kernel space)", kernel_addr_, kernel_pages_count_); + kernel_allocator_ = (AllocatorBase*)&first_fit_allocator_kernel; + // 用户空间 + static FirstFitAllocator first_fit_allocator( + "First Fit Allocator(User space)", user_start_, user_pages_count_); + user_allocator_ = (AllocatorBase*)&first_fit_allocator; + + // 内核占用页数 + auto kernel_pages = kBasicInfo.GetInstance().kernel_size / kPageSize; + if (kBasicInfo.GetInstance().kernel_size % kPageSize != 0) { + kernel_pages++; + } + // 将内核已使用部进行分配 + AllocKernelPagesAt(kBasicInfo.GetInstance().kernel_addr, kernel_pages); + MoveElfDtb(); } size_t PhysicalMemoryManager::GetPagesCount() const { return pages_count_; } @@ -128,32 +128,31 @@ void PhysicalMemoryManager::FreePages(uint64_t addr, size_t pages_count) { } void PhysicalMemoryManager::MoveElfDtb() { - auto old_elf_addr = kBasicInfo.GetInstance().elf_addr; - // auto old_dtb_addr = kBasicInfo.GetInstance().dtb_addr; - // 计算需要多少页 - auto elf_pages = kBasicInfo.GetInstance().elf_size / kPageSize; - if (kBasicInfo.GetInstance().elf_size % kPageSize != 0) { - elf_pages++; - } - // auto dtb_pages = kBasicInfo.GetInstance().dtb_size / kPageSize; - // if (kBasicInfo.GetInstance().dtb_size % kPageSize != 0) { - // elf_pages++; - // } - // 申请空间 - auto new_elf_addr = AllocKernelPages(elf_pages); - // auto new_dtb_addr = AllocKernelPages(dtb_pages); - // 复制过来,完成后以前的内存就可以使用了 - memcpy((void*)old_elf_addr, (void*)new_elf_addr, elf_pages * kPageSize); - // memcpy((void*)old_dtb_addr, (void*)new_dtb_addr, dtb_pages * kPageSize); - // 更新 kBasicInfo 信息 - kBasicInfo.GetInstance().elf_addr = new_elf_addr; - // kBasicInfo.GetInstance().dtb_addr = new_dtb_addr; // 重新初始化 - if (kBasicInfo.GetInstance().elf_size != 0) { + if (kBasicInfo.GetInstance().elf_addr != 0) { + auto old_elf_addr = kBasicInfo.GetInstance().elf_addr; + // 计算需要多少页 + auto elf_pages = kBasicInfo.GetInstance().elf_size / kPageSize; + if (kBasicInfo.GetInstance().elf_size % kPageSize != 0) { + elf_pages++; + } + // 申请空间 + auto new_elf_addr = AllocKernelPages(elf_pages); + // 复制过来,完成后以前的内存就可以使用了 + memcpy((void*)new_elf_addr, (void*)old_elf_addr, elf_pages * kPageSize); + // 更新 kBasicInfo 信息 + kBasicInfo.GetInstance().elf_addr = new_elf_addr; kKernelElf.GetInstance() = KernelElf(kBasicInfo.GetInstance().elf_addr, kBasicInfo.GetInstance().elf_size); } - // kKernelFdt.GetInstance() = KernelFdt(kBasicInfo.GetInstance().dtb_addr); + if (kBasicInfo.GetInstance().fdt_addr != 0) { + auto old_fdt_addr = kBasicInfo.GetInstance().fdt_addr; + auto fdt_pages = 1; + auto new_fdt_addr = AllocKernelPages(fdt_pages); + memcpy((void*)new_fdt_addr, (void*)old_fdt_addr, fdt_pages * kPageSize); + kBasicInfo.GetInstance().fdt_addr = new_fdt_addr; + kKernelFdt.GetInstance() = KernelFdt(kBasicInfo.GetInstance().fdt_addr); + } } uint32_t PhysicalMemoryInit(uint32_t argc, uint8_t* argv) { From 4c22a77f2e8652c23b7097259ce5f022eab6f057 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Thu, 1 Aug 2024 09:29:32 +0000 Subject: [PATCH 06/17] fix: add return 0 Signed-off-by: Zone.N --- src/kernel/physical_memory_manager.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/kernel/physical_memory_manager.cpp b/src/kernel/physical_memory_manager.cpp index 6ec6e2ec5..306afcdb0 100644 --- a/src/kernel/physical_memory_manager.cpp +++ b/src/kernel/physical_memory_manager.cpp @@ -155,11 +155,13 @@ void PhysicalMemoryManager::MoveElfDtb() { } } -uint32_t PhysicalMemoryInit(uint32_t argc, uint8_t* argv) { +uint32_t PhysicalMemoryInit(uint32_t, uint8_t*) { // 初始化物理内存管理器 - // kPhysicalMemoryManager.GetInstance() = - // PhysicalMemoryManager(kBasicInfo.GetInstance().physical_memory_addr, - // kBasicInfo.GetInstance().physical_memory_size); + kPhysicalMemoryManager.GetInstance() = + PhysicalMemoryManager(kBasicInfo.GetInstance().physical_memory_addr, + kBasicInfo.GetInstance().physical_memory_size); - // Info("Hello PhysicalMemoryInit\n"); + log::Info("Hello PhysicalMemoryInit\n"); + + return 0; } From 89e5c63f9d53850a70fb19c5bd30612e2e5d3f30 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Tue, 27 Aug 2024 03:15:41 +0000 Subject: [PATCH 07/17] style: PAGE_SIZE->kPageSize Signed-off-by: Zone.N --- src/kernel/firstfit_allocator.cpp | 16 ++++++++-------- src/kernel/include/memory/allocator_base.hpp | 3 +++ src/kernel/include/memory/firstfit_allocator.h | 4 +--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/kernel/firstfit_allocator.cpp b/src/kernel/firstfit_allocator.cpp index e23b344dd..63b197a94 100644 --- a/src/kernel/firstfit_allocator.cpp +++ b/src/kernel/firstfit_allocator.cpp @@ -25,7 +25,7 @@ FirstFitAllocator::FirstFitAllocator(const char* name, uint64_t addr, size_t pages_count) : AllocatorBase(name, addr, pages_count) { - if (addr % PAGE_SIZE != 0) { + if (addr % kPageSize != 0) { log::Err("addr not aligned. 0x%lX\n", addr); throw; } @@ -47,7 +47,7 @@ uint64_t FirstFitAllocator::Alloc(size_t pages_count) { } // 计算实际地址 // 分配器起始地址+页长度*第几页 - res_addr = addr_ + (PAGE_SIZE * idx); + res_addr = addr_ + (kPageSize * idx); // 更新统计信息 free_length_ -= pages_count; used_length_ += pages_count; @@ -56,18 +56,18 @@ uint64_t FirstFitAllocator::Alloc(size_t pages_count) { bool FirstFitAllocator::AllocAt(uint64_t addr, size_t pages_count) { // 页对齐 - if (addr % PAGE_SIZE != 0) { + if (addr % kPageSize != 0) { log::Warn("addr not aligned 0x%lX.\n", addr); return false; } // 申请地址超出范围 if (addr < addr_ || addr > addr_ || - addr + pages_count * PAGE_SIZE > addr_ + length_ * PAGE_SIZE) { + addr + pages_count * kPageSize > addr_ + length_ * kPageSize) { log::Warn("out of range 0x%lX %d.\n", addr, pages_count); return false; } // 计算 addr 在 map 中的索引 - size_t idx = (addr - addr_) / PAGE_SIZE; + size_t idx = (addr - addr_) / kPageSize; // 遍历 for (auto i = idx; i < idx + pages_count; i++) { // 如果在范围内有已经分配的内存,返回 false @@ -89,18 +89,18 @@ bool FirstFitAllocator::AllocAt(uint64_t addr, size_t pages_count) { void FirstFitAllocator::Free(uint64_t addr, size_t pages_count) { // 页对齐 - if (addr % PAGE_SIZE != 0) { + if (addr % kPageSize != 0) { log::Warn("addr not aligned 0x%lX.\n", addr); return; } // 申请地址超出范围 if (addr < addr_ || addr > addr_ || - addr + pages_count * PAGE_SIZE > addr_ + length_ * PAGE_SIZE) { + addr + pages_count * kPageSize > addr_ + length_ * kPageSize) { log::Warn("out of range 0x%lX %d.\n", addr, pages_count); return; } // 计算 addr 在 map 中的索引 - size_t idx = (addr - addr_) / PAGE_SIZE; + size_t idx = (addr - addr_) / kPageSize; for (auto i = idx; i < idx + pages_count; i++) { map[i] = 0; } diff --git a/src/kernel/include/memory/allocator_base.hpp b/src/kernel/include/memory/allocator_base.hpp index 8a0e09306..21701304e 100644 --- a/src/kernel/include/memory/allocator_base.hpp +++ b/src/kernel/include/memory/allocator_base.hpp @@ -85,6 +85,9 @@ class AllocatorBase { virtual size_t GetFreeCount() const { return free_length_; } protected: + /// 页大小 4096 bytes + static constexpr const uint64_t kPageSize = 0x1000; + /// 分配器名称 const char *name_; /// 当前管理的内存区域地址 diff --git a/src/kernel/include/memory/firstfit_allocator.h b/src/kernel/include/memory/firstfit_allocator.h index 0c088b16a..b5417a2e0 100644 --- a/src/kernel/include/memory/firstfit_allocator.h +++ b/src/kernel/include/memory/firstfit_allocator.h @@ -71,10 +71,8 @@ class FirstFitAllocator : AllocatorBase { void Free(uint64_t addr, size_t pages_count) override; private: - static constexpr const uint64_t PAGE_SIZE = 0x1000; - /// 位图,每一位表示一页内存,1 表示已使用,0 表示未使用 - std::bitset map; + std::bitset map; /** * @brief 寻找连续 pages_count 个 val 位,返回开始索引 From 03faff10c6e291a34b8e3dcf92e390cd9ae2e6cd Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Tue, 27 Aug 2024 09:54:08 +0000 Subject: [PATCH 08/17] feat: working on vmm Signed-off-by: Zone.N --- .../include/memory/virtual_memory_manager.hpp | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/kernel/include/memory/virtual_memory_manager.hpp diff --git a/src/kernel/include/memory/virtual_memory_manager.hpp b/src/kernel/include/memory/virtual_memory_manager.hpp new file mode 100644 index 000000000..624e36688 --- /dev/null +++ b/src/kernel/include/memory/virtual_memory_manager.hpp @@ -0,0 +1,186 @@ + +/** + * @file virtual_memory_manager.hpp + * @brief 虚拟内存管理 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#ifndef SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_HPP_ +#define SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_HPP_ + +#include +#include + +#include "physical_memory_manager.h" +#include "singleton.hpp" + +/** + * @brief 虚拟内存管理接口 + * 对物理内存的管理来说 + * 1. 管理所有的物理内存,不论是否被机器保留/无法访问 + * 2. 内存开始地址与长度由 bootloader 给出: x86 下为 grub, riscv 下为 opensbi + * 3. + * 不关心内存是否被使用,但是默认的物理内存分配空间从内核结束后开始 + * 如果由体系结构需要分配内核开始前内存空间的,则尽量避免 + * 4. 最管理单位为页 + */ +class VirtualMemoryManager { + public: + /** + * @brief 构造函数 + * @param addr 物理地址起点 + * @param pages_count 物理页数 + */ + explicit VirtualMemoryManager(uint64_t addr, size_t pages_count); + + /// @name 构造/析构函数 + /// @{ + VirtualMemoryManager() = default; + VirtualMemoryManager(const VirtualMemoryManager &) = default; + VirtualMemoryManager(VirtualMemoryManager &&) = default; + auto operator=(const VirtualMemoryManager &) -> VirtualMemoryManager & = + default; + auto operator=(VirtualMemoryManager &&) -> VirtualMemoryManager & = default; + ~VirtualMemoryManager() = default; + /// @} + + /** + * @brief 获取物理内存页数 + * @return size_t 物理内存页数 + */ + size_t GetPagesCount() const; + + /** + * @brief 获取内核空间起始地址 + * @return uint64_t 内核空间起始地址 + */ + uint64_t GetKernelSpaceAddr() const; + + /** + * @brief 获取内核空间页数 + * @return size_t 内核空间页数 + */ + size_t GetKernelSpacePagesCount() const; + + /** + * @brief 获取用户间起始地址 + * @return uint64_t 用户间起始地址 + */ + uint64_t GetUserSpaceAddr() const; + + /** + * @brief 获取用户间页数 + * @return size_t 用户间页数 + */ + size_t GetUserSpacePagesCount() const; + + /** + * @brief 获取当前已使用页数 + * @return size_t 已使用页数 + */ + size_t GetUsedPagesCount() const; + + /** + * @brief 获取当前空闲页数 + * @return size_t 空闲页数 + */ + size_t GetFreePagesCount() const; + + /** + * @brief 分配一页 + * @return uint64_t 分配的内存起始地址 + */ + uint64_t AllocUserPage(); + + /** + * @brief 分配多页 + * @param _len 页数 + * @return uint64_t 分配的内存起始地址 + */ + uint64_t AllocUserPages(size_t _len); + + /** + * @brief 分配以指定地址开始的 _len 页 + * @param addr 指定的地址 + * @param _len 页数 + * @return true 成功 + * @return false 失败 + */ + bool AllocUserPagesAt(uint64_t addr, size_t _len); + + /** + * @brief 在内核空间申请一页 + * @return uint64_t 分配的内存起始地址 + */ + uint64_t AllocKernelPage(); + + /** + * @brief 在内核空间分配 pages_count 页 + * @param pages_count 页数 + * @return uint64_t 分配到的内存起始地址 + */ + uint64_t AllocKernelPages(size_t pages_count); + + /** + * @brief 在内核空间分配以指定地址开始的 _len 页 + * @param addr 指定的地址 + * @param pages_count 页数 + * @return true 成功 + * @return false 失败 + */ + bool AllocKernelPagesAt(uint64_t addr, size_t pages_count); + + /** + * @brief 回收一页 + * @param addr 要回收的地址 + */ + void FreePage(uint64_t addr); + + /** + * @brief 回收多页 + * @param addr 要回收的地址 + * @param pages_count 页数 + */ + void FreePages(uint64_t addr, size_t pages_count); + + private: + /// 物理内存开始地址 + uint64_t addr_; + /// 物理内存页数 + size_t pages_count_; + /// 内核空间起始地址 + uint64_t kernel_addr_; + /// 内核页数 + size_t kernel_pages_count_; + /// 用户空间起始地址 + uint64_t user_start_; + /// 用户空间页数 + size_t user_pages_count_; + + /// 内核空间不会位于内存中间,导致出现用户间被切割为两部分的情况 + /// 物理内存分配器,分配内核空间 + AllocatorBase *kernel_allocator_; + /// 物理内存分配器,分配用户空间 + AllocatorBase *user_allocator_; + + /** + * @brief 将 elf 与 dtb + * 信息移动到内核空间,位于内核结束后的下一页,分别占用一页 + */ + void MoveElfDtb(); +}; + +/// 全局物理内存管理器 +[[maybe_unused]] static Singleton kPhysicalMemoryManager; + +#endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_HPP_ \ + */ From 386f001c0e6635d669b8c89ece26bc2da887625e Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Wed, 28 Aug 2024 07:08:44 +0000 Subject: [PATCH 09/17] refactor: firstfit_allocator as template Signed-off-by: Zone.N --- src/kernel/CMakeLists.txt | 1 - src/kernel/firstfit_allocator.cpp | 129 -------- src/kernel/include/memory/allocator_base.hpp | 3 - .../include/memory/firstfit_allocator.h | 86 ----- .../include/memory/firstfit_allocator.hpp | 183 +++++++++++ .../include/memory/physical_memory_manager.h | 3 + .../include/memory/virtual_memory_manager.hpp | 294 +++++++++++------- src/kernel/physical_memory_manager.cpp | 7 +- src/kernel/virtual_memory_manager.cpp | 191 ++++++++++++ src/project_config.h | 3 - tools/project_config.h.in | 3 - 11 files changed, 559 insertions(+), 344 deletions(-) delete mode 100644 src/kernel/firstfit_allocator.cpp delete mode 100644 src/kernel/include/memory/firstfit_allocator.h create mode 100644 src/kernel/include/memory/firstfit_allocator.hpp create mode 100644 src/kernel/virtual_memory_manager.cpp diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index 5f14d2ebc..abacbbb4a 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -21,7 +21,6 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/driver) add_executable(${PROJECT_NAME} main.cpp - firstfit_allocator.cpp physical_memory_manager.cpp ) diff --git a/src/kernel/firstfit_allocator.cpp b/src/kernel/firstfit_allocator.cpp deleted file mode 100644 index 63b197a94..000000000 --- a/src/kernel/firstfit_allocator.cpp +++ /dev/null @@ -1,129 +0,0 @@ - -/** - * @file firstfit_allocator.cpp - * @brief firstfit 内存分配器实现 - * @author Zone.N (Zone.Niuzh@hotmail.com) - * @version 1.0 - * @date 2021-09-18 - * @copyright MIT LICENSE - * https://github.com/Simple-XX/SimpleKernel - * @par change log: - * - *
DateAuthorDescription - *
2021-09-18digmouse233迁移到 doxygen - *
- */ - -#include "memory/firstfit_allocator.h" - -#include -#include -#include - -#include "kernel_log.hpp" - -FirstFitAllocator::FirstFitAllocator(const char* name, uint64_t addr, - size_t pages_count) - : AllocatorBase(name, addr, pages_count) { - if (addr % kPageSize != 0) { - log::Err("addr not aligned. 0x%lX\n", addr); - throw; - } - printf("%s: 0x%p(0x%X pages) init.\n", name_, addr_, length_); -} - -uint64_t FirstFitAllocator::Alloc(size_t pages_count) { - uint64_t res_addr = 0; - // 在位图中寻找连续 pages_count 的位置 - auto [is_found, idx] = Find(pages_count, false); - if (is_found == false) { - log::Warn("NO ENOUGH MEM %d.\n", pages_count); - return res_addr; - } - // 遍历区域 - for (auto i = idx; i < idx + pages_count; i++) { - // 置位,说明已使用 - map[i] = 1; - } - // 计算实际地址 - // 分配器起始地址+页长度*第几页 - res_addr = addr_ + (kPageSize * idx); - // 更新统计信息 - free_length_ -= pages_count; - used_length_ += pages_count; - return res_addr; -} - -bool FirstFitAllocator::AllocAt(uint64_t addr, size_t pages_count) { - // 页对齐 - if (addr % kPageSize != 0) { - log::Warn("addr not aligned 0x%lX.\n", addr); - return false; - } - // 申请地址超出范围 - if (addr < addr_ || addr > addr_ || - addr + pages_count * kPageSize > addr_ + length_ * kPageSize) { - log::Warn("out of range 0x%lX %d.\n", addr, pages_count); - return false; - } - // 计算 addr 在 map 中的索引 - size_t idx = (addr - addr_) / kPageSize; - // 遍历 - for (auto i = idx; i < idx + pages_count; i++) { - // 如果在范围内有已经分配的内存,返回 false - if (map[i] == true) { - return false; - } - } - // 到这里说明范围内没有已使用内存 - // 再次遍历 - for (auto i = idx; i < idx + pages_count; i++) { - // 置位 - map[i] = 1; - } - // 更新统计信息 - free_length_ -= pages_count; - used_length_ += pages_count; - return true; -} - -void FirstFitAllocator::Free(uint64_t addr, size_t pages_count) { - // 页对齐 - if (addr % kPageSize != 0) { - log::Warn("addr not aligned 0x%lX.\n", addr); - return; - } - // 申请地址超出范围 - if (addr < addr_ || addr > addr_ || - addr + pages_count * kPageSize > addr_ + length_ * kPageSize) { - log::Warn("out of range 0x%lX %d.\n", addr, pages_count); - return; - } - // 计算 addr 在 map 中的索引 - size_t idx = (addr - addr_) / kPageSize; - for (auto i = idx; i < idx + pages_count; i++) { - map[i] = 0; - } - // 更新统计信息 - free_length_ += pages_count; - used_length_ -= pages_count; -} - -std::pair FirstFitAllocator::Find(size_t pages_count, - bool val) const { - size_t count = 0; - size_t idx = 0; - // 遍历位图 - for (uint64_t i = 0; i < length_; i++) { - if (map[i] != val) { - count = 0; - idx = i + 1; - } else { - count++; - } - if (count == pages_count) { - return {true, idx}; - } - } - return {false, 0}; -} diff --git a/src/kernel/include/memory/allocator_base.hpp b/src/kernel/include/memory/allocator_base.hpp index 21701304e..8a0e09306 100644 --- a/src/kernel/include/memory/allocator_base.hpp +++ b/src/kernel/include/memory/allocator_base.hpp @@ -85,9 +85,6 @@ class AllocatorBase { virtual size_t GetFreeCount() const { return free_length_; } protected: - /// 页大小 4096 bytes - static constexpr const uint64_t kPageSize = 0x1000; - /// 分配器名称 const char *name_; /// 当前管理的内存区域地址 diff --git a/src/kernel/include/memory/firstfit_allocator.h b/src/kernel/include/memory/firstfit_allocator.h deleted file mode 100644 index b5417a2e0..000000000 --- a/src/kernel/include/memory/firstfit_allocator.h +++ /dev/null @@ -1,86 +0,0 @@ - -/** - * @file firstfit_allocator.h - * @brief firstfit 内存分配器头文件 - * @author Zone.N (Zone.Niuzh@hotmail.com) - * @version 1.0 - * @date 2021-09-18 - * @copyright MIT LICENSE - * https://github.com/Simple-XX/SimpleKernel - * @par change log: - * - *
DateAuthorDescription - *
2021-09-18digmouse233迁移到 doxygen - *
- */ - -#ifndef SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_BASE_H_ -#define SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_BASE_H_ - -#include -#include -#include - -#include "memory/allocator_base.hpp" - -/** - * @brief 使用 first fit 算法的分配器 - */ -class FirstFitAllocator : AllocatorBase { - public: - /** - * @brief 构造函数 - * @param name 分配器名称 - * @param addr 开始地址 - * @param pages_count 页数量 - */ - explicit FirstFitAllocator(const char *name, uint64_t addr, - size_t pages_count); - - /// @name 构造/析构函数 - /// @{ - FirstFitAllocator() = default; - FirstFitAllocator(const FirstFitAllocator &) = default; - FirstFitAllocator(FirstFitAllocator &&) = default; - auto operator=(const FirstFitAllocator &) -> FirstFitAllocator & = default; - auto operator=(FirstFitAllocator &&) -> FirstFitAllocator & = default; - ~FirstFitAllocator() = default; - /// @} - - /** - * @brief 分配长度为 pages_count 页的内存 - * @param pages_count 页数 - * @return uint64_t 分配的内存起点地址 - */ - uint64_t Alloc(size_t pages_count) override; - - /** - * @brief 在 addr 处分配长度为 pages_count 页的内存 - * @param addr 指定的地址 - * @param pages_count 页数 - * @return true 成功 - * @return false 失败 - */ - bool AllocAt(uint64_t addr, size_t pages_count) override; - - /** - * @brief 释放 addr 处 pages_count 页的内存 - * @param addr 要释放内存起点地址 - * @param pages_count 页数 - */ - void Free(uint64_t addr, size_t pages_count) override; - - private: - /// 位图,每一位表示一页内存,1 表示已使用,0 表示未使用 - std::bitset map; - - /** - * @brief 寻找连续 pages_count 个 val 位,返回开始索引 - * @param pages_count 连续页数量 - * @param val 要寻找的页状态 - * @return std::pair 找到返回 ,失败返回 - */ - std::pair Find(size_t pages_count, bool val) const; -}; - -#endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_BASE_H_ */ diff --git a/src/kernel/include/memory/firstfit_allocator.hpp b/src/kernel/include/memory/firstfit_allocator.hpp new file mode 100644 index 000000000..8939fc568 --- /dev/null +++ b/src/kernel/include/memory/firstfit_allocator.hpp @@ -0,0 +1,183 @@ + +/** + * @file firstfit_allocator.hpp + * @brief firstfit 内存分配器头文件 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#ifndef SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_HPP_ +#define SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_HPP_ + +#include +#include +#include + +#include "kernel_log.hpp" +#include "memory/allocator_base.hpp" + +/** + * @brief 使用 first fit 算法的分配器 + * @tparam page_size 页大小,默认 0x1000,即 4096 bytes + */ +template +class FirstFitAllocator : public AllocatorBase { + public: + /** + * @brief 构造函数 + * @param name 分配器名称 + * @param addr 开始地址 + * @param pages_count 页数量 + */ + explicit FirstFitAllocator(const char *name, uint64_t addr, + size_t pages_count) + : AllocatorBase(name, addr, pages_count) { + if (addr % page_size != 0) { + log::Err("addr not aligned. 0x%lX\n", addr); + throw; + } + printf("%s: 0x%p(0x%X pages) init.\n", name_, addr_, length_); + } + + /// @name 构造/析构函数 + /// @{ + FirstFitAllocator() = default; + FirstFitAllocator(const FirstFitAllocator &) = default; + FirstFitAllocator(FirstFitAllocator &&) = default; + auto operator=(const FirstFitAllocator &) -> FirstFitAllocator & = default; + auto operator=(FirstFitAllocator &&) -> FirstFitAllocator & = default; + ~FirstFitAllocator() = default; + /// @} + + /** + * @brief 分配长度为 pages_count 页的内存 + * @param pages_count 页数 + * @return uint64_t 分配的内存起点地址 + */ + uint64_t Alloc(size_t pages_count) override { + uint64_t res_addr = 0; + // 在位图中寻找连续 pages_count 的位置 + auto [is_found, idx] = Find(pages_count, false); + if (is_found == false) { + log::Warn("NO ENOUGH MEM %d.\n", pages_count); + return res_addr; + } + // 遍历区域 + for (auto i = idx; i < idx + pages_count; i++) { + // 置位,说明已使用 + map_[i] = 1; + } + // 计算实际地址 + // 分配器起始地址+页长度*第几页 + res_addr = addr_ + (page_size * idx); + // 更新统计信息 + free_length_ -= pages_count; + used_length_ += pages_count; + return res_addr; + } + + /** + * @brief 在 addr 处分配长度为 pages_count 页的内存 + * @param addr 指定的地址 + * @param pages_count 页数 + * @return true 成功 + * @return false 失败 + */ + bool AllocAt(uint64_t addr, size_t pages_count) override { + // 页对齐 + if (addr % page_size != 0) { + log::Warn("addr not aligned 0x%lX.\n", addr); + return false; + } + // 申请地址超出范围 + if (addr < addr_ || addr > addr_ || + addr + pages_count * page_size > addr_ + length_ * page_size) { + log::Warn("out of range 0x%lX %d.\n", addr, pages_count); + return false; + } + // 计算 addr 在 map_ 中的索引 + size_t idx = (addr - addr_) / page_size; + // 遍历 + for (auto i = idx; i < idx + pages_count; i++) { + // 如果在范围内有已经分配的内存,返回 false + if (map_[i] == true) { + return false; + } + } + // 到这里说明范围内没有已使用内存 + // 再次遍历 + for (auto i = idx; i < idx + pages_count; i++) { + // 置位 + map_[i] = 1; + } + // 更新统计信息 + free_length_ -= pages_count; + used_length_ += pages_count; + return true; + } + + /** + * @brief 释放 addr 处 pages_count 页的内存 + * @param addr 要释放内存起点地址 + * @param pages_count 页数 + */ + void Free(uint64_t addr, size_t pages_count) override { + // 页对齐 + if (addr % page_size != 0) { + log::Warn("addr not aligned 0x%lX.\n", addr); + return; + } + // 申请地址超出范围 + if (addr < addr_ || addr > addr_ || + addr + pages_count * page_size > addr_ + length_ * page_size) { + log::Warn("out of range 0x%lX %d.\n", addr, pages_count); + return; + } + // 计算 addr 在 map_ 中的索引 + size_t idx = (addr - addr_) / page_size; + for (auto i = idx; i < idx + pages_count; i++) { + map_[i] = 0; + } + // 更新统计信息 + free_length_ += pages_count; + used_length_ -= pages_count; + } + + private: + /// 位图,每一位表示一页内存,1 表示已使用,0 表示未使用 + std::bitset map_; + + /** + * @brief 寻找连续 pages_count 个 val 位,返回开始索引 + * @param pages_count 连续页数量 + * @param val 要寻找的页状态 + * @return std::pair 找到返回 ,失败返回 + */ + std::pair Find(size_t pages_count, bool val) const { + size_t count = 0; + size_t idx = 0; + // 遍历位图 + for (uint64_t i = 0; i < length_; i++) { + if (map_[i] != val) { + count = 0; + idx = i + 1; + } else { + count++; + } + if (count == pages_count) { + return {true, idx}; + } + } + return {false, 0}; + } +}; + +#endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_FIRSTFIT_ALLOCATOR_HPP_ */ diff --git a/src/kernel/include/memory/physical_memory_manager.h b/src/kernel/include/memory/physical_memory_manager.h index 74663627a..cdba265b6 100644 --- a/src/kernel/include/memory/physical_memory_manager.h +++ b/src/kernel/include/memory/physical_memory_manager.h @@ -35,6 +35,9 @@ */ class PhysicalMemoryManager { public: + /// 页大小 4096 bytes + static constexpr const uint64_t kPageSize = 0x1000; + /** * @brief 构造函数 * @param addr 物理地址起点 diff --git a/src/kernel/include/memory/virtual_memory_manager.hpp b/src/kernel/include/memory/virtual_memory_manager.hpp index 624e36688..b529890a2 100644 --- a/src/kernel/include/memory/virtual_memory_manager.hpp +++ b/src/kernel/include/memory/virtual_memory_manager.hpp @@ -23,164 +23,228 @@ #include "physical_memory_manager.h" #include "singleton.hpp" +// TODO: 可以优化 + +/// 页表项,最底层 +typedef uintptr_t pte_t; +/// 页表,也可以是页目录,它们的结构是一样的 +typedef uintptr_t* pt_t; + +/// 每个页表能映射多少页 = 页大小/页表项大小: 2^9 +static constexpr const size_t VMM_PAGES_PRE_PAGE_TABLE = + COMMON::PAGE_SIZE / sizeof(pte_t); + +/// 映射内核空间的大小 +static constexpr const size_t VMM_KERNEL_SPACE_SIZE = COMMON::KERNEL_SPACE_SIZE; + +/// 内核映射的页数 +static constexpr const size_t VMM_KERNEL_SPACE_PAGES = + VMM_KERNEL_SPACE_SIZE / COMMON::PAGE_SIZE; + +#if defined(__i386__) +/// P = 1 表示有效; P = 0 表示无效。 +static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; +/// 如果为 0 表示页面只读或可执行。 +static constexpr const uint8_t VMM_PAGE_READABLE = 0; +static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; +static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; +/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 +/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 +static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; +/// 内核虚拟地址相对物理地址的偏移 +static constexpr const size_t KERNEL_OFFSET = 0x0; +/// PTE 属性位数 +static constexpr const size_t VMM_PTE_PROP_BITS = 12; +/// PTE 页内偏移位数 +static constexpr const size_t VMM_PAGE_OFF_BITS = 12; +/// VPN 位数 +static constexpr const size_t VMM_VPN_BITS = 10; +/// VPN 位数掩码,10 位 VPN +static constexpr const size_t VMM_VPN_BITS_MASK = 0x3FF; +/// i386 使用了两级页表 +static constexpr const size_t VMM_PT_LEVEL = 2; + +#elif defined(__x86_64__) +/// P = 1 表示有效; P = 0 表示无效。 +static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; +/// 如果为 0 表示页面只读或可执行。 +static constexpr const uint8_t VMM_PAGE_READABLE = 0; +static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; +static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; +/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 +/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 +static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; +/// 内核虚拟地址相对物理地址的偏移 +static constexpr const size_t KERNEL_OFFSET = 0x0; +/// PTE 属性位数 +static constexpr const size_t VMM_PTE_PROP_BITS = 12; +/// PTE 页内偏移位数 +static constexpr const size_t VMM_PAGE_OFF_BITS = 12; +/// VPN 位数 +static constexpr const size_t VMM_VPN_BITS = 9; +/// VPN 位数掩码,9 位 VPN +static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; +/// x86_64 使用了四级页表 +static constexpr const size_t VMM_PT_LEVEL = 4; + +#elif defined(__riscv) +/// 有效位 +static constexpr const uint8_t VMM_PAGE_VALID = CPU::pte_t::VALID; +/// 可读位 +static constexpr const uint8_t VMM_PAGE_READABLE = CPU::pte_t::READ; +/// 可写位s +static constexpr const uint8_t VMM_PAGE_WRITABLE = CPU::pte_t::WRITE; +/// 可执行位 +static constexpr const uint8_t VMM_PAGE_EXECUTABLE = CPU::pte_t::EXEC; +/// 用户位 +static constexpr const uint8_t VMM_PAGE_USER = CPU::pte_t::USER; +/// 全局位,我们不会使用 +static constexpr const uint8_t VMM_PAGE_GLOBAL = CPU::pte_t::GLOBAL; +/// 已使用位,用于替换算法 +static constexpr const uint8_t VMM_PAGE_ACCESSED = CPU::pte_t::ACCESSED; +/// 已修改位,用于替换算法 +static constexpr const uint8_t VMM_PAGE_DIRTY = CPU::pte_t::DIRTY; +/// 内核虚拟地址相对物理地址的偏移 +static constexpr const size_t KERNEL_OFFSET = 0x0; +/// PTE 属性位数 +static constexpr const size_t VMM_PTE_PROP_BITS = 10; +/// PTE 页内偏移位数 +static constexpr const size_t VMM_PAGE_OFF_BITS = 12; +/// VPN 位数 +static constexpr const size_t VMM_VPN_BITS = 9; +/// VPN 位数掩码,9 位 VPN +static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; +/// riscv64 使用了三级页表 +static constexpr const size_t VMM_PT_LEVEL = 3; +#endif + /** - * @brief 虚拟内存管理接口 - * 对物理内存的管理来说 - * 1. 管理所有的物理内存,不论是否被机器保留/无法访问 - * 2. 内存开始地址与长度由 bootloader 给出: x86 下为 grub, riscv 下为 opensbi - * 3. - * 不关心内存是否被使用,但是默认的物理内存分配空间从内核结束后开始 - * 如果由体系结构需要分配内核开始前内存空间的,则尽量避免 - * 4. 最管理单位为页 + * @brief 虚拟地址到物理地址转换 + * @param _va 要转换的虚拟地址 + * @return constexpr uintptr_t 转换好的地址 */ -class VirtualMemoryManager { - public: - /** - * @brief 构造函数 - * @param addr 物理地址起点 - * @param pages_count 物理页数 - */ - explicit VirtualMemoryManager(uint64_t addr, size_t pages_count); - - /// @name 构造/析构函数 - /// @{ - VirtualMemoryManager() = default; - VirtualMemoryManager(const VirtualMemoryManager &) = default; - VirtualMemoryManager(VirtualMemoryManager &&) = default; - auto operator=(const VirtualMemoryManager &) -> VirtualMemoryManager & = - default; - auto operator=(VirtualMemoryManager &&) -> VirtualMemoryManager & = default; - ~VirtualMemoryManager() = default; - /// @} +static constexpr uintptr_t VMM_VA2PA(uintptr_t _va) { + return _va - KERNEL_OFFSET; +} - /** - * @brief 获取物理内存页数 - * @return size_t 物理内存页数 - */ - size_t GetPagesCount() const; - - /** - * @brief 获取内核空间起始地址 - * @return uint64_t 内核空间起始地址 - */ - uint64_t GetKernelSpaceAddr() const; - - /** - * @brief 获取内核空间页数 - * @return size_t 内核空间页数 - */ - size_t GetKernelSpacePagesCount() const; +/** + * @brief 物理地址到虚拟地址转换 + * @param _pa 要转换的物理地址 + * @return constexpr uintptr_t 转换好的地址 + */ +static constexpr uintptr_t VMM_PA2VA(uintptr_t _pa) { + return _pa + KERNEL_OFFSET; +} +/** + * @brief 虚拟内存抽象 + */ +class VirtualMemoryManager { + private: /** - * @brief 获取用户间起始地址 - * @return uint64_t 用户间起始地址 + * @brief 物理地址转换到页表项 + * @param _pa 物理地址 + * @return constexpr uintptr_t 对应的虚拟地址 + * @note 0~11: pte 属性 + * 12~31: 页表的物理页地址 */ - uint64_t GetUserSpaceAddr() const; + static constexpr uintptr_t PA2PTE(uintptr_t _pa) { + return (_pa >> VMM_PAGE_OFF_BITS) << VMM_PTE_PROP_BITS; + } /** - * @brief 获取用户间页数 - * @return size_t 用户间页数 + * @brief 页表项转换到物理地址 + * @param _pte 页表 + * @return constexpr uintptr_t 对应的物理地址 */ - size_t GetUserSpacePagesCount() const; + static constexpr uintptr_t PTE2PA(const pte_t _pte) { + return (((uintptr_t)_pte) >> VMM_PTE_PROP_BITS) << VMM_PAGE_OFF_BITS; + } /** - * @brief 获取当前已使用页数 - * @return size_t 已使用页数 + * @brief 计算 X 级页表的位置 + * @param _level 级别 + * @return constexpr uintptr_t 偏移 */ - size_t GetUsedPagesCount() const; + static constexpr uintptr_t PXSHIFT(const size_t _level) { + return VMM_PAGE_OFF_BITS + (VMM_VPN_BITS * _level); + } /** - * @brief 获取当前空闲页数 - * @return size_t 空闲页数 + * @brief 获取 _va 的第 _level 级 VPN + * @note 例如虚拟地址右移 12+(10 * _level) 位, + * 得到的就是第 _level 级页表的 VPN */ - size_t GetFreePagesCount() const; + static constexpr uintptr_t PX(size_t _level, uintptr_t _va) { + return (_va >> PXSHIFT(_level)) & VMM_VPN_BITS_MASK; + } /** - * @brief 分配一页 - * @return uint64_t 分配的内存起始地址 + * @brief 在 _pgd 中查找 _va 对应的页表项 + * 如果未找到,_alloc 为真时会进行分配 + * @param _pgd 要查找的页目录 + * @param _va 虚拟地址 + * @param _alloc 是否分配 + * @return pte_t* 未找到返回 nullptr */ - uint64_t AllocUserPage(); + pte_t* find(const pt_t _pgd, uintptr_t _va, bool _alloc); + protected: + public: /** - * @brief 分配多页 - * @param _len 页数 - * @return uint64_t 分配的内存起始地址 + * @brief 获取单例 + * @return VirtualMemoryManager& 静态对象 */ - uint64_t AllocUserPages(size_t _len); + static VirtualMemoryManager& get_instance(void); /** - * @brief 分配以指定地址开始的 _len 页 - * @param addr 指定的地址 - * @param _len 页数 + * @brief 初始化 * @return true 成功 * @return false 失败 */ - bool AllocUserPagesAt(uint64_t addr, size_t _len); - - /** - * @brief 在内核空间申请一页 - * @return uint64_t 分配的内存起始地址 - */ - uint64_t AllocKernelPage(); + bool init(void); /** - * @brief 在内核空间分配 pages_count 页 - * @param pages_count 页数 - * @return uint64_t 分配到的内存起始地址 + * @brief 获取当前页目录 + * @return pt_t 当前页目录 */ - uint64_t AllocKernelPages(size_t pages_count); + pt_t get_pgd(void); /** - * @brief 在内核空间分配以指定地址开始的 _len 页 - * @param addr 指定的地址 - * @param pages_count 页数 - * @return true 成功 - * @return false 失败 + * @brief 设置当前页目录 + * @param _pgd 要设置的页目录 */ - bool AllocKernelPagesAt(uint64_t addr, size_t pages_count); + void set_pgd(const pt_t _pgd); /** - * @brief 回收一页 - * @param addr 要回收的地址 + * @brief 映射物理地址到虚拟地址 + * @param _pgd 要使用的页目录 + * @param _va 要映射的虚拟地址 + * @param _pa 物理地址 + * @param _flag 属性 */ - void FreePage(uint64_t addr); + void mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag); /** - * @brief 回收多页 - * @param addr 要回收的地址 - * @param pages_count 页数 + * @brief 取消映射 + * @param _pgd 要操作的页目录 + * @param _va 要取消映射的虚拟地址 */ - void FreePages(uint64_t addr, size_t pages_count); - - private: - /// 物理内存开始地址 - uint64_t addr_; - /// 物理内存页数 - size_t pages_count_; - /// 内核空间起始地址 - uint64_t kernel_addr_; - /// 内核页数 - size_t kernel_pages_count_; - /// 用户空间起始地址 - uint64_t user_start_; - /// 用户空间页数 - size_t user_pages_count_; - - /// 内核空间不会位于内存中间,导致出现用户间被切割为两部分的情况 - /// 物理内存分配器,分配内核空间 - AllocatorBase *kernel_allocator_; - /// 物理内存分配器,分配用户空间 - AllocatorBase *user_allocator_; + void unmmap(const pt_t _pgd, uintptr_t _va); /** - * @brief 将 elf 与 dtb - * 信息移动到内核空间,位于内核结束后的下一页,分别占用一页 + * @brief 获取映射的物理地址 + * @param _pgd 页目录 + * @param _va 虚拟地址 + * @param _pa 如果已经映射,保存映射的物理地址,否则为 nullptr + * @return true 已映射 + * @return false 未映射 */ - void MoveElfDtb(); + bool get_mmap(const pt_t _pgd, uintptr_t _va, const void* _pa); }; -/// 全局物理内存管理器 -[[maybe_unused]] static Singleton kPhysicalMemoryManager; +/// 全局虚拟内存管理器 +[[maybe_unused]] static Singleton kVirtualMemoryManager; #endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_HPP_ \ */ diff --git a/src/kernel/physical_memory_manager.cpp b/src/kernel/physical_memory_manager.cpp index 306afcdb0..ddf74f2b6 100644 --- a/src/kernel/physical_memory_manager.cpp +++ b/src/kernel/physical_memory_manager.cpp @@ -23,9 +23,8 @@ #include "kernel_elf.hpp" #include "kernel_fdt.hpp" #include "kernel_log.hpp" -#include "memory/firstfit_allocator.h" +#include "memory/firstfit_allocator.hpp" #include "project_config.h" -// #include "resource.h" PhysicalMemoryManager::PhysicalMemoryManager(uint64_t addr, size_t pages_count) : addr_(addr), pages_count_(pages_count) { @@ -40,11 +39,11 @@ PhysicalMemoryManager::PhysicalMemoryManager(uint64_t addr, size_t pages_count) // 创建分配器 // 内核空间 - static FirstFitAllocator first_fit_allocator_kernel( + static FirstFitAllocator first_fit_allocator_kernel( "First Fit Allocator(Kernel space)", kernel_addr_, kernel_pages_count_); kernel_allocator_ = (AllocatorBase*)&first_fit_allocator_kernel; // 用户空间 - static FirstFitAllocator first_fit_allocator( + static FirstFitAllocator first_fit_allocator( "First Fit Allocator(User space)", user_start_, user_pages_count_); user_allocator_ = (AllocatorBase*)&first_fit_allocator; diff --git a/src/kernel/virtual_memory_manager.cpp b/src/kernel/virtual_memory_manager.cpp new file mode 100644 index 000000000..14d3a1bc3 --- /dev/null +++ b/src/kernel/virtual_memory_manager.cpp @@ -0,0 +1,191 @@ + +/** + * @file virtual_memory_manager.cpp + * @brief 虚拟内存管理 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#include "memory/virtual_memory_manager.hpp" + +#include +#include + +#include "basic_info.hpp" +#include "kernel_elf.hpp" +#include "kernel_fdt.hpp" +#include "kernel_log.hpp" +#include "memory/firstfit_allocator.h" +#include "project_config.h" +// #include "resource.h" + +// 在 _pgd 中查找 _va 对应的页表项 +// 如果未找到,_alloc 为真时会进行分配 +pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { + pt_t pgd = _pgd; + // sv39 共有三级页表,一级一级查找 + // -1 是因为最后一级是具体的某一页,在函数最后直接返回 + for (size_t level = VMM_PT_LEVEL - 1; level > 0; level--) { + // 每次循环会找到 _va 的第 level 级页表 pgd + // 相当于 pgd_level[VPN_level],这样相当于得到了第 level 级页表的地址 + pte_t* pte = (pte_t*)&pgd[PX(level, _va)]; + // 解引用 pte,如果有效,获取 level+1 级页表, + if ((*pte & VMM_PAGE_VALID) == 1) { + // pgd 指向下一级页表 + // *pte 保存的是页表项,需要转换为对应的物理地址 + pgd = (pt_t)PTE2PA(*pte); + } + // 如果无效 + else { + // 判断是否需要分配 + // 如果需要 + if (_alloc == true) { + // 申请新的物理页 + pgd = (pt_t)PMM::get_instance().alloc_page_kernel(); + bzero(pgd, COMMON::PAGE_SIZE); + // 申请失败则返回 + if (pgd == nullptr) { + // 如果出现这种情况,说明物理内存不够,一般不会出现 + assert(0); + return nullptr; + } + // 清零 + bzero(pgd, COMMON::PAGE_SIZE); + // 填充页表项 + *pte = PA2PTE((uintptr_t)pgd) | VMM_PAGE_VALID; + } + // 不分配的话直接返回 + else { + return nullptr; + } + } + } + // 0 最低级 pt + return &pgd[PX(0, _va)]; +} + +VirtualMemoryManager& VirtualMemoryManager::get_instance(void) { + /// 定义全局 VirtualMemoryManager 对象 + static VirtualMemoryManager vmm; + return vmm; +} + +bool VirtualMemoryManager::init(void) { +#if defined(__i386__) || defined(__x86_64__) + GDT::init(); +#endif + // 分配一页用于保存页目录 + pt_t pgd_kernel = (pt_t)PMM::get_instance().alloc_page_kernel(); + bzero(pgd_kernel, COMMON::PAGE_SIZE); + // 映射内核空间 + for (uintptr_t addr = (uintptr_t)COMMON::KERNEL_START_ADDR; + addr < (uintptr_t)COMMON::KERNEL_START_ADDR + VMM_KERNEL_SPACE_SIZE; + addr += COMMON::PAGE_SIZE) { + // TODO: 区分代码/数据等段分别映射 + mmap(pgd_kernel, addr, addr, + VMM_PAGE_READABLE | VMM_PAGE_WRITABLE | VMM_PAGE_EXECUTABLE); + } + // 设置页目录 + set_pgd(pgd_kernel); + // 开启分页 + CPU::ENABLE_PG(); + info("vmm init.\n"); + return 0; +} + +pt_t VirtualMemoryManager::get_pgd(void) { return (pt_t)CPU::GET_PGD(); } + +void VirtualMemoryManager::set_pgd(const pt_t _pgd) { + // 设置页目录 + CPU::SET_PGD((uintptr_t)_pgd); + // 刷新缓存 + CPU::VMM_FLUSH(0); + return; +} + +void VirtualMemoryManager::mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, + uint32_t _flag) { + pte_t* pte = find(_pgd, _va, true); + // 一般情况下不应该为空 + assert(pte != nullptr); + // 已经映射过了 且 flag 没有变化 + if (((*pte & VMM_PAGE_VALID) == VMM_PAGE_VALID) && + ((*pte & ((1 << VMM_PTE_PROP_BITS) - 1)) == _flag)) { + warn("remap.\n"); + } + // 没有映射,或更改了 flag + else { + // 那么设置 *pte + // pte 解引用后的值是页表项 + *pte = PA2PTE(_pa) | _flag | (*pte & ((1 << VMM_PTE_PROP_BITS) - 1)) | + VMM_PAGE_VALID; + // 刷新缓存 + CPU::VMM_FLUSH(0); + } + return; +} + +void VirtualMemoryManager::unmmap(const pt_t _pgd, uintptr_t _va) { + pte_t* pte = find(_pgd, _va, false); + // 找到页表项 + // 未找到 + if (pte == nullptr) { + warn("VirtualMemoryManager::unmmap: find.\n"); + return; + } + // 找到了,但是并没有被映射 + if ((*pte & VMM_PAGE_VALID) == 0) { + warn("VirtualMemoryManager::unmmap: not mapped.\n"); + } + // 置零 + *pte = 0x00; + // 刷新缓存 + CPU::VMM_FLUSH(0); + // TODO: 如果一页表都被 unmap,释放占用的物理内存 + return; +} + +bool VirtualMemoryManager::get_mmap(const pt_t _pgd, uintptr_t _va, + const void* _pa) { + pte_t* pte = find(_pgd, _va, false); + bool res = false; + // pte 不为空且有效,说明映射了 + if ((pte != nullptr) && ((*pte & VMM_PAGE_VALID) == 1)) { + // 如果 _pa 不为空 + if (_pa != nullptr) { + // 设置 _pa + // 将页表项转换为物理地址 + *(uintptr_t*)_pa = PTE2PA(*pte); + } + // 返回 true + res = true; + } + // 否则说明没有映射 + else { + // 如果 _pa 不为空 + if (_pa != nullptr) { + // 设置 _pa + *(uintptr_t*)_pa = (uintptr_t) nullptr; + } + } + return res; +} + +uint32_t VirtualMemoryInit(uint32_t, uint8_t*) { + // 初始化虚拟内存管理器 + kVirtualMemoryManager.GetInstance() = + VirtualMemoryManager(kBasicInfo.GetInstance().physical_memory_addr, + kBasicInfo.GetInstance().physical_memory_size); + + log::Info("Hello VirtualMemoryInit\n"); + + return 0; +} diff --git a/src/project_config.h b/src/project_config.h index 53f16bf84..bf8b37db4 100644 --- a/src/project_config.h +++ b/src/project_config.h @@ -25,9 +25,6 @@ #define SIMPLEKERNEL_DEBUG_LOG #endif -/// 使用 4KB 页大小 -#define kPageSize (0x1000) - /// 内核空间设为 64MB #define kKernelSpaceSize (0x4000000) diff --git a/tools/project_config.h.in b/tools/project_config.h.in index c37dd3903..e9ee5237d 100644 --- a/tools/project_config.h.in +++ b/tools/project_config.h.in @@ -25,9 +25,6 @@ #define SIMPLEKERNEL_DEBUG_LOG #endif -/// 使用 4KB 页大小 -#define kPageSize (0x1000) - /// 内核空间设为 64MB #define kKernelSpaceSize (0x4000000) From 61b29bdd5dc05418029a0766d616953024edb1c0 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Fri, 30 Aug 2024 02:33:55 +0000 Subject: [PATCH 10/17] feat: working on vmm Signed-off-by: Zone.N --- src/kernel/arch/riscv64/include/cpu.hpp | 46 +++- src/kernel/arch/x86_64/include/cpu.hpp | 26 +- .../include/memory/virtual_memory_manager.h | 165 ++++++++++++ .../include/memory/virtual_memory_manager.hpp | 250 ------------------ src/kernel/virtual_memory_manager.cpp | 39 ++- 5 files changed, 249 insertions(+), 277 deletions(-) create mode 100644 src/kernel/include/memory/virtual_memory_manager.h delete mode 100644 src/kernel/include/memory/virtual_memory_manager.hpp diff --git a/src/kernel/arch/riscv64/include/cpu.hpp b/src/kernel/arch/riscv64/include/cpu.hpp index 634f478bb..01d46e35d 100644 --- a/src/kernel/arch/riscv64/include/cpu.hpp +++ b/src/kernel/arch/riscv64/include/cpu.hpp @@ -38,6 +38,37 @@ */ namespace cpu { +// namespace vmm_info { +// /// 有效位 +// static constexpr const uint8_t VMM_PAGE_VALID = CPU::pte_t::VALID; +// /// 可读位 +// static constexpr const uint8_t VMM_PAGE_READABLE = CPU::pte_t::READ; +// /// 可写位s +// static constexpr const uint8_t VMM_PAGE_WRITABLE = CPU::pte_t::WRITE; +// /// 可执行位 +// static constexpr const uint8_t VMM_PAGE_EXECUTABLE = CPU::pte_t::EXEC; +// /// 用户位 +// static constexpr const uint8_t VMM_PAGE_USER = CPU::pte_t::USER; +// /// 全局位,我们不会使用 +// static constexpr const uint8_t VMM_PAGE_GLOBAL = CPU::pte_t::GLOBAL; +// /// 已使用位,用于替换算法 +// static constexpr const uint8_t VMM_PAGE_ACCESSED = CPU::pte_t::ACCESSED; +// /// 已修改位,用于替换算法 +// static constexpr const uint8_t VMM_PAGE_DIRTY = CPU::pte_t::DIRTY; +// /// 内核虚拟地址相对物理地址的偏移 +// static constexpr const size_t KERNEL_OFFSET = 0x0; +// /// PTE 属性位数 +// static constexpr const size_t VMM_PTE_PROP_BITS = 10; +// /// PTE 页内偏移位数 +// static constexpr const size_t VMM_PAGE_OFF_BITS = 12; +// /// VPN 位数 +// static constexpr const size_t VMM_VPN_BITS = 9; +// /// VPN 位数掩码,9 位 VPN +// static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; +// /// riscv64 使用了三级页表 +// static constexpr const size_t VMM_PT_LEVEL = 3; +// }; // namespace vmm_info + // 第一部分:寄存器定义 namespace reginfo { @@ -1184,7 +1215,8 @@ class Sstatus : public ReadWriteRegBase { virtual ~Sstatus() = default; /// @} - friend sk_std::ostream &operator<<(sk_std::ostream &os, const Sstatus &sstatus) { + friend sk_std::ostream &operator<<(sk_std::ostream &os, + const Sstatus &sstatus) { auto sie = sstatus.sie.Get(); auto spie = sstatus.spie.Get(); auto spp = sstatus.spp.Get(); @@ -1348,7 +1380,8 @@ class Instret : public ReadOnlyRegBase { virtual ~Instret() = default; /// @} - friend sk_std::ostream &operator<<(sk_std::ostream &os, const Instret &instret) { + friend sk_std::ostream &operator<<(sk_std::ostream &os, + const Instret &instret) { printf("val: 0x%p", (void *)instret.Read()); return os; } @@ -1366,7 +1399,8 @@ class Sscratch : public ReadWriteRegBase { virtual ~Sscratch() = default; /// @} - friend sk_std::ostream &operator<<(sk_std::ostream &os, const Sscratch &sscratch) { + friend sk_std::ostream &operator<<(sk_std::ostream &os, + const Sscratch &sscratch) { printf("val: 0x%p", (void *)sscratch.Read()); return os; } @@ -1409,7 +1443,8 @@ class Scause : public ReadWriteRegBase { virtual ~Scause() = default; /// @} - friend sk_std::ostream &operator<<(sk_std::ostream &os, const Scause &scause) { + friend sk_std::ostream &operator<<(sk_std::ostream &os, + const Scause &scause) { auto exception_code = scause.exception_code.Get(); auto interrupt = scause.interrupt.Get(); printf("val: 0x%p, exception_code: 0x%lX, interrupt: %s, name: %s", @@ -1483,7 +1518,8 @@ class Stimecmp : public ReadOnlyRegBase { virtual ~Stimecmp() = default; /// @} - friend sk_std::ostream &operator<<(sk_std::ostream &os, const Stimecmp &stimecmp) { + friend sk_std::ostream &operator<<(sk_std::ostream &os, + const Stimecmp &stimecmp) { printf("val: 0x%p", (void *)stimecmp.Read()); return os; } diff --git a/src/kernel/arch/x86_64/include/cpu.hpp b/src/kernel/arch/x86_64/include/cpu.hpp index f6c2e88b7..4918679de 100644 --- a/src/kernel/arch/x86_64/include/cpu.hpp +++ b/src/kernel/arch/x86_64/include/cpu.hpp @@ -22,9 +22,9 @@ #include #include +#include "kernel_log.hpp" #include "sk_cstdio" #include "sk_iostream" -#include "kernel_log.hpp" /** * x86_64 cpu 相关定义 @@ -467,6 +467,30 @@ class Pit { volatile size_t ticks_ = 0; }; +namespace vmm_info { +/// P = 1 表示有效; P = 0 表示无效。 +static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; +/// 如果为 0 表示页面只读或可执行。 +static constexpr const uint8_t VMM_PAGE_READABLE = 0; +static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; +static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; +/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 +/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 +static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; +/// 内核虚拟地址相对物理地址的偏移 +static constexpr const size_t KERNEL_OFFSET = 0x0; +/// PTE 属性位数 +static constexpr const size_t VMM_PTE_PROP_BITS = 12; +/// PTE 页内偏移位数 +static constexpr const size_t VMM_PAGE_OFF_BITS = 12; +/// VPN 位数 +static constexpr const size_t VMM_VPN_BITS = 9; +/// VPN 位数掩码,9 位 VPN +static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; +/// x86_64 使用了四级页表 +static constexpr const size_t VMM_PT_LEVEL = 4; +}; // namespace vmm_info + // 第一部分:寄存器定义 namespace reginfo { diff --git a/src/kernel/include/memory/virtual_memory_manager.h b/src/kernel/include/memory/virtual_memory_manager.h new file mode 100644 index 000000000..44874cc7c --- /dev/null +++ b/src/kernel/include/memory/virtual_memory_manager.h @@ -0,0 +1,165 @@ + +/** + * @file virtual_memory_manager.h + * @brief 虚拟内存管理 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#ifndef SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_H_ +#define SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_H_ + +#include +#include + +#include "cpu.hpp" +#include "physical_memory_manager.h" +#include "singleton.hpp" + +// TODO: 可以优化 + +/// 页表项,最底层 +typedef uintptr_t pte_t; +/// 页表,也可以是页目录,它们的结构是一样的 +typedef uintptr_t* pt_t; + +/// 每个页表能映射多少页 = 页大小/页表项大小: 2^9 +static constexpr const size_t VMM_PAGES_PRE_PAGE_TABLE = + PhysicalMemoryManager::kPageSize / sizeof(pte_t); + +/// 映射内核空间的大小 +static constexpr const size_t VMM_KERNEL_SPACE_SIZE = COMMON::KERNEL_SPACE_SIZE; + +/// 内核映射的页数 +static constexpr const size_t VMM_KERNEL_SPACE_PAGES = + VMM_KERNEL_SPACE_SIZE / PhysicalMemoryManager::kPageSize; + +/** + * @brief 虚拟内存抽象 + * 设计目标:在内核中存在若干个 VirtualMemoryManager + * 实例,内核拥有一个静态实例,每个进程有独立的 VirtualMemoryManager + * 另外要考虑:每个 cpu 维护自己的页寄存器,调度算法需要考虑到 + * - 页表(保存到寄存器) + * - 一级页表项 + * - 二级页表项 + * - 三级页表项(最低级) + */ +class VirtualMemoryManager { + private: + /** + * @brief 物理地址转换到页表项 + * @param _pa 物理地址 + * @return constexpr uintptr_t 对应的虚拟地址 + * @note 0~11: pte 属性 + * 12~31: 页表的物理页地址 + */ + static constexpr uintptr_t PA2PTE(uintptr_t _pa) { + return (_pa >> cpu::vmm_info::VMM_PAGE_OFF_BITS) + << cpu::vmm_info::VMM_PTE_PROP_BITS; + } + + /** + * @brief 页表项转换到物理地址 + * @param _pte 页表 + * @return constexpr uintptr_t 对应的物理地址 + */ + static constexpr uintptr_t PTE2PA(const pte_t _pte) { + return (((uintptr_t)_pte) >> cpu::vmm_info::VMM_PTE_PROP_BITS) + << cpu::vmm_info::VMM_PAGE_OFF_BITS; + } + + /** + * @brief 计算 X 级页表的位置 + * @param _level 级别 + * @return constexpr uintptr_t 偏移 + */ + static constexpr uintptr_t PXSHIFT(const size_t _level) { + return cpu::vmm_info::VMM_PAGE_OFF_BITS + + (cpu::vmm_info::VMM_VPN_BITS * _level); + } + + /** + * @brief 获取 _va 的第 _level 级 VPN + * @note 例如虚拟地址右移 12+(10 * _level) 位, + * 得到的就是第 _level 级页表的 VPN + */ + static constexpr uintptr_t PX(size_t _level, uintptr_t _va) { + return (_va >> PXSHIFT(_level)) & cpu::vmm_info::VMM_VPN_BITS_MASK; + } + + /** + * @brief 在 _pgd 中查找 _va 对应的页表项 + * 如果未找到,_alloc 为真时会进行分配 + * @param _pgd 要查找的页目录 + * @param _va 虚拟地址 + * @param _alloc 是否分配 + * @return pte_t* 未找到返回 nullptr + */ + pte_t* find(const pt_t _pgd, uintptr_t _va, bool _alloc); + + protected: + public: + /** + * @brief 获取单例 + * @return VirtualMemoryManager& 静态对象 + */ + static VirtualMemoryManager& get_instance(void); + + /** + * @brief 初始化 + * @return true 成功 + * @return false 失败 + */ + bool init(void); + + /** + * @brief 获取当前页目录 + * @return pt_t 当前页目录 + */ + pt_t get_pgd(void); + + /** + * @brief 设置当前页目录 + * @param _pgd 要设置的页目录 + */ + void set_pgd(const pt_t _pgd); + + /** + * @brief 映射物理地址到虚拟地址 + * @param _pgd 要使用的页目录 + * @param _va 要映射的虚拟地址 + * @param _pa 物理地址 + * @param _flag 属性 + */ + void mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag); + + /** + * @brief 取消映射 + * @param _pgd 要操作的页目录 + * @param _va 要取消映射的虚拟地址 + */ + void unmmap(const pt_t _pgd, uintptr_t _va); + + /** + * @brief 获取映射的物理地址 + * @param _pgd 页目录 + * @param _va 虚拟地址 + * @param _pa 如果已经映射,保存映射的物理地址,否则为 nullptr + * @return true 已映射 + * @return false 未映射 + */ + bool get_mmap(const pt_t _pgd, uintptr_t _va, const void* _pa); +}; + +/// 全局虚拟内存管理器 +[[maybe_unused]] static Singleton kVirtualMemoryManager; + +#endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_H_ */ diff --git a/src/kernel/include/memory/virtual_memory_manager.hpp b/src/kernel/include/memory/virtual_memory_manager.hpp deleted file mode 100644 index b529890a2..000000000 --- a/src/kernel/include/memory/virtual_memory_manager.hpp +++ /dev/null @@ -1,250 +0,0 @@ - -/** - * @file virtual_memory_manager.hpp - * @brief 虚拟内存管理 - * @author Zone.N (Zone.Niuzh@hotmail.com) - * @version 1.0 - * @date 2021-09-18 - * @copyright MIT LICENSE - * https://github.com/Simple-XX/SimpleKernel - * @par change log: - * - *
DateAuthorDescription - *
2021-09-18digmouse233迁移到 doxygen - *
- */ - -#ifndef SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_HPP_ -#define SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_HPP_ - -#include -#include - -#include "physical_memory_manager.h" -#include "singleton.hpp" - -// TODO: 可以优化 - -/// 页表项,最底层 -typedef uintptr_t pte_t; -/// 页表,也可以是页目录,它们的结构是一样的 -typedef uintptr_t* pt_t; - -/// 每个页表能映射多少页 = 页大小/页表项大小: 2^9 -static constexpr const size_t VMM_PAGES_PRE_PAGE_TABLE = - COMMON::PAGE_SIZE / sizeof(pte_t); - -/// 映射内核空间的大小 -static constexpr const size_t VMM_KERNEL_SPACE_SIZE = COMMON::KERNEL_SPACE_SIZE; - -/// 内核映射的页数 -static constexpr const size_t VMM_KERNEL_SPACE_PAGES = - VMM_KERNEL_SPACE_SIZE / COMMON::PAGE_SIZE; - -#if defined(__i386__) -/// P = 1 表示有效; P = 0 表示无效。 -static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; -/// 如果为 0 表示页面只读或可执行。 -static constexpr const uint8_t VMM_PAGE_READABLE = 0; -static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; -static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; -/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 -/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 -static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; -/// 内核虚拟地址相对物理地址的偏移 -static constexpr const size_t KERNEL_OFFSET = 0x0; -/// PTE 属性位数 -static constexpr const size_t VMM_PTE_PROP_BITS = 12; -/// PTE 页内偏移位数 -static constexpr const size_t VMM_PAGE_OFF_BITS = 12; -/// VPN 位数 -static constexpr const size_t VMM_VPN_BITS = 10; -/// VPN 位数掩码,10 位 VPN -static constexpr const size_t VMM_VPN_BITS_MASK = 0x3FF; -/// i386 使用了两级页表 -static constexpr const size_t VMM_PT_LEVEL = 2; - -#elif defined(__x86_64__) -/// P = 1 表示有效; P = 0 表示无效。 -static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; -/// 如果为 0 表示页面只读或可执行。 -static constexpr const uint8_t VMM_PAGE_READABLE = 0; -static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; -static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; -/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 -/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 -static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; -/// 内核虚拟地址相对物理地址的偏移 -static constexpr const size_t KERNEL_OFFSET = 0x0; -/// PTE 属性位数 -static constexpr const size_t VMM_PTE_PROP_BITS = 12; -/// PTE 页内偏移位数 -static constexpr const size_t VMM_PAGE_OFF_BITS = 12; -/// VPN 位数 -static constexpr const size_t VMM_VPN_BITS = 9; -/// VPN 位数掩码,9 位 VPN -static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; -/// x86_64 使用了四级页表 -static constexpr const size_t VMM_PT_LEVEL = 4; - -#elif defined(__riscv) -/// 有效位 -static constexpr const uint8_t VMM_PAGE_VALID = CPU::pte_t::VALID; -/// 可读位 -static constexpr const uint8_t VMM_PAGE_READABLE = CPU::pte_t::READ; -/// 可写位s -static constexpr const uint8_t VMM_PAGE_WRITABLE = CPU::pte_t::WRITE; -/// 可执行位 -static constexpr const uint8_t VMM_PAGE_EXECUTABLE = CPU::pte_t::EXEC; -/// 用户位 -static constexpr const uint8_t VMM_PAGE_USER = CPU::pte_t::USER; -/// 全局位,我们不会使用 -static constexpr const uint8_t VMM_PAGE_GLOBAL = CPU::pte_t::GLOBAL; -/// 已使用位,用于替换算法 -static constexpr const uint8_t VMM_PAGE_ACCESSED = CPU::pte_t::ACCESSED; -/// 已修改位,用于替换算法 -static constexpr const uint8_t VMM_PAGE_DIRTY = CPU::pte_t::DIRTY; -/// 内核虚拟地址相对物理地址的偏移 -static constexpr const size_t KERNEL_OFFSET = 0x0; -/// PTE 属性位数 -static constexpr const size_t VMM_PTE_PROP_BITS = 10; -/// PTE 页内偏移位数 -static constexpr const size_t VMM_PAGE_OFF_BITS = 12; -/// VPN 位数 -static constexpr const size_t VMM_VPN_BITS = 9; -/// VPN 位数掩码,9 位 VPN -static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; -/// riscv64 使用了三级页表 -static constexpr const size_t VMM_PT_LEVEL = 3; -#endif - -/** - * @brief 虚拟地址到物理地址转换 - * @param _va 要转换的虚拟地址 - * @return constexpr uintptr_t 转换好的地址 - */ -static constexpr uintptr_t VMM_VA2PA(uintptr_t _va) { - return _va - KERNEL_OFFSET; -} - -/** - * @brief 物理地址到虚拟地址转换 - * @param _pa 要转换的物理地址 - * @return constexpr uintptr_t 转换好的地址 - */ -static constexpr uintptr_t VMM_PA2VA(uintptr_t _pa) { - return _pa + KERNEL_OFFSET; -} - -/** - * @brief 虚拟内存抽象 - */ -class VirtualMemoryManager { - private: - /** - * @brief 物理地址转换到页表项 - * @param _pa 物理地址 - * @return constexpr uintptr_t 对应的虚拟地址 - * @note 0~11: pte 属性 - * 12~31: 页表的物理页地址 - */ - static constexpr uintptr_t PA2PTE(uintptr_t _pa) { - return (_pa >> VMM_PAGE_OFF_BITS) << VMM_PTE_PROP_BITS; - } - - /** - * @brief 页表项转换到物理地址 - * @param _pte 页表 - * @return constexpr uintptr_t 对应的物理地址 - */ - static constexpr uintptr_t PTE2PA(const pte_t _pte) { - return (((uintptr_t)_pte) >> VMM_PTE_PROP_BITS) << VMM_PAGE_OFF_BITS; - } - - /** - * @brief 计算 X 级页表的位置 - * @param _level 级别 - * @return constexpr uintptr_t 偏移 - */ - static constexpr uintptr_t PXSHIFT(const size_t _level) { - return VMM_PAGE_OFF_BITS + (VMM_VPN_BITS * _level); - } - - /** - * @brief 获取 _va 的第 _level 级 VPN - * @note 例如虚拟地址右移 12+(10 * _level) 位, - * 得到的就是第 _level 级页表的 VPN - */ - static constexpr uintptr_t PX(size_t _level, uintptr_t _va) { - return (_va >> PXSHIFT(_level)) & VMM_VPN_BITS_MASK; - } - - /** - * @brief 在 _pgd 中查找 _va 对应的页表项 - * 如果未找到,_alloc 为真时会进行分配 - * @param _pgd 要查找的页目录 - * @param _va 虚拟地址 - * @param _alloc 是否分配 - * @return pte_t* 未找到返回 nullptr - */ - pte_t* find(const pt_t _pgd, uintptr_t _va, bool _alloc); - - protected: - public: - /** - * @brief 获取单例 - * @return VirtualMemoryManager& 静态对象 - */ - static VirtualMemoryManager& get_instance(void); - - /** - * @brief 初始化 - * @return true 成功 - * @return false 失败 - */ - bool init(void); - - /** - * @brief 获取当前页目录 - * @return pt_t 当前页目录 - */ - pt_t get_pgd(void); - - /** - * @brief 设置当前页目录 - * @param _pgd 要设置的页目录 - */ - void set_pgd(const pt_t _pgd); - - /** - * @brief 映射物理地址到虚拟地址 - * @param _pgd 要使用的页目录 - * @param _va 要映射的虚拟地址 - * @param _pa 物理地址 - * @param _flag 属性 - */ - void mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag); - - /** - * @brief 取消映射 - * @param _pgd 要操作的页目录 - * @param _va 要取消映射的虚拟地址 - */ - void unmmap(const pt_t _pgd, uintptr_t _va); - - /** - * @brief 获取映射的物理地址 - * @param _pgd 页目录 - * @param _va 虚拟地址 - * @param _pa 如果已经映射,保存映射的物理地址,否则为 nullptr - * @return true 已映射 - * @return false 未映射 - */ - bool get_mmap(const pt_t _pgd, uintptr_t _va, const void* _pa); -}; - -/// 全局虚拟内存管理器 -[[maybe_unused]] static Singleton kVirtualMemoryManager; - -#endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_MEMORY_VIRTUAL_MEMORY_MANAGER_HPP_ \ - */ diff --git a/src/kernel/virtual_memory_manager.cpp b/src/kernel/virtual_memory_manager.cpp index 14d3a1bc3..51251aeb9 100644 --- a/src/kernel/virtual_memory_manager.cpp +++ b/src/kernel/virtual_memory_manager.cpp @@ -14,18 +14,12 @@ * */ -#include "memory/virtual_memory_manager.hpp" +#include "memory/virtual_memory_manager.h" #include #include -#include "basic_info.hpp" -#include "kernel_elf.hpp" -#include "kernel_fdt.hpp" #include "kernel_log.hpp" -#include "memory/firstfit_allocator.h" -#include "project_config.h" -// #include "resource.h" // 在 _pgd 中查找 _va 对应的页表项 // 如果未找到,_alloc 为真时会进行分配 @@ -33,12 +27,12 @@ pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { pt_t pgd = _pgd; // sv39 共有三级页表,一级一级查找 // -1 是因为最后一级是具体的某一页,在函数最后直接返回 - for (size_t level = VMM_PT_LEVEL - 1; level > 0; level--) { + for (size_t level = cpu::vmm_info::VMM_PT_LEVEL - 1; level > 0; level--) { // 每次循环会找到 _va 的第 level 级页表 pgd // 相当于 pgd_level[VPN_level],这样相当于得到了第 level 级页表的地址 pte_t* pte = (pte_t*)&pgd[PX(level, _va)]; // 解引用 pte,如果有效,获取 level+1 级页表, - if ((*pte & VMM_PAGE_VALID) == 1) { + if ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 1) { // pgd 指向下一级页表 // *pte 保存的是页表项,需要转换为对应的物理地址 pgd = (pt_t)PTE2PA(*pte); @@ -50,7 +44,7 @@ pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { if (_alloc == true) { // 申请新的物理页 pgd = (pt_t)PMM::get_instance().alloc_page_kernel(); - bzero(pgd, COMMON::PAGE_SIZE); + bzero(pgd, PhysicalMemoryManager::kPageSize); // 申请失败则返回 if (pgd == nullptr) { // 如果出现这种情况,说明物理内存不够,一般不会出现 @@ -58,9 +52,9 @@ pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { return nullptr; } // 清零 - bzero(pgd, COMMON::PAGE_SIZE); + bzero(pgd, PhysicalMemoryManager::kPageSize); // 填充页表项 - *pte = PA2PTE((uintptr_t)pgd) | VMM_PAGE_VALID; + *pte = PA2PTE((uintptr_t)pgd) | cpu::vmm_info::VMM_PAGE_VALID; } // 不分配的话直接返回 else { @@ -84,14 +78,15 @@ bool VirtualMemoryManager::init(void) { #endif // 分配一页用于保存页目录 pt_t pgd_kernel = (pt_t)PMM::get_instance().alloc_page_kernel(); - bzero(pgd_kernel, COMMON::PAGE_SIZE); + bzero(pgd_kernel, PhysicalMemoryManager::kPageSize); // 映射内核空间 for (uintptr_t addr = (uintptr_t)COMMON::KERNEL_START_ADDR; addr < (uintptr_t)COMMON::KERNEL_START_ADDR + VMM_KERNEL_SPACE_SIZE; - addr += COMMON::PAGE_SIZE) { + addr += PhysicalMemoryManager::kPageSize) { // TODO: 区分代码/数据等段分别映射 mmap(pgd_kernel, addr, addr, - VMM_PAGE_READABLE | VMM_PAGE_WRITABLE | VMM_PAGE_EXECUTABLE); + cpu::vmm_info::VMM_PAGE_READABLE | cpu::vmm_info::VMM_PAGE_WRITABLE | + cpu::vmm_info::VMM_PAGE_EXECUTABLE); } // 设置页目录 set_pgd(pgd_kernel); @@ -117,16 +112,18 @@ void VirtualMemoryManager::mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, // 一般情况下不应该为空 assert(pte != nullptr); // 已经映射过了 且 flag 没有变化 - if (((*pte & VMM_PAGE_VALID) == VMM_PAGE_VALID) && - ((*pte & ((1 << VMM_PTE_PROP_BITS) - 1)) == _flag)) { + if (((*pte & cpu::vmm_info::VMM_PAGE_VALID) == + cpu::vmm_info::VMM_PAGE_VALID) && + ((*pte & ((1 << cpu::vmm_info::VMM_PTE_PROP_BITS) - 1)) == _flag)) { warn("remap.\n"); } // 没有映射,或更改了 flag else { // 那么设置 *pte // pte 解引用后的值是页表项 - *pte = PA2PTE(_pa) | _flag | (*pte & ((1 << VMM_PTE_PROP_BITS) - 1)) | - VMM_PAGE_VALID; + *pte = PA2PTE(_pa) | _flag | + (*pte & ((1 << cpu::vmm_info::VMM_PTE_PROP_BITS) - 1)) | + cpu::vmm_info::VMM_PAGE_VALID; // 刷新缓存 CPU::VMM_FLUSH(0); } @@ -142,7 +139,7 @@ void VirtualMemoryManager::unmmap(const pt_t _pgd, uintptr_t _va) { return; } // 找到了,但是并没有被映射 - if ((*pte & VMM_PAGE_VALID) == 0) { + if ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 0) { warn("VirtualMemoryManager::unmmap: not mapped.\n"); } // 置零 @@ -158,7 +155,7 @@ bool VirtualMemoryManager::get_mmap(const pt_t _pgd, uintptr_t _va, pte_t* pte = find(_pgd, _va, false); bool res = false; // pte 不为空且有效,说明映射了 - if ((pte != nullptr) && ((*pte & VMM_PAGE_VALID) == 1)) { + if ((pte != nullptr) && ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 1)) { // 如果 _pa 不为空 if (_pa != nullptr) { // 设置 _pa From 58e18ad88d0db30e60de15f1fd394f8d1b81a713 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Fri, 30 Aug 2024 02:58:47 +0000 Subject: [PATCH 11/17] feat: working on vmm Signed-off-by: Zone.N --- src/kernel/CMakeLists.txt | 1 + src/kernel/arch/x86_64/include/cpu.hpp | 50 +++--- .../include/memory/virtual_memory_manager.h | 129 +++++++++------- src/kernel/virtual_memory_manager.cpp | 146 +++++++++--------- 4 files changed, 167 insertions(+), 159 deletions(-) diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index abacbbb4a..605008b96 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/driver) add_executable(${PROJECT_NAME} main.cpp physical_memory_manager.cpp + virtual_memory_manager.cpp ) target_include_directories(kernel PRIVATE diff --git a/src/kernel/arch/x86_64/include/cpu.hpp b/src/kernel/arch/x86_64/include/cpu.hpp index 4918679de..9a3c3301a 100644 --- a/src/kernel/arch/x86_64/include/cpu.hpp +++ b/src/kernel/arch/x86_64/include/cpu.hpp @@ -467,30 +467,6 @@ class Pit { volatile size_t ticks_ = 0; }; -namespace vmm_info { -/// P = 1 表示有效; P = 0 表示无效。 -static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; -/// 如果为 0 表示页面只读或可执行。 -static constexpr const uint8_t VMM_PAGE_READABLE = 0; -static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; -static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; -/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 -/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 -static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; -/// 内核虚拟地址相对物理地址的偏移 -static constexpr const size_t KERNEL_OFFSET = 0x0; -/// PTE 属性位数 -static constexpr const size_t VMM_PTE_PROP_BITS = 12; -/// PTE 页内偏移位数 -static constexpr const size_t VMM_PAGE_OFF_BITS = 12; -/// VPN 位数 -static constexpr const size_t VMM_VPN_BITS = 9; -/// VPN 位数掩码,9 位 VPN -static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; -/// x86_64 使用了四级页表 -static constexpr const size_t VMM_PT_LEVEL = 4; -}; // namespace vmm_info - // 第一部分:寄存器定义 namespace reginfo { @@ -2218,6 +2194,32 @@ struct InterruptContextErrorCode { [[maybe_unused]] static AllXreg kAllXreg; [[maybe_unused]] static AllCr kAllCr; +namespace vmm_info { +/// P = 1 表示有效; P = 0 表示无效。 +static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; +/// 如果为 0 表示页面只读或可执行。 +static constexpr const uint8_t VMM_PAGE_READABLE = 0; +static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; +static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; +/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 +/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 +static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; +/// 内核虚拟地址相对物理地址的偏移 +static constexpr const size_t KERNEL_OFFSET = 0x0; +/// PTE 属性位数 +static constexpr const size_t VMM_PTE_PROP_BITS = 12; +/// PTE 页内偏移位数 +static constexpr const size_t VMM_PAGE_OFF_BITS = 12; +/// VPN 位数 +static constexpr const size_t VMM_VPN_BITS = 9; +/// VPN 位数掩码,9 位 VPN +static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; +/// x86_64 使用了四级页表 +static constexpr const size_t VMM_PT_LEVEL = 4; +}; // namespace vmm_info + +namespace vmm {} // namespace vmm + }; // namespace cpu #endif // SIMPLEKERNEL_SRC_KERNEL_ARCH_X86_64_INCLUDE_CPU_HPP_ diff --git a/src/kernel/include/memory/virtual_memory_manager.h b/src/kernel/include/memory/virtual_memory_manager.h index 44874cc7c..ec33a6307 100644 --- a/src/kernel/include/memory/virtual_memory_manager.h +++ b/src/kernel/include/memory/virtual_memory_manager.h @@ -19,6 +19,7 @@ #include #include +#include #include "cpu.hpp" #include "physical_memory_manager.h" @@ -29,18 +30,15 @@ /// 页表项,最底层 typedef uintptr_t pte_t; /// 页表,也可以是页目录,它们的结构是一样的 -typedef uintptr_t* pt_t; +typedef uintptr_t *pt_t; /// 每个页表能映射多少页 = 页大小/页表项大小: 2^9 static constexpr const size_t VMM_PAGES_PRE_PAGE_TABLE = PhysicalMemoryManager::kPageSize / sizeof(pte_t); -/// 映射内核空间的大小 -static constexpr const size_t VMM_KERNEL_SPACE_SIZE = COMMON::KERNEL_SPACE_SIZE; - /// 内核映射的页数 static constexpr const size_t VMM_KERNEL_SPACE_PAGES = - VMM_KERNEL_SPACE_SIZE / PhysicalMemoryManager::kPageSize; + kKernelSpaceSize / PhysicalMemoryManager::kPageSize; /** * @brief 虚拟内存抽象 @@ -53,7 +51,73 @@ static constexpr const size_t VMM_KERNEL_SPACE_PAGES = * - 三级页表项(最低级) */ class VirtualMemoryManager { + public: + /** + * @brief 构造函数 + * @param addr 物理地址起点 + * @param pages_count 物理页数 + */ + explicit VirtualMemoryManager(); + + /// @name 构造/析构函数 + /// @{ + // VirtualMemoryManager() = default; + VirtualMemoryManager(const VirtualMemoryManager &) = default; + VirtualMemoryManager(VirtualMemoryManager &&) = default; + auto operator=(const VirtualMemoryManager &) -> VirtualMemoryManager & = + default; + auto operator=(VirtualMemoryManager &&) -> VirtualMemoryManager & = default; + ~VirtualMemoryManager() = default; + /// @} + + /** + * @brief 初始化 + * @return true 成功 + * @return false 失败 + */ + bool init(void); + + /** + * @brief 获取当前页目录 + * @return pt_t 当前页目录 + */ + pt_t get_pgd(void); + + /** + * @brief 设置当前页目录 + * @param _pgd 要设置的页目录 + */ + void set_pgd(const pt_t _pgd); + + /** + * @brief 映射物理地址到虚拟地址 + * @param _pgd 要使用的页目录 + * @param _va 要映射的虚拟地址 + * @param _pa 物理地址 + * @param _flag 属性 + */ + void mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag); + + /** + * @brief 取消映射 + * @param _pgd 要操作的页目录 + * @param _va 要取消映射的虚拟地址 + */ + void unmmap(const pt_t _pgd, uintptr_t _va); + + /** + * @brief 获取映射的物理地址 + * @param _pgd 页目录 + * @param _va 虚拟地址 + * @param _pa 如果已经映射,保存映射的物理地址,否则为 nullptr + * @return true 已映射 + * @return false 未映射 + */ + bool get_mmap(const pt_t _pgd, uintptr_t _va, const void *_pa); + private: + std::function AllocKernelPage; + /** * @brief 物理地址转换到页表项 * @param _pa 物理地址 @@ -103,60 +167,7 @@ class VirtualMemoryManager { * @param _alloc 是否分配 * @return pte_t* 未找到返回 nullptr */ - pte_t* find(const pt_t _pgd, uintptr_t _va, bool _alloc); - - protected: - public: - /** - * @brief 获取单例 - * @return VirtualMemoryManager& 静态对象 - */ - static VirtualMemoryManager& get_instance(void); - - /** - * @brief 初始化 - * @return true 成功 - * @return false 失败 - */ - bool init(void); - - /** - * @brief 获取当前页目录 - * @return pt_t 当前页目录 - */ - pt_t get_pgd(void); - - /** - * @brief 设置当前页目录 - * @param _pgd 要设置的页目录 - */ - void set_pgd(const pt_t _pgd); - - /** - * @brief 映射物理地址到虚拟地址 - * @param _pgd 要使用的页目录 - * @param _va 要映射的虚拟地址 - * @param _pa 物理地址 - * @param _flag 属性 - */ - void mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag); - - /** - * @brief 取消映射 - * @param _pgd 要操作的页目录 - * @param _va 要取消映射的虚拟地址 - */ - void unmmap(const pt_t _pgd, uintptr_t _va); - - /** - * @brief 获取映射的物理地址 - * @param _pgd 页目录 - * @param _va 虚拟地址 - * @param _pa 如果已经映射,保存映射的物理地址,否则为 nullptr - * @return true 已映射 - * @return false 未映射 - */ - bool get_mmap(const pt_t _pgd, uintptr_t _va, const void* _pa); + pte_t *find(const pt_t _pgd, uintptr_t _va, bool _alloc); }; /// 全局虚拟内存管理器 diff --git a/src/kernel/virtual_memory_manager.cpp b/src/kernel/virtual_memory_manager.cpp index 51251aeb9..3368fd428 100644 --- a/src/kernel/virtual_memory_manager.cpp +++ b/src/kernel/virtual_memory_manager.cpp @@ -16,72 +16,18 @@ #include "memory/virtual_memory_manager.h" -#include -#include - +#include "basic_info.hpp" #include "kernel_log.hpp" +#include "sk_cstdio" +#include "sk_cstring" -// 在 _pgd 中查找 _va 对应的页表项 -// 如果未找到,_alloc 为真时会进行分配 -pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { - pt_t pgd = _pgd; - // sv39 共有三级页表,一级一级查找 - // -1 是因为最后一级是具体的某一页,在函数最后直接返回 - for (size_t level = cpu::vmm_info::VMM_PT_LEVEL - 1; level > 0; level--) { - // 每次循环会找到 _va 的第 level 级页表 pgd - // 相当于 pgd_level[VPN_level],这样相当于得到了第 level 级页表的地址 - pte_t* pte = (pte_t*)&pgd[PX(level, _va)]; - // 解引用 pte,如果有效,获取 level+1 级页表, - if ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 1) { - // pgd 指向下一级页表 - // *pte 保存的是页表项,需要转换为对应的物理地址 - pgd = (pt_t)PTE2PA(*pte); - } - // 如果无效 - else { - // 判断是否需要分配 - // 如果需要 - if (_alloc == true) { - // 申请新的物理页 - pgd = (pt_t)PMM::get_instance().alloc_page_kernel(); - bzero(pgd, PhysicalMemoryManager::kPageSize); - // 申请失败则返回 - if (pgd == nullptr) { - // 如果出现这种情况,说明物理内存不够,一般不会出现 - assert(0); - return nullptr; - } - // 清零 - bzero(pgd, PhysicalMemoryManager::kPageSize); - // 填充页表项 - *pte = PA2PTE((uintptr_t)pgd) | cpu::vmm_info::VMM_PAGE_VALID; - } - // 不分配的话直接返回 - else { - return nullptr; - } - } - } - // 0 最低级 pt - return &pgd[PX(0, _va)]; -} - -VirtualMemoryManager& VirtualMemoryManager::get_instance(void) { - /// 定义全局 VirtualMemoryManager 对象 - static VirtualMemoryManager vmm; - return vmm; -} - -bool VirtualMemoryManager::init(void) { -#if defined(__i386__) || defined(__x86_64__) - GDT::init(); -#endif +VirtualMemoryManager::VirtualMemoryManager() { // 分配一页用于保存页目录 - pt_t pgd_kernel = (pt_t)PMM::get_instance().alloc_page_kernel(); + pt_t pgd_kernel = (pt_t)AllocKernelPage(); bzero(pgd_kernel, PhysicalMemoryManager::kPageSize); // 映射内核空间 - for (uintptr_t addr = (uintptr_t)COMMON::KERNEL_START_ADDR; - addr < (uintptr_t)COMMON::KERNEL_START_ADDR + VMM_KERNEL_SPACE_SIZE; + for (uintptr_t addr = kBasicInfo.GetInstance().kernel_addr; + addr < kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; addr += PhysicalMemoryManager::kPageSize) { // TODO: 区分代码/数据等段分别映射 mmap(pgd_kernel, addr, addr, @@ -91,18 +37,18 @@ bool VirtualMemoryManager::init(void) { // 设置页目录 set_pgd(pgd_kernel); // 开启分页 - CPU::ENABLE_PG(); - info("vmm init.\n"); - return 0; + cpu::vmm::EnablePage(); } -pt_t VirtualMemoryManager::get_pgd(void) { return (pt_t)CPU::GET_PGD(); } +pt_t VirtualMemoryManager::get_pgd(void) { + return (pt_t)cpu::vmm::GetPageDirectory(); +} void VirtualMemoryManager::set_pgd(const pt_t _pgd) { // 设置页目录 - CPU::SET_PGD((uintptr_t)_pgd); + cpu::vmm::SetPageDirectory((uintptr_t)_pgd); // 刷新缓存 - CPU::VMM_FLUSH(0); + cpu::vmm::FlushPage(0); return; } @@ -110,12 +56,16 @@ void VirtualMemoryManager::mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag) { pte_t* pte = find(_pgd, _va, true); // 一般情况下不应该为空 - assert(pte != nullptr); + if (pte == nullptr) { + log::Err("pte == nullptr\n"); + throw; + } + // 已经映射过了 且 flag 没有变化 if (((*pte & cpu::vmm_info::VMM_PAGE_VALID) == cpu::vmm_info::VMM_PAGE_VALID) && ((*pte & ((1 << cpu::vmm_info::VMM_PTE_PROP_BITS) - 1)) == _flag)) { - warn("remap.\n"); + log::Warn("remap.\n"); } // 没有映射,或更改了 flag else { @@ -125,7 +75,7 @@ void VirtualMemoryManager::mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, (*pte & ((1 << cpu::vmm_info::VMM_PTE_PROP_BITS) - 1)) | cpu::vmm_info::VMM_PAGE_VALID; // 刷新缓存 - CPU::VMM_FLUSH(0); + cpu::vmm::FlushPage(0); } return; } @@ -135,17 +85,17 @@ void VirtualMemoryManager::unmmap(const pt_t _pgd, uintptr_t _va) { // 找到页表项 // 未找到 if (pte == nullptr) { - warn("VirtualMemoryManager::unmmap: find.\n"); + log::Warn("VirtualMemoryManager::unmmap: find.\n"); return; } // 找到了,但是并没有被映射 if ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 0) { - warn("VirtualMemoryManager::unmmap: not mapped.\n"); + log::Warn("VirtualMemoryManager::unmmap: not mapped.\n"); } // 置零 *pte = 0x00; // 刷新缓存 - CPU::VMM_FLUSH(0); + cpu::vmm::FlushPage(0); // TODO: 如果一页表都被 unmap,释放占用的物理内存 return; } @@ -176,11 +126,55 @@ bool VirtualMemoryManager::get_mmap(const pt_t _pgd, uintptr_t _va, return res; } +// 在 _pgd 中查找 _va 对应的页表项 +// 如果未找到,_alloc 为真时会进行分配 +pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { + pt_t pgd = _pgd; + // sv39 共有三级页表,一级一级查找 + // -1 是因为最后一级是具体的某一页,在函数最后直接返回 + for (size_t level = cpu::vmm_info::VMM_PT_LEVEL - 1; level > 0; level--) { + // 每次循环会找到 _va 的第 level 级页表 pgd + // 相当于 pgd_level[VPN_level],这样相当于得到了第 level 级页表的地址 + pte_t* pte = (pte_t*)&pgd[PX(level, _va)]; + // 解引用 pte,如果有效,获取 level+1 级页表, + if ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 1) { + // pgd 指向下一级页表 + // *pte 保存的是页表项,需要转换为对应的物理地址 + pgd = (pt_t)PTE2PA(*pte); + } + // 如果无效 + else { + // 判断是否需要分配 + // 如果需要 + if (_alloc == true) { + // 申请新的物理页 + pgd = (pt_t)AllocKernelPage(); + bzero(pgd, PhysicalMemoryManager::kPageSize); + // 申请失败则返回 + if (pgd == nullptr) { + // 如果出现这种情况,说明物理内存不够,一般不会出现 + log::Err("No Enough Memory\n"); + throw; + return nullptr; + } + // 清零 + bzero(pgd, PhysicalMemoryManager::kPageSize); + // 填充页表项 + *pte = PA2PTE((uintptr_t)pgd) | cpu::vmm_info::VMM_PAGE_VALID; + } + // 不分配的话直接返回 + else { + return nullptr; + } + } + } + // 0 最低级 pt + return &pgd[PX(0, _va)]; +} + uint32_t VirtualMemoryInit(uint32_t, uint8_t*) { // 初始化虚拟内存管理器 - kVirtualMemoryManager.GetInstance() = - VirtualMemoryManager(kBasicInfo.GetInstance().physical_memory_addr, - kBasicInfo.GetInstance().physical_memory_size); + kVirtualMemoryManager.GetInstance() = VirtualMemoryManager(); log::Info("Hello VirtualMemoryInit\n"); From 8cbab957c976b9dbc4f90a3f298d9d1742fc46ac Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Fri, 30 Aug 2024 07:13:10 +0000 Subject: [PATCH 12/17] feat: working on vmm Signed-off-by: Zone.N --- src/kernel/include/memory/virtual_memory_manager.h | 4 +++- src/kernel/virtual_memory_manager.cpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/kernel/include/memory/virtual_memory_manager.h b/src/kernel/include/memory/virtual_memory_manager.h index 8d34bce73..7a65d7833 100644 --- a/src/kernel/include/memory/virtual_memory_manager.h +++ b/src/kernel/include/memory/virtual_memory_manager.h @@ -116,7 +116,9 @@ class VirtualMemoryManager { bool get_mmap(const pt_t _pgd, uintptr_t _va, const void *_pa); private: - std::function AllocKernelPage; + /// @todo + // std::function AllocKernelPage; + uint64_t AllocKernelPage() { return 0; } /** * @brief 物理地址转换到页表项 diff --git a/src/kernel/virtual_memory_manager.cpp b/src/kernel/virtual_memory_manager.cpp index 2eb91465b..6fc110814 100644 --- a/src/kernel/virtual_memory_manager.cpp +++ b/src/kernel/virtual_memory_manager.cpp @@ -19,6 +19,7 @@ #include "basic_info.hpp" #include "kernel_log.hpp" #include "sk_cstdio" +#include #include "sk_cstring" VirtualMemoryManager::VirtualMemoryManager() { From da67739a5d4d6a5aa79e63e6ec111591982f8994 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Fri, 30 Aug 2024 09:45:30 +0000 Subject: [PATCH 13/17] feat: working on vmm Signed-off-by: Zone.N --- src/boot/boot.cpp | 4 +- src/kernel/arch/x86_64/include/cpu/cpu.hpp | 47 +++++++++++++ src/kernel/arch/x86_64/include/cpu/regs.hpp | 47 +++++-------- src/kernel/include/kernel.h | 2 + .../include/memory/virtual_memory_manager.h | 20 +++--- src/kernel/main.cpp | 6 +- src/kernel/virtual_memory_manager.cpp | 69 +++++++++++-------- 7 files changed, 124 insertions(+), 71 deletions(-) diff --git a/src/boot/boot.cpp b/src/boot/boot.cpp index d742b15fa..8b8a95895 100644 --- a/src/boot/boot.cpp +++ b/src/boot/boot.cpp @@ -68,7 +68,6 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) { // 初始化 Memory auto memory = Memory(); - memory.PrintInfo(); // 加载内核 auto elf = Elf(KERNEL_NAME); auto [kernel_addr, elf_info] = elf.Load(); @@ -77,6 +76,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) { return EFI_LOAD_ERROR; } + // 输出内存映射 Memory + memory.PrintInfo(); + debug << L"Set Kernel Entry Point to: [" << OutStream::hex_X << kernel_addr << L"]." << OutStream::endl; debug << L"Elf addr: [" << OutStream::hex_X << elf_info.first << L"]." diff --git a/src/kernel/arch/x86_64/include/cpu/cpu.hpp b/src/kernel/arch/x86_64/include/cpu/cpu.hpp index c4d045ef6..d17790bdf 100644 --- a/src/kernel/arch/x86_64/include/cpu/cpu.hpp +++ b/src/kernel/arch/x86_64/include/cpu/cpu.hpp @@ -512,6 +512,53 @@ struct InterruptContextErrorCode { } }; +namespace vmm { +/// P = 1 表示有效; P = 0 表示无效。 +static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; +/// 如果为 0 表示页面只读或可执行。 +static constexpr const uint8_t VMM_PAGE_READABLE = 0; +static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; +static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; +/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 +/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 +static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; +/// 内核虚拟地址相对物理地址的偏移 +static constexpr const size_t KERNEL_OFFSET = 0x0; +/// PTE 属性位数 +static constexpr const size_t VMM_PTE_PROP_BITS = 12; +/// PTE 页内偏移位数 +static constexpr const size_t VMM_PAGE_OFF_BITS = 12; +/// VPN 位数 +static constexpr const size_t VMM_VPN_BITS = 9; +/// VPN 位数掩码,9 位 VPN +static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; +/// x86_64 使用了四级页表 +static constexpr const size_t VMM_PT_LEVEL = 4; + +// 开启 PG +inline void EnablePage() { kAllCr.cr0.pg.Set(); } +inline void DisablePage() { kAllCr.cr0.pg.Clear(); } + +/** + * @brief 设置 页目录 + * @param _pgd 要设置的页表 + * @return true 成功 + * @return false 失败 + */ +inline void SetPageDirectory(uint64_t pgd) { kAllCr.cr3.Write(pgd); } + +/** + * @brief 获取页目录 CR3 + * @return uint64_t CR3 值 + */ +inline uint64_t GetPageDirectory() { return kAllCr.cr3.Read(); } + +inline void FlushPage(uint64_t addr) { + __asm__ volatile("invlpg (%0)" : : "r"(addr) : "memory"); +} + +}; // namespace vmm + }; // namespace cpu #endif // SIMPLEKERNEL_SRC_KERNEL_ARCH_X86_64_INCLUDE_CPU_CPU_HPP_ diff --git a/src/kernel/arch/x86_64/include/cpu/regs.hpp b/src/kernel/arch/x86_64/include/cpu/regs.hpp index 7b25686fa..0a90ad830 100644 --- a/src/kernel/arch/x86_64/include/cpu/regs.hpp +++ b/src/kernel/arch/x86_64/include/cpu/regs.hpp @@ -999,7 +999,15 @@ class WriteOnlyRegBase { klog::Err("TODO\n"); } else if constexpr (std::is_same::value) { - __asm__ volatile("bts %%cr0, %0" : : "r"(offset) :); + // __asm__ volatile("bts %%cr0, %0" : : "r"(offset) :); + uint64_t value; + __asm__ volatile( + "mov %%cr0, %0\n\t" + "bts %1, %0\n\t" + "mov %0, %%cr0" + : "=r"(value) + : "r"(offset) + : "memory"); } else if constexpr (std::is_same::value) { __asm__ volatile("bts %%cr2, %0" : : "r"(offset) :); @@ -1059,7 +1067,16 @@ class WriteOnlyRegBase { klog::Err("TODO\n"); } else if constexpr (std::is_same::value) { - __asm__ volatile("btr %%cr0, %0" : : "r"(offset) :); + // __asm__ volatile("btr %%cr0, %0" : : "r"(offset) :); + // __asm__ volatile("bts %%cr0, %0" : : "r"(offset) :); + uint64_t value; + __asm__ volatile( + "mov %%cr0, %0\n\t" + "btr %1, %0\n\t" + "mov %0, %%cr0" + : "=r"(value) + : "r"(offset) + : "memory"); } else if constexpr (std::is_same::value) { __asm__ volatile("btr %%cr2, %0" : : "r"(offset) :); @@ -1771,32 +1788,6 @@ struct AllCr { [[maybe_unused]] static AllXreg kAllXreg; [[maybe_unused]] static AllCr kAllCr; -namespace vmm_info { -/// P = 1 表示有效; P = 0 表示无效。 -static constexpr const uint8_t VMM_PAGE_VALID = 1 << 0; -/// 如果为 0 表示页面只读或可执行。 -static constexpr const uint8_t VMM_PAGE_READABLE = 0; -static constexpr const uint8_t VMM_PAGE_WRITABLE = 1 << 1; -static constexpr const uint8_t VMM_PAGE_EXECUTABLE = 0; -/// U/S-- 位 2 是用户 / 超级用户 (User/Supervisor) 标志。 -/// 如果为 1 那么运行在任何特权级上的程序都可以访问该页面。 -static constexpr const uint8_t VMM_PAGE_USER = 1 << 2; -/// 内核虚拟地址相对物理地址的偏移 -static constexpr const size_t KERNEL_OFFSET = 0x0; -/// PTE 属性位数 -static constexpr const size_t VMM_PTE_PROP_BITS = 12; -/// PTE 页内偏移位数 -static constexpr const size_t VMM_PAGE_OFF_BITS = 12; -/// VPN 位数 -static constexpr const size_t VMM_VPN_BITS = 9; -/// VPN 位数掩码,9 位 VPN -static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; -/// x86_64 使用了四级页表 -static constexpr const size_t VMM_PT_LEVEL = 4; -}; // namespace vmm_info - -namespace vmm {} // namespace vmm - }; // namespace cpu #endif // SIMPLEKERNEL_SRC_KERNEL_ARCH_X86_64_INCLUDE_CPU_REGS_HPP_ diff --git a/src/kernel/include/kernel.h b/src/kernel/include/kernel.h index a8de6445f..5bb38b1c6 100644 --- a/src/kernel/include/kernel.h +++ b/src/kernel/include/kernel.h @@ -45,4 +45,6 @@ uint32_t InterruptInit(uint32_t argc, uint8_t* argv); uint32_t PhysicalMemoryInit(uint32_t argc, uint8_t* argv); +uint32_t VirtualMemoryInit(uint32_t argc, uint8_t* argv); + #endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_KERNEL_H_ */ diff --git a/src/kernel/include/memory/virtual_memory_manager.h b/src/kernel/include/memory/virtual_memory_manager.h index 7a65d7833..7de5158c3 100644 --- a/src/kernel/include/memory/virtual_memory_manager.h +++ b/src/kernel/include/memory/virtual_memory_manager.h @@ -57,11 +57,11 @@ class VirtualMemoryManager { * @param addr 物理地址起点 * @param pages_count 物理页数 */ - explicit VirtualMemoryManager(); + explicit VirtualMemoryManager(uint32_t, uint8_t *); /// @name 构造/析构函数 /// @{ - // VirtualMemoryManager() = default; + VirtualMemoryManager() = default; VirtualMemoryManager(const VirtualMemoryManager &) = default; VirtualMemoryManager(VirtualMemoryManager &&) = default; auto operator=(const VirtualMemoryManager &) -> VirtualMemoryManager & = @@ -118,7 +118,9 @@ class VirtualMemoryManager { private: /// @todo // std::function AllocKernelPage; - uint64_t AllocKernelPage() { return 0; } + uint64_t AllocKernelPage() { + return kPhysicalMemoryManager.GetInstance().AllocKernelPage(); + } /** * @brief 物理地址转换到页表项 @@ -128,8 +130,7 @@ class VirtualMemoryManager { * 12~31: 页表的物理页地址 */ static constexpr uintptr_t PA2PTE(uintptr_t _pa) { - return (_pa >> cpu::vmm_info::VMM_PAGE_OFF_BITS) - << cpu::vmm_info::VMM_PTE_PROP_BITS; + return (_pa >> cpu::vmm::VMM_PAGE_OFF_BITS) << cpu::vmm::VMM_PTE_PROP_BITS; } /** @@ -138,8 +139,8 @@ class VirtualMemoryManager { * @return constexpr uintptr_t 对应的物理地址 */ static constexpr uintptr_t PTE2PA(const pte_t _pte) { - return (((uintptr_t)_pte) >> cpu::vmm_info::VMM_PTE_PROP_BITS) - << cpu::vmm_info::VMM_PAGE_OFF_BITS; + return (((uintptr_t)_pte) >> cpu::vmm::VMM_PTE_PROP_BITS) + << cpu::vmm::VMM_PAGE_OFF_BITS; } /** @@ -148,8 +149,7 @@ class VirtualMemoryManager { * @return constexpr uintptr_t 偏移 */ static constexpr uintptr_t PXSHIFT(const size_t _level) { - return cpu::vmm_info::VMM_PAGE_OFF_BITS + - (cpu::vmm_info::VMM_VPN_BITS * _level); + return cpu::vmm::VMM_PAGE_OFF_BITS + (cpu::vmm::VMM_VPN_BITS * _level); } /** @@ -158,7 +158,7 @@ class VirtualMemoryManager { * 得到的就是第 _level 级页表的 VPN */ static constexpr uintptr_t PX(size_t _level, uintptr_t _va) { - return (_va >> PXSHIFT(_level)) & cpu::vmm_info::VMM_VPN_BITS_MASK; + return (_va >> PXSHIFT(_level)) & cpu::vmm::VMM_VPN_BITS_MASK; } /** diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index fb0484ea6..5a5158aff 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -16,10 +16,10 @@ #include #include "arch.h" -#include "sk_cstdio" -#include "sk_iostream" #include "kernel.h" #include "kernel_log.hpp" +#include "sk_cstdio" +#include "sk_iostream" #include "sk_libcxx.h" void _start(uint32_t argc, uint8_t *argv) { @@ -55,6 +55,8 @@ uint32_t main(uint32_t argc, uint8_t *argv) { PhysicalMemoryInit(argc, argv); + VirtualMemoryInit(argc, argv); + // 进入死循环 while (1) { for (uint64_t i = 0; i < 99999999; i++) { diff --git a/src/kernel/virtual_memory_manager.cpp b/src/kernel/virtual_memory_manager.cpp index 6fc110814..12d0fc4a7 100644 --- a/src/kernel/virtual_memory_manager.cpp +++ b/src/kernel/virtual_memory_manager.cpp @@ -16,29 +16,39 @@ #include "memory/virtual_memory_manager.h" +#include + #include "basic_info.hpp" #include "kernel_log.hpp" #include "sk_cstdio" -#include #include "sk_cstring" -VirtualMemoryManager::VirtualMemoryManager() { +VirtualMemoryManager::VirtualMemoryManager(uint32_t, uint8_t*) { + auto kernel_pgd = cpu::vmm::GetPageDirectory(); + uint64_t pa = 0; + + auto res = + get_mmap((pt_t)kernel_pgd, kBasicInfo.GetInstance().kernel_addr, &pa); + klog::Debug("res: %d, pa 0x%X\n", res, pa); + // 分配一页用于保存页目录 - pt_t pgd_kernel = (pt_t)AllocKernelPage(); - bzero(pgd_kernel, PhysicalMemoryManager::kPageSize); - // 映射内核空间 - for (uintptr_t addr = kBasicInfo.GetInstance().kernel_addr; - addr < kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; - addr += PhysicalMemoryManager::kPageSize) { - // TODO: 区分代码/数据等段分别映射 - mmap(pgd_kernel, addr, addr, - cpu::vmm_info::VMM_PAGE_READABLE | cpu::vmm_info::VMM_PAGE_WRITABLE | - cpu::vmm_info::VMM_PAGE_EXECUTABLE); - } + // pt_t pgd_kernel = (pt_t)AllocKernelPage(); + // memset(pgd_kernel, 0, PhysicalMemoryManager::kPageSize); + // // 映射内核空间 + // for (uint64_t addr = kBasicInfo.GetInstance().kernel_addr; + // addr < kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; + // addr += PhysicalMemoryManager::kPageSize) { + // // TODO: 区分代码/数据等段分别映射 + // mmap(pgd_kernel, addr, addr, + // cpu::vmm::VMM_PAGE_READABLE | cpu::vmm::VMM_PAGE_WRITABLE | + // cpu::vmm::VMM_PAGE_EXECUTABLE); + // } // 设置页目录 - set_pgd(pgd_kernel); + // cpu::vmm::DisablePage(); + // klog::Debug("set_pgd: 0x%X\n", pgd_kernel); + // set_pgd(pgd_kernel); // 开启分页 - cpu::vmm::EnablePage(); + // cpu::vmm::EnablePage(); } pt_t VirtualMemoryManager::get_pgd(void) { @@ -47,7 +57,7 @@ pt_t VirtualMemoryManager::get_pgd(void) { void VirtualMemoryManager::set_pgd(const pt_t _pgd) { // 设置页目录 - cpu::vmm::SetPageDirectory((uintptr_t)_pgd); + cpu::vmm::SetPageDirectory((uint64_t)_pgd); // 刷新缓存 cpu::vmm::FlushPage(0); return; @@ -63,9 +73,8 @@ void VirtualMemoryManager::mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, } // 已经映射过了 且 flag 没有变化 - if (((*pte & cpu::vmm_info::VMM_PAGE_VALID) == - cpu::vmm_info::VMM_PAGE_VALID) && - ((*pte & ((1 << cpu::vmm_info::VMM_PTE_PROP_BITS) - 1)) == _flag)) { + if (((*pte & cpu::vmm::VMM_PAGE_VALID) == cpu::vmm::VMM_PAGE_VALID) && + ((*pte & ((1 << cpu::vmm::VMM_PTE_PROP_BITS) - 1)) == _flag)) { klog::Warn("remap.\n"); } // 没有映射,或更改了 flag @@ -73,8 +82,8 @@ void VirtualMemoryManager::mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, // 那么设置 *pte // pte 解引用后的值是页表项 *pte = PA2PTE(_pa) | _flag | - (*pte & ((1 << cpu::vmm_info::VMM_PTE_PROP_BITS) - 1)) | - cpu::vmm_info::VMM_PAGE_VALID; + (*pte & ((1 << cpu::vmm::VMM_PTE_PROP_BITS) - 1)) | + cpu::vmm::VMM_PAGE_VALID; // 刷新缓存 cpu::vmm::FlushPage(0); } @@ -90,7 +99,7 @@ void VirtualMemoryManager::unmmap(const pt_t _pgd, uintptr_t _va) { return; } // 找到了,但是并没有被映射 - if ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 0) { + if ((*pte & cpu::vmm::VMM_PAGE_VALID) == 0) { klog::Warn("VirtualMemoryManager::unmmap: not mapped.\n"); } // 置零 @@ -106,7 +115,7 @@ bool VirtualMemoryManager::get_mmap(const pt_t _pgd, uintptr_t _va, pte_t* pte = find(_pgd, _va, false); bool res = false; // pte 不为空且有效,说明映射了 - if ((pte != nullptr) && ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 1)) { + if ((pte != nullptr) && ((*pte & cpu::vmm::VMM_PAGE_VALID) == 1)) { // 如果 _pa 不为空 if (_pa != nullptr) { // 设置 _pa @@ -133,12 +142,12 @@ pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { pt_t pgd = _pgd; // sv39 共有三级页表,一级一级查找 // -1 是因为最后一级是具体的某一页,在函数最后直接返回 - for (size_t level = cpu::vmm_info::VMM_PT_LEVEL - 1; level > 0; level--) { + for (size_t level = cpu::vmm::VMM_PT_LEVEL - 1; level > 0; level--) { // 每次循环会找到 _va 的第 level 级页表 pgd // 相当于 pgd_level[VPN_level],这样相当于得到了第 level 级页表的地址 pte_t* pte = (pte_t*)&pgd[PX(level, _va)]; // 解引用 pte,如果有效,获取 level+1 级页表, - if ((*pte & cpu::vmm_info::VMM_PAGE_VALID) == 1) { + if ((*pte & cpu::vmm::VMM_PAGE_VALID) == 1) { // pgd 指向下一级页表 // *pte 保存的是页表项,需要转换为对应的物理地址 pgd = (pt_t)PTE2PA(*pte); @@ -150,7 +159,7 @@ pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { if (_alloc == true) { // 申请新的物理页 pgd = (pt_t)AllocKernelPage(); - bzero(pgd, PhysicalMemoryManager::kPageSize); + memset(pgd, 0, PhysicalMemoryManager::kPageSize); // 申请失败则返回 if (pgd == nullptr) { // 如果出现这种情况,说明物理内存不够,一般不会出现 @@ -159,9 +168,9 @@ pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { return nullptr; } // 清零 - bzero(pgd, PhysicalMemoryManager::kPageSize); + memset(pgd, 0, PhysicalMemoryManager::kPageSize); // 填充页表项 - *pte = PA2PTE((uintptr_t)pgd) | cpu::vmm_info::VMM_PAGE_VALID; + *pte = PA2PTE((uintptr_t)pgd) | cpu::vmm::VMM_PAGE_VALID; } // 不分配的话直接返回 else { @@ -173,9 +182,9 @@ pte_t* VirtualMemoryManager::find(const pt_t _pgd, uintptr_t _va, bool _alloc) { return &pgd[PX(0, _va)]; } -uint32_t VirtualMemoryInit(uint32_t, uint8_t*) { +uint32_t VirtualMemoryInit(uint32_t argc, uint8_t* argv) { // 初始化虚拟内存管理器 - kVirtualMemoryManager.GetInstance() = VirtualMemoryManager(); + kVirtualMemoryManager.GetInstance() = VirtualMemoryManager(argc, argv); klog::Info("Hello VirtualMemoryInit\n"); From 939f3aa91a6d2ccaa3adcb24698db582f1a0322e Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Fri, 30 Aug 2024 10:05:35 +0000 Subject: [PATCH 14/17] feat: working on vmm Signed-off-by: Zone.N --- src/kernel/virtual_memory_manager.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/kernel/virtual_memory_manager.cpp b/src/kernel/virtual_memory_manager.cpp index 12d0fc4a7..dd15fab49 100644 --- a/src/kernel/virtual_memory_manager.cpp +++ b/src/kernel/virtual_memory_manager.cpp @@ -31,6 +31,15 @@ VirtualMemoryManager::VirtualMemoryManager(uint32_t, uint8_t*) { get_mmap((pt_t)kernel_pgd, kBasicInfo.GetInstance().kernel_addr, &pa); klog::Debug("res: %d, pa 0x%X\n", res, pa); + klog::Debug("cr0: 0x%X\n", cpu::kAllCr.cr0.Read()); + klog::Debug("cr3: 0x%X\n", cpu::kAllCr.cr3.Read()); + + klog::Debug("kernel_pgd: 0x%X\n", kernel_pgd); + klog::Debug("*kernel_pgd: 0x%X\n", *((uint64_t*)kernel_pgd)); + klog::Debug("**kernel_pgd: 0x%X\n", *((uint64_t*)*((uint64_t*)kernel_pgd))); + klog::Debug("**kernel_pgd: 0x%X\n", + *((uint64_t*)*((uint64_t*)*((uint64_t*)kernel_pgd)))); + // 分配一页用于保存页目录 // pt_t pgd_kernel = (pt_t)AllocKernelPage(); // memset(pgd_kernel, 0, PhysicalMemoryManager::kPageSize); From 75af41111b5bc32fbaa48f511411bd0f58d2a8c6 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Wed, 11 Sep 2024 03:25:16 +0000 Subject: [PATCH 15/17] feat: working on vmm Signed-off-by: Zone.N --- src/kernel/arch/riscv64/include/cpu/cpu.hpp | 80 +++++++++++++++++++++ src/kernel/virtual_memory_manager.cpp | 45 ++++-------- 2 files changed, 94 insertions(+), 31 deletions(-) diff --git a/src/kernel/arch/riscv64/include/cpu/cpu.hpp b/src/kernel/arch/riscv64/include/cpu/cpu.hpp index 7eebf372c..f6d776b3e 100644 --- a/src/kernel/arch/riscv64/include/cpu/cpu.hpp +++ b/src/kernel/arch/riscv64/include/cpu/cpu.hpp @@ -27,4 +27,84 @@ #include "sk_cstdio" #include "sk_iostream" +namespace cpu { +namespace vmm { +enum { + VALID_OFFSET = 0, + READ_OFFSET = 1, + WRITE_OFFSET = 2, + EXEC_OFFSET = 3, + USER_OFFSET = 4, + GLOBAL_OFFSET = 5, + ACCESSED_OFFSET = 6, + DIRTY_OFFSET = 7, + VALID = 1 << VALID_OFFSET, + READ = 1 << READ_OFFSET, + WRITE = 1 << WRITE_OFFSET, + EXEC = 1 << EXEC_OFFSET, + USER = 1 << USER_OFFSET, + GLOBAL = 1 << GLOBAL_OFFSET, + ACCESSED = 1 << ACCESSED_OFFSET, + DIRTY = 1 << DIRTY_OFFSET, +}; +/// 有效位 +static constexpr const uint8_t VMM_PAGE_VALID = VALID; +/// 可读位 +static constexpr const uint8_t VMM_PAGE_READABLE = READ; +/// 可写位s +static constexpr const uint8_t VMM_PAGE_WRITABLE = WRITE; +/// 可执行位 +static constexpr const uint8_t VMM_PAGE_EXECUTABLE = EXEC; +/// 用户位 +static constexpr const uint8_t VMM_PAGE_USER = USER; +/// 全局位,我们不会使用 +static constexpr const uint8_t VMM_PAGE_GLOBAL = GLOBAL; +/// 已使用位,用于替换算法 +static constexpr const uint8_t VMM_PAGE_ACCESSED = ACCESSED; +/// 已修改位,用于替换算法 +static constexpr const uint8_t VMM_PAGE_DIRTY = DIRTY; +/// 内核虚拟地址相对物理地址的偏移 +static constexpr const size_t KERNEL_OFFSET = 0x0; +/// PTE 属性位数 +static constexpr const size_t VMM_PTE_PROP_BITS = 10; +/// PTE 页内偏移位数 +static constexpr const size_t VMM_PAGE_OFF_BITS = 12; +/// VPN 位数 +static constexpr const size_t VMM_VPN_BITS = 9; +/// VPN 位数掩码,9 位 VPN +static constexpr const size_t VMM_VPN_BITS_MASK = 0x1FF; +/// riscv64 使用了三级页表 +static constexpr const size_t VMM_PT_LEVEL = 3; + +// 开启 PG +inline void EnablePage() { + kAllCsr.satp.asid.Write(0); + kAllCsr.satp.mode.Write(register_info::csr::SatpInfo::kSv39); +} +inline void DisablePage() { + kAllCsr.satp.mode.Write(register_info::csr::SatpInfo::kBare); +} + +/** + * @brief 设置 页目录 + * @param _pgd 要设置的页表 + * @return true 成功 + * @return false 失败 + */ +inline void SetPageDirectory(uint64_t pgd) { kAllCsr.satp.ppn.Write(pgd); } + +/** + * @brief 获取页目录 + * @return uint64_t 页目录值 + */ +inline uint64_t GetPageDirectory() { return kAllCsr.satp.ppn.Get(); } + +inline void FlushPage(uint64_t addr) { + (void)addr; + __asm__ volatile("sfence.vma zero, zero"); +} + +} // namespace vmm +} // namespace cpu + #endif // SIMPLEKERNEL_SRC_KERNEL_ARCH_RISCV64_INCLUDE_CPU_CPU_HPP_ diff --git a/src/kernel/virtual_memory_manager.cpp b/src/kernel/virtual_memory_manager.cpp index dd15fab49..73e8ad6f6 100644 --- a/src/kernel/virtual_memory_manager.cpp +++ b/src/kernel/virtual_memory_manager.cpp @@ -24,40 +24,23 @@ #include "sk_cstring" VirtualMemoryManager::VirtualMemoryManager(uint32_t, uint8_t*) { - auto kernel_pgd = cpu::vmm::GetPageDirectory(); - uint64_t pa = 0; - - auto res = - get_mmap((pt_t)kernel_pgd, kBasicInfo.GetInstance().kernel_addr, &pa); - klog::Debug("res: %d, pa 0x%X\n", res, pa); - - klog::Debug("cr0: 0x%X\n", cpu::kAllCr.cr0.Read()); - klog::Debug("cr3: 0x%X\n", cpu::kAllCr.cr3.Read()); - - klog::Debug("kernel_pgd: 0x%X\n", kernel_pgd); - klog::Debug("*kernel_pgd: 0x%X\n", *((uint64_t*)kernel_pgd)); - klog::Debug("**kernel_pgd: 0x%X\n", *((uint64_t*)*((uint64_t*)kernel_pgd))); - klog::Debug("**kernel_pgd: 0x%X\n", - *((uint64_t*)*((uint64_t*)*((uint64_t*)kernel_pgd)))); - // 分配一页用于保存页目录 - // pt_t pgd_kernel = (pt_t)AllocKernelPage(); - // memset(pgd_kernel, 0, PhysicalMemoryManager::kPageSize); - // // 映射内核空间 - // for (uint64_t addr = kBasicInfo.GetInstance().kernel_addr; - // addr < kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; - // addr += PhysicalMemoryManager::kPageSize) { - // // TODO: 区分代码/数据等段分别映射 - // mmap(pgd_kernel, addr, addr, - // cpu::vmm::VMM_PAGE_READABLE | cpu::vmm::VMM_PAGE_WRITABLE | - // cpu::vmm::VMM_PAGE_EXECUTABLE); - // } + pt_t pgd_kernel = (pt_t)AllocKernelPage(); + memset(pgd_kernel, 0, PhysicalMemoryManager::kPageSize); + // 映射内核空间 + for (uint64_t addr = kBasicInfo.GetInstance().kernel_addr; + addr < kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; + addr += PhysicalMemoryManager::kPageSize) { + // TODO: 区分代码/数据等段分别映射 + mmap(pgd_kernel, addr, addr, + cpu::vmm::VMM_PAGE_READABLE | cpu::vmm::VMM_PAGE_WRITABLE | + cpu::vmm::VMM_PAGE_EXECUTABLE); + } // 设置页目录 - // cpu::vmm::DisablePage(); - // klog::Debug("set_pgd: 0x%X\n", pgd_kernel); - // set_pgd(pgd_kernel); + klog::Debug("set_pgd: 0x%X\n", pgd_kernel); + set_pgd(pgd_kernel); // 开启分页 - // cpu::vmm::EnablePage(); + cpu::vmm::EnablePage(); } pt_t VirtualMemoryManager::get_pgd(void) { From 8af08a75f324eac01d9b7ef93c426232efd9e324 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Wed, 11 Sep 2024 05:12:29 +0000 Subject: [PATCH 16/17] fix: overflow Signed-off-by: Zone.N --- src/kernel/arch/riscv64/include/cpu/regs.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/kernel/arch/riscv64/include/cpu/regs.hpp b/src/kernel/arch/riscv64/include/cpu/regs.hpp index 9423ebf56..fe29ec56d 100644 --- a/src/kernel/arch/riscv64/include/cpu/regs.hpp +++ b/src/kernel/arch/riscv64/include/cpu/regs.hpp @@ -1234,10 +1234,10 @@ class ReadWriteField : public ReadOnlyField, * @param value 新值 */ static __always_inline void Write(RegInfo::DataType value) { - uint64_t org_value = Reg::Read(); - uint64_t new_value = - (org_value & ~RegInfo::kBitMask) | - (((uint64_t)value << RegInfo::kBitOffset) & RegInfo::kBitMask); + auto org_value = Reg::Read(); + auto new_value = (org_value & ~RegInfo::kBitMask) | + (((decltype(org_value))value << RegInfo::kBitOffset) & + RegInfo::kBitMask); Reg::Write(new_value); } @@ -1247,10 +1247,10 @@ class ReadWriteField : public ReadOnlyField, * @return RegInfo::DataType 由寄存器规定的数据类型 */ static __always_inline RegInfo::DataType ReadWrite(RegInfo::DataType value) { - uint64_t org_value = Reg::Read(); - uint64_t new_value = - (org_value & ~RegInfo::kBitMask) | - (((uint64_t)value << RegInfo::kBitOffset) & RegInfo::kBitMask); + auto org_value = Reg::Read(); + auto new_value = (org_value & ~RegInfo::kBitMask) | + (((decltype(org_value))value << RegInfo::kBitOffset) & + RegInfo::kBitMask); Reg::Write(new_value); return (RegInfo::DataType)((org_value & RegInfo::kBitMask) >> RegInfo::kBitOffset); From 77d443e691cc1a9de7e0cde3e06e4847da327150 Mon Sep 17 00:00:00 2001 From: "Zone.N" Date: Wed, 11 Sep 2024 05:18:49 +0000 Subject: [PATCH 17/17] feat: working on vmm Signed-off-by: Zone.N --- .../include/memory/virtual_memory_manager.h | 17 ++++--------- src/kernel/virtual_memory_manager.cpp | 25 ++++++++----------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/kernel/include/memory/virtual_memory_manager.h b/src/kernel/include/memory/virtual_memory_manager.h index 7de5158c3..f92c7d117 100644 --- a/src/kernel/include/memory/virtual_memory_manager.h +++ b/src/kernel/include/memory/virtual_memory_manager.h @@ -70,24 +70,17 @@ class VirtualMemoryManager { ~VirtualMemoryManager() = default; /// @} - /** - * @brief 初始化 - * @return true 成功 - * @return false 失败 - */ - bool init(void); - /** * @brief 获取当前页目录 * @return pt_t 当前页目录 */ - pt_t get_pgd(void); + pt_t GetPageDirectory(); /** * @brief 设置当前页目录 * @param _pgd 要设置的页目录 */ - void set_pgd(const pt_t _pgd); + void SetPageDirectory(const pt_t _pgd); /** * @brief 映射物理地址到虚拟地址 @@ -96,14 +89,14 @@ class VirtualMemoryManager { * @param _pa 物理地址 * @param _flag 属性 */ - void mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag); + void Mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag); /** * @brief 取消映射 * @param _pgd 要操作的页目录 * @param _va 要取消映射的虚拟地址 */ - void unmmap(const pt_t _pgd, uintptr_t _va); + void Unmmap(const pt_t _pgd, uintptr_t _va); /** * @brief 获取映射的物理地址 @@ -113,7 +106,7 @@ class VirtualMemoryManager { * @return true 已映射 * @return false 未映射 */ - bool get_mmap(const pt_t _pgd, uintptr_t _va, const void *_pa); + bool GetMmap(const pt_t _pgd, uintptr_t _va, const void *_pa); private: /// @todo diff --git a/src/kernel/virtual_memory_manager.cpp b/src/kernel/virtual_memory_manager.cpp index 73e8ad6f6..cc082ba27 100644 --- a/src/kernel/virtual_memory_manager.cpp +++ b/src/kernel/virtual_memory_manager.cpp @@ -32,30 +32,29 @@ VirtualMemoryManager::VirtualMemoryManager(uint32_t, uint8_t*) { addr < kBasicInfo.GetInstance().kernel_addr + kKernelSpaceSize; addr += PhysicalMemoryManager::kPageSize) { // TODO: 区分代码/数据等段分别映射 - mmap(pgd_kernel, addr, addr, + Mmap(pgd_kernel, addr, addr, cpu::vmm::VMM_PAGE_READABLE | cpu::vmm::VMM_PAGE_WRITABLE | cpu::vmm::VMM_PAGE_EXECUTABLE); } // 设置页目录 - klog::Debug("set_pgd: 0x%X\n", pgd_kernel); - set_pgd(pgd_kernel); + klog::Debug("SetPageDirectory: 0x%X\n", pgd_kernel); + SetPageDirectory(pgd_kernel); // 开启分页 cpu::vmm::EnablePage(); } -pt_t VirtualMemoryManager::get_pgd(void) { +pt_t VirtualMemoryManager::GetPageDirectory() { return (pt_t)cpu::vmm::GetPageDirectory(); } -void VirtualMemoryManager::set_pgd(const pt_t _pgd) { +void VirtualMemoryManager::SetPageDirectory(const pt_t _pgd) { // 设置页目录 cpu::vmm::SetPageDirectory((uint64_t)_pgd); // 刷新缓存 cpu::vmm::FlushPage(0); - return; } -void VirtualMemoryManager::mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, +void VirtualMemoryManager::Mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, uint32_t _flag) { pte_t* pte = find(_pgd, _va, true); // 一般情况下不应该为空 @@ -79,31 +78,29 @@ void VirtualMemoryManager::mmap(const pt_t _pgd, uintptr_t _va, uintptr_t _pa, // 刷新缓存 cpu::vmm::FlushPage(0); } - return; } -void VirtualMemoryManager::unmmap(const pt_t _pgd, uintptr_t _va) { +void VirtualMemoryManager::Unmmap(const pt_t _pgd, uintptr_t _va) { pte_t* pte = find(_pgd, _va, false); // 找到页表项 // 未找到 if (pte == nullptr) { - klog::Warn("VirtualMemoryManager::unmmap: find.\n"); + klog::Warn("VirtualMemoryManager::Unmmap: find.\n"); return; } // 找到了,但是并没有被映射 if ((*pte & cpu::vmm::VMM_PAGE_VALID) == 0) { - klog::Warn("VirtualMemoryManager::unmmap: not mapped.\n"); + klog::Warn("VirtualMemoryManager::Unmmap: not mapped.\n"); } // 置零 *pte = 0x00; // 刷新缓存 cpu::vmm::FlushPage(0); // TODO: 如果一页表都被 unmap,释放占用的物理内存 - return; } -bool VirtualMemoryManager::get_mmap(const pt_t _pgd, uintptr_t _va, - const void* _pa) { +bool VirtualMemoryManager::GetMmap(const pt_t _pgd, uintptr_t _va, + const void* _pa) { pte_t* pte = find(_pgd, _va, false); bool res = false; // pte 不为空且有效,说明映射了