diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 79e46f6ae..58f4ee909 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -28,7 +28,7 @@ If applicable, add screenshots to help explain your problem. - Compile tools: [e.g. i386-elf-gcc i386-elf-ld] - Emulator: [e.g. Bochs 2.6.9] - Branch: [e.g. TODO] - - Any modifications to tools/env.sh: + - Any modifications to tools/env.sh: **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/help.md b/.github/ISSUE_TEMPLATE/help.md index 2080ae454..f3f577f6c 100644 --- a/.github/ISSUE_TEMPLATE/help.md +++ b/.github/ISSUE_TEMPLATE/help.md @@ -12,7 +12,7 @@ assignees: '' - Compile tools: [e.g. i386-elf-gcc i386-elf-ld] - Emulator: [e.g. Bochs 2.6.9] - Branch: [e.g. TODO] - - Any modifications to tools/env.sh: + - Any modifications to tools/env.sh: **Describe your question** A clear and concise description of what the question is. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 14c5df50b..000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,207 +0,0 @@ -# SimpleKernel Copilot Instructions - -## 项目概述 - -SimpleKernel 是一个**面向 AI 辅助学习的现代化操作系统内核项目**。使用 **C++23** 编写,支持 **x86_64**、**RISC-V 64** 和 **AArch64** 三种架构。 - -### 核心设计理念:接口驱动(Interface-Driven) - -- **项目主体是接口定义**——头文件(`.h/.hpp`)只包含类声明、纯虚接口、类型定义和 Doxygen 文档 -- **实现由 AI 完成**——用户阅读接口契约,AI 根据头文件生成 `.cpp` 实现 -- **现有代码是参考实现**——用于验证 AI 生成代码的正确性,不是唯一正确答案 -- **测试套件验证契约**——GoogleTest 测试用例验证实现是否符合接口要求 - -### 你作为 AI 的角色 - -当用户要求你实现某个模块时: -1. **先阅读对应的接口头文件**——理解类声明、纯虚方法、Doxygen 契约(`@pre`/`@post`/`@note`) -2. **在独立的 `.cpp` 文件中生成实现**——不要修改头文件中的接口定义 -3. **确保实现符合契约**——满足前置条件检查、后置条件保证、不引入额外的公共接口 -4. **运行测试验证**——`make unit-test` 或 `make SimpleKernel && make run` - -### 关键接口文件(你最常需要阅读的文件) - -| 接口文件 | 职责 | 对应实现 | -|---------|------|---------| -| `src/arch/arch.h` | 架构无关的统一入口 | `src/arch/{arch}/` 各文件 | -| `src/include/interrupt_base.h` | 中断子系统抽象基类 | `src/arch/{arch}/interrupt.cpp` | -| `src/driver/include/console_driver.h` | 控制台驱动抽象 | `ns16550a.cpp` / `pl011.cpp` | -| `src/include/virtual_memory.hpp` | 虚拟内存管理 | `src/virtual_memory.cpp` | -| `src/include/kernel_fdt.hpp` | 设备树解析 | `src/kernel_fdt.cpp` | -| `src/include/kernel_elf.hpp` | ELF 解析 | `src/kernel_elf.cpp` | -| `src/task/include/scheduler_base.hpp` | 调度器抽象基类 | `src/task/*_scheduler.cpp` | -| `src/include/spinlock.hpp` | 自旋锁 | header-only(性能要求) | -| `src/include/mutex.hpp` | 互斥锁 | `src/task/mutex.cpp` | - -## 技术栈 - -| 组件 | 技术选型 | -|------|---------| -| 语言标准 | C23 / C++23 | -| 构建系统 | CMake 3.27+ (CMakePresets) | -| 编译器 | GCC 交叉编译工具链 | -| 模拟器 | QEMU | -| 代码风格 | Google Style (clang-format/clang-tidy) | -| 测试框架 | GoogleTest | -| 容器化 | Docker (`ptrnull233/simple_kernel:latest`) | - -## 项目结构 - -``` -SimpleKernel/ -├── src/ # 内核源码 -│ ├── include/ # 📐 公共接口头文件(项目核心,你应该先读这里) -│ ├── arch/ # 架构相关代码 (aarch64/riscv64/x86_64) -│ │ ├── arch.h # 📐 架构无关统一接口 -│ │ └── {arch}/ # 每架构: boot.S, link.ld, arch_main.cpp 等 -│ ├── driver/ # 设备驱动 -│ │ ├── include/ # 📐 驱动接口 (console_driver.h 等) -│ │ └── ... # 驱动实现 (ns16550a/pl011/gic/plic/apic 等) -│ ├── task/ # 任务管理 -│ │ ├── include/ # 📐 调度器接口 (scheduler_base.hpp 等) -│ │ └── ... # 调度器和任务管理实现 -│ ├── libc/ # 内核 C 标准库实现 -│ └── libcxx/ # 内核 C++ 运行时 -├── tests/ # 🧪 测试套件(验证实现是否符合接口契约) -│ ├── unit_test/ # 单元测试 -│ ├── integration_test/ # 集成测试 -│ └── system_test/ # 系统测试(QEMU 运行) -├── cmake/ # CMake 模块和工具链文件 -│ ├── {arch}-gcc.cmake # 交叉编译工具链定义 -│ ├── compile_config.cmake # 编译选项配置 -│ ├── functions.cmake # 项目使用的辅助函数 -│ ├── project_config.cmake # 项目配置生成 -│ └── 3rd.cmake # 第三方依赖管理 -├── 3rd/ # 第三方依赖 (Git Submodule) -├── tools/ # 构建工具和模板 -└── doc/ # 文档 -``` - -## 构建命令 - -```bash -# 0. 确保子模块已初始化 (首次克隆后必须执行) -git submodule update --init --recursive - -# 1. 配置 (选择架构: build_riscv64 / build_aarch64 / build_x86_64) -cmake --preset build_{arch} - -# 2. 编译内核 (目标名称是 SimpleKernel,不是 kernel) -cd build_{arch} && make SimpleKernel - -# 3. 在 QEMU 中运行 -make run - -# 4. 调试模式 (GDB 连接 localhost:1234) -make debug - -# 5. 运行单元测试 ({arch} 需要与 HOST 架构一致) -cmake --preset build_{arch} -cd build_{arch} && make unit-test coverage -``` - -**VS Code 任务**: 使用 `Tasks: Run Task` 选择 `build_{arch}` 或 `run_{arch}`。 - -**⚠️ aarch64 特殊要求**: 运行前需要先启动两个串口终端任务 (`::54320` 和 `::54321`)。 - -## 编码规范 - -### 接口文件规范(最重要) - -当创建或修改接口头文件时,必须遵循以下规范: - -- **只包含声明**:类声明、纯虚接口、类型定义、常量,不包含方法实现 -- **Doxygen 契约文档**:每个类和方法必须包含 `@brief`、`@pre`(前置条件)、`@post`(后置条件) -- **最小化 include**:接口头文件只包含声明所需的头文件,实现所需的头文件放在 `.cpp` 中 -- **性能例外**:标记 `__always_inline` 的方法(如 `SpinLock::Lock()`)允许保留在头文件中 - -### 代码风格 -- **格式化**: Google Style,使用 `.clang-format` 自动格式化 -- **静态检查**: 使用 `.clang-tidy` 配置 -- **Pre-commit**: 自动执行 clang-format、cmake-format、shellcheck - -### Git Commit 规范 -``` -(): - -type: feat|fix|bug|docs|style|refactor|perf|test|build|revert|merge|sync|comment -scope: 可选,影响的模块 (如 arch, driver, libc) -subject: 不超过50字符,不加句号 -``` - -### 命名约定 -- **文件**: 小写下划线 (`kernel_log.hpp`) -- **类/结构体**: PascalCase (`TaskManager`) -- **函数**: PascalCase (`ArchInit`) 或 snake_case (`sys_yield`) -- **变量**: snake_case (`per_cpu_data`) -- **宏**: SCREAMING_SNAKE_CASE (`SIMPLEKERNEL_DEBUG`) -- **常量**: kCamelCase (`kPageSize`) -- **内核专用 libc/libc++ 头文件**: 使用 `sk_` 前缀 (`sk_cstdio`, `sk_vector`) - -## 架构开发指南 - -### 添加新架构功能 -1. 在 `src/arch/{arch}/` 目录下实现架构特定代码 -2. 更新 `src/arch/arch.h` 中的统一接口 -3. 必需文件: `boot.S`, `link.ld`, `arch_main.cpp` - -### 添加新驱动 -1. 在 `src/driver/` 下创建驱动目录 -2. 实现驱动并更新 `src/driver/CMakeLists.txt` -3. 在架构初始化代码中调用驱动初始化 - -### 添加测试 -1. 每当你添加一个新模块时,在 `tests/` 下创建测试文件 -2. 使用 GoogleTest 编写测试用例 -3. 更新相应的 `CMakeLists.txt` 以包含新测试 -4. 如果是 libc/libcxx,需要创建 unit-test 与 system-test 两类测试 -5. 如果是架构相关代码或内核代码,需要创建 system-test 测试 - -### 引导链 -| 架构 | 引导流程 | -|------|---------| -| x86_64 | U-Boot → kernel.elf | -| riscv64 | U-Boot SPL → OpenSBI → U-Boot → kernel.elf | -| aarch64 | U-Boot → ATF → OP-TEE → kernel.elf | - -## 关键 API - -### 日志系统 (`kernel_log.hpp`) -```cpp -klog::Debug("message %d", value); -klog::Info("info message\n"); -klog::Warn("warning\n"); -klog::Error("error\n"); -``` - -### 中断注册 -```cpp -// 架构相关,参见各架构的 interrupt.h -RegisterInterruptHandler(irq_num, handler_func); -``` - -### 任务管理 -```cpp -auto task = new TaskControlBlock("name", priority, func, arg); -Singleton::GetInstance().AddTask(task); -``` - -## 注意事项 - -### 构建问题排查 -- **子模块未初始化**: 运行 `git submodule update --init --recursive` -- **工具链缺失**: 使用 Docker 环境或参考 `doc/0_工具链.md` -- **aarch64 运行需要**: 先启动 VS Code 任务 `::54320` 和 `::54321` (串口终端) - -### 常见陷阱 -- 内核代码中禁止使用标准库的动态内存分配,使用 `libc/` 和 `libcxx/` 中的实现 -- 不同架构的 `_start` 参数含义不同 (见 `kernel.h` 注释) -- 编译选项使用 `-ffreestanding`,在 https://en.cppreference.com/w/cpp/freestanding.html 查阅可用库函数 - -## 资源链接 - -- **文档目录**: `doc/` (工具链、系统启动、调试输出、中断) -- **Docker 使用**: `doc/docker.md` -- **Git 规范**: `doc/git_commit.md` -- **接口重构计划**: `doc/TODO_interface_refactor.md` -- **调试信息**: `build_{arch}/bin` 目录会自动生成 objdump、 nm、map、dts 等文件,qemu 的运行日志也在这里 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 120000 index 000000000..be77ac83a --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index b0c1851a7..4eb521561 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,6 @@ [submodule "3rd/MPMCQueue"] path = 3rd/MPMCQueue url = https://github.com/MRNIU/MPMCQueue.git +[submodule "3rd/device_framework"] + path = 3rd/device_framework + url = https://github.com/MRNIU/device_framework.git diff --git a/3rd/MPMCQueue/CMakeLists.txt b/3rd/MPMCQueue/CMakeLists.txt index 753949416..658b54c4f 100644 --- a/3rd/MPMCQueue/CMakeLists.txt +++ b/3rd/MPMCQueue/CMakeLists.txt @@ -1,20 +1,21 @@ # Copyright The MPMCQueue Contributors -cmake_minimum_required(VERSION 3.20) +CMAKE_MINIMUM_REQUIRED (VERSION 3.20) -project(MPMCQueue - DESCRIPTION "Multi-Producer Multi-Consumer Lock-Free Queue for Freestanding C++26" - LANGUAGES CXX -) +PROJECT ( + MPMCQueue + DESCRIPTION + "Multi-Producer Multi-Consumer Lock-Free Queue for Freestanding C++26" + LANGUAGES CXX) -if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR}) - message( - FATAL_ERROR +IF(${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR}) + MESSAGE ( + FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there." ) -endif () +ENDIF() ADD_LIBRARY (${PROJECT_NAME} INTERFACE) TARGET_INCLUDE_DIRECTORIES (${PROJECT_NAME} INTERFACE include) -add_subdirectory(${PROJECT_SOURCE_DIR}/test) +ADD_SUBDIRECTORY (${PROJECT_SOURCE_DIR}/test) diff --git a/3rd/MPMCQueue/CMakePresets.json b/3rd/MPMCQueue/CMakePresets.json index 8e159bef8..61008a470 100644 --- a/3rd/MPMCQueue/CMakePresets.json +++ b/3rd/MPMCQueue/CMakePresets.json @@ -65,4 +65,4 @@ "description": "build" } ] -} \ No newline at end of file +} diff --git a/3rd/MPMCQueue/README.md b/3rd/MPMCQueue/README.md index 74d90f044..33fbd0ae6 100644 --- a/3rd/MPMCQueue/README.md +++ b/3rd/MPMCQueue/README.md @@ -29,7 +29,7 @@ A C++26 Multi-Producer Multi-Consumer (MPMC) lock-free queue implementation for ## 要求 (Requirements) - C++26 兼容的编译器 - - GCC 14+ + - GCC 14+ - Clang 18+ - MSVC 2024+ - CMake 3.20+(用于构建示例和测试) @@ -37,7 +37,7 @@ A C++26 Multi-Producer Multi-Consumer (MPMC) lock-free queue implementation for --- - C++26 compatible compiler - - GCC 14+ + - GCC 14+ - Clang 18+ - MSVC 2024+ - CMake 3.20+ (for building examples and tests) @@ -53,12 +53,12 @@ int main() { // 创建一个容量为 256 的队列(必须是 2 的幂) // Create a queue with capacity of 256 (must be power of 2) mpmc_queue::MPMCQueue queue; - + // 入队 // Enqueue queue.push(42); queue.push(100); - + // 出队 // Dequeue int value; @@ -66,7 +66,7 @@ int main() { // 成功获取值 // Successfully got value } - + return 0; } ``` @@ -100,10 +100,10 @@ void consumer() { int main() { std::thread t1(producer); std::thread t2(consumer); - + t1.join(); t2.join(); - + return 0; } ``` diff --git a/3rd/MPMCQueue/test/CMakeLists.txt b/3rd/MPMCQueue/test/CMakeLists.txt index 37cd4273c..881e9f6db 100644 --- a/3rd/MPMCQueue/test/CMakeLists.txt +++ b/3rd/MPMCQueue/test/CMakeLists.txt @@ -1,43 +1,28 @@ # Copyright The MPMCQueue Contributors -project( - MPMCQueue_test -) +PROJECT (MPMCQueue_test) -add_executable(${PROJECT_NAME} - test.cpp -) +ADD_EXECUTABLE (${PROJECT_NAME} test.cpp) -target_compile_options(${PROJECT_NAME} PRIVATE - -g - -ggdb - -Wall - -Wextra - -pedantic - --coverage - -fprofile-update=atomic - -O0 -) +TARGET_COMPILE_OPTIONS ( + ${PROJECT_NAME} + PRIVATE -g + -ggdb + -Wall + -Wextra + -pedantic + --coverage + -fprofile-update=atomic + -O0) -target_link_options(${PROJECT_NAME} PRIVATE - --coverage - -fsanitize=leak - -fsanitize=address - -fno-omit-frame-pointer -) +TARGET_LINK_OPTIONS (${PROJECT_NAME} PRIVATE --coverage -fsanitize=leak + -fsanitize=address -fno-omit-frame-pointer) # 添加要链接的库 -target_link_libraries(${PROJECT_NAME} PRIVATE - MPMCQueue - gtest - gmock - gtest_main -) +TARGET_LINK_LIBRARIES (${PROJECT_NAME} PRIVATE MPMCQueue gtest gmock gtest_main) -add_dependencies(${PROJECT_NAME} - MPMCQueue -) +ADD_DEPENDENCIES (${PROJECT_NAME} MPMCQueue) # 添加测试到CTest -include(GoogleTest) -gtest_discover_tests(${PROJECT_NAME}) +INCLUDE (GoogleTest) +GTEST_DISCOVER_TESTS (${PROJECT_NAME}) diff --git a/3rd/MPMCQueue/test/test.cpp b/3rd/MPMCQueue/test/test.cpp index d781f621e..de4e1eebe 100644 --- a/3rd/MPMCQueue/test/test.cpp +++ b/3rd/MPMCQueue/test/test.cpp @@ -1,265 +1,265 @@ #include + #include +#include #include #include -#include using namespace mpmc_queue; TEST(MPMCQueueTest, BasicPushPop) { - MPMCQueue queue; - int val = 0; - - EXPECT_TRUE(queue.push(1)); - EXPECT_TRUE(queue.push(2)); - EXPECT_TRUE(queue.push(3)); - EXPECT_TRUE(queue.push(4)); - EXPECT_FALSE(queue.push(5)); // Full - - EXPECT_TRUE(queue.pop(val)); - EXPECT_EQ(val, 1); - EXPECT_TRUE(queue.pop(val)); - EXPECT_EQ(val, 2); - EXPECT_TRUE(queue.pop(val)); - EXPECT_EQ(val, 3); - EXPECT_TRUE(queue.pop(val)); - EXPECT_EQ(val, 4); - EXPECT_FALSE(queue.pop(val)); // Empty + MPMCQueue queue; + int val = 0; + + EXPECT_TRUE(queue.push(1)); + EXPECT_TRUE(queue.push(2)); + EXPECT_TRUE(queue.push(3)); + EXPECT_TRUE(queue.push(4)); + EXPECT_FALSE(queue.push(5)); // Full + + EXPECT_TRUE(queue.pop(val)); + EXPECT_EQ(val, 1); + EXPECT_TRUE(queue.pop(val)); + EXPECT_EQ(val, 2); + EXPECT_TRUE(queue.pop(val)); + EXPECT_EQ(val, 3); + EXPECT_TRUE(queue.pop(val)); + EXPECT_EQ(val, 4); + EXPECT_FALSE(queue.pop(val)); // Empty } TEST(MPMCQueueTest, MultiThreadedPushPop) { - MPMCQueue queue; - std::atomic sum{0}; - const int num_ops = 1000; - const int num_threads = 4; - - auto producer = [&]() { - for (int i = 0; i < num_ops; ++i) { - while (!queue.push(1)) { - std::this_thread::yield(); - } - } - }; - - auto consumer = [&]() { - int val; - for (int i = 0; i < num_ops; ++i) { - while (!queue.pop(val)) { - std::this_thread::yield(); - } - sum += val; - } - }; - - std::vector producers; - std::vector consumers; - - for (int i = 0; i < num_threads; ++i) { - producers.emplace_back(producer); - consumers.emplace_back(consumer); + MPMCQueue queue; + std::atomic sum{0}; + const int num_ops = 1000; + const int num_threads = 4; + + auto producer = [&]() { + for (int i = 0; i < num_ops; ++i) { + while (!queue.push(1)) { + std::this_thread::yield(); + } } + }; + + auto consumer = [&]() { + int val; + for (int i = 0; i < num_ops; ++i) { + while (!queue.pop(val)) { + std::this_thread::yield(); + } + sum += val; + } + }; + + std::vector producers; + std::vector consumers; - for (auto& t : producers) t.join(); - for (auto& t : consumers) t.join(); + for (int i = 0; i < num_threads; ++i) { + producers.emplace_back(producer); + consumers.emplace_back(consumer); + } - EXPECT_EQ(sum, num_ops * num_threads); + for (auto& t : producers) t.join(); + for (auto& t : consumers) t.join(); + + EXPECT_EQ(sum, num_ops * num_threads); } TEST(MPMCQueueTest, HeavyLoadStressTest) { - MPMCQueue queue; - std::atomic sum{0}; - const int num_ops_per_thread = 100000; - const int num_threads = 16; - - auto producer = [&](int /*id*/) { - for (int i = 0; i < num_ops_per_thread; ++i) { - while (!queue.push(1)) { - std::this_thread::yield(); - } - } - }; - - auto consumer = [&]() { - int val; - for (int i = 0; i < num_ops_per_thread; ++i) { - while (!queue.pop(val)) { - std::this_thread::yield(); - } - sum += val; - } - }; - - std::vector producers; - std::vector consumers; - - for (int i = 0; i < num_threads; ++i) { - producers.emplace_back(producer, i); - consumers.emplace_back(consumer); + MPMCQueue queue; + std::atomic sum{0}; + const int num_ops_per_thread = 100000; + const int num_threads = 16; + + auto producer = [&](int /*id*/) { + for (int i = 0; i < num_ops_per_thread; ++i) { + while (!queue.push(1)) { + std::this_thread::yield(); + } + } + }; + + auto consumer = [&]() { + int val; + for (int i = 0; i < num_ops_per_thread; ++i) { + while (!queue.pop(val)) { + std::this_thread::yield(); + } + sum += val; } + }; + + std::vector producers; + std::vector consumers; + + for (int i = 0; i < num_threads; ++i) { + producers.emplace_back(producer, i); + consumers.emplace_back(consumer); + } - for (auto& t : producers) t.join(); - for (auto& t : consumers) t.join(); + for (auto& t : producers) t.join(); + for (auto& t : consumers) t.join(); - EXPECT_EQ(sum, static_cast(num_ops_per_thread) * num_threads); + EXPECT_EQ(sum, static_cast(num_ops_per_thread) * num_threads); } TEST(MPMCQueueTest, ManyProducersFewConsumers) { - MPMCQueue queue; - std::atomic consumer_sum{0}; - const int num_producers = 12; - const int num_consumers = 4; - const int ops_per_producer = 10000; - - // Total items produced = num_producers * ops_per_producer - // Each consumer must consume = (Total items) / num_consumers - const int total_items = num_producers * ops_per_producer; - const int ops_per_consumer = total_items / num_consumers; - - auto producer = [&]() { - for (int i = 0; i < ops_per_producer; ++i) { - while (!queue.push(1)) { - std::this_thread::yield(); - } - } - }; - - auto consumer = [&]() { - int val; - for (int i = 0; i < ops_per_consumer; ++i) { - while (!queue.pop(val)) { - std::this_thread::yield(); - } - consumer_sum += val; - } - }; - - std::vector producers; - std::vector consumers; - - for (int i = 0; i < num_producers; ++i) producers.emplace_back(producer); - for (int i = 0; i < num_consumers; ++i) consumers.emplace_back(consumer); - - for (auto& t : producers) t.join(); - for (auto& t : consumers) t.join(); - - EXPECT_EQ(consumer_sum, total_items); + MPMCQueue queue; + std::atomic consumer_sum{0}; + const int num_producers = 12; + const int num_consumers = 4; + const int ops_per_producer = 10000; + + // Total items produced = num_producers * ops_per_producer + // Each consumer must consume = (Total items) / num_consumers + const int total_items = num_producers * ops_per_producer; + const int ops_per_consumer = total_items / num_consumers; + + auto producer = [&]() { + for (int i = 0; i < ops_per_producer; ++i) { + while (!queue.push(1)) { + std::this_thread::yield(); + } + } + }; + + auto consumer = [&]() { + int val; + for (int i = 0; i < ops_per_consumer; ++i) { + while (!queue.pop(val)) { + std::this_thread::yield(); + } + consumer_sum += val; + } + }; + + std::vector producers; + std::vector consumers; + + for (int i = 0; i < num_producers; ++i) producers.emplace_back(producer); + for (int i = 0; i < num_consumers; ++i) consumers.emplace_back(consumer); + + for (auto& t : producers) t.join(); + for (auto& t : consumers) t.join(); + + EXPECT_EQ(consumer_sum, total_items); } TEST(MPMCQueueTest, QueueFullEmptyStress) { - MPMCQueue queue; - const int iterations = 100000; - - auto producer = [&]() { - for(int i=0; i producers; - for(int i=0; i<4; ++i) producers.emplace_back(producer); - - std::thread c(consumer); - - for(auto& p : producers) p.join(); - c.join(); + MPMCQueue queue; + const int iterations = 100000; + + auto producer = [&]() { + for (int i = 0; i < iterations; ++i) { + while (!queue.push(i)) { + std::this_thread::yield(); + } + } + }; + + auto consumer = [&]() { + int val; + int count = 0; + // 4 producers * iterations + while (count < iterations * 4) { + if (queue.pop(val)) { + count++; + } else { + std::this_thread::yield(); + } + } + }; + + std::vector producers; + for (int i = 0; i < 4; ++i) producers.emplace_back(producer); + + std::thread c(consumer); + + for (auto& p : producers) p.join(); + c.join(); } TEST(MPMCQueueTest, FewProducersManyConsumers) { - MPMCQueue queue; - std::atomic consumer_sum{0}; - const int num_producers = 4; - const int num_consumers = 12; - const int ops_per_consumer = 10000; - - // Total items needed = num_consumers * ops_per_consumer - // Each producer must produce = (Total items) / num_producers - const int total_items = num_consumers * ops_per_consumer; - const int ops_per_producer = total_items / num_producers; - - auto producer = [&]() { - for (int i = 0; i < ops_per_producer; ++i) { - while (!queue.push(1)) { - std::this_thread::yield(); - } - } - }; - - auto consumer = [&]() { - int val; - for (int i = 0; i < ops_per_consumer; ++i) { - while (!queue.pop(val)) { - std::this_thread::yield(); - } - consumer_sum += val; - } - }; - - std::vector producers; - std::vector consumers; - - for (int i = 0; i < num_producers; ++i) producers.emplace_back(producer); - for (int i = 0; i < num_consumers; ++i) consumers.emplace_back(consumer); - - for (auto& t : producers) t.join(); - for (auto& t : consumers) t.join(); - - EXPECT_EQ(consumer_sum, total_items); + MPMCQueue queue; + std::atomic consumer_sum{0}; + const int num_producers = 4; + const int num_consumers = 12; + const int ops_per_consumer = 10000; + + // Total items needed = num_consumers * ops_per_consumer + // Each producer must produce = (Total items) / num_producers + const int total_items = num_consumers * ops_per_consumer; + const int ops_per_producer = total_items / num_producers; + + auto producer = [&]() { + for (int i = 0; i < ops_per_producer; ++i) { + while (!queue.push(1)) { + std::this_thread::yield(); + } + } + }; + + auto consumer = [&]() { + int val; + for (int i = 0; i < ops_per_consumer; ++i) { + while (!queue.pop(val)) { + std::this_thread::yield(); + } + consumer_sum += val; + } + }; + + std::vector producers; + std::vector consumers; + + for (int i = 0; i < num_producers; ++i) producers.emplace_back(producer); + for (int i = 0; i < num_consumers; ++i) consumers.emplace_back(consumer); + + for (auto& t : producers) t.join(); + for (auto& t : consumers) t.join(); + + EXPECT_EQ(consumer_sum, total_items); } TEST(MPMCQueueTest, EightProducersEightConsumers) { - MPMCQueue queue; - std::atomic consumer_sum{0}; - const int num_producers = 8; - const int num_consumers = 8; - const int ops_per_producer = 50000; - - // Total items produced = num_producers * ops_per_producer - // Each consumer must consume = (Total items) / num_consumers - const int total_items = num_producers * ops_per_producer; - const int ops_per_consumer = total_items / num_consumers; - - auto producer = [&]() { - for (int i = 0; i < ops_per_producer; ++i) { - while (!queue.push(1)) { - std::this_thread::yield(); - } - } - }; - - auto consumer = [&]() { - int val; - for (int i = 0; i < ops_per_consumer; ++i) { - while (!queue.pop(val)) { - std::this_thread::yield(); - } - consumer_sum += val; - } - }; - - std::vector producers; - std::vector consumers; - - for (int i = 0; i < num_producers; ++i) producers.emplace_back(producer); - for (int i = 0; i < num_consumers; ++i) consumers.emplace_back(consumer); - - for (auto& t : producers) t.join(); - for (auto& t : consumers) t.join(); - - EXPECT_EQ(consumer_sum, total_items); -} + MPMCQueue queue; + std::atomic consumer_sum{0}; + const int num_producers = 8; + const int num_consumers = 8; + const int ops_per_producer = 50000; + + // Total items produced = num_producers * ops_per_producer + // Each consumer must consume = (Total items) / num_consumers + const int total_items = num_producers * ops_per_producer; + const int ops_per_consumer = total_items / num_consumers; + + auto producer = [&]() { + for (int i = 0; i < ops_per_producer; ++i) { + while (!queue.push(1)) { + std::this_thread::yield(); + } + } + }; + + auto consumer = [&]() { + int val; + for (int i = 0; i < ops_per_consumer; ++i) { + while (!queue.pop(val)) { + std::this_thread::yield(); + } + consumer_sum += val; + } + }; + std::vector producers; + std::vector consumers; + + for (int i = 0; i < num_producers; ++i) producers.emplace_back(producer); + for (int i = 0; i < num_consumers; ++i) consumers.emplace_back(consumer); + + for (auto& t : producers) t.join(); + for (auto& t : consumers) t.join(); + + EXPECT_EQ(consumer_sum, total_items); +} diff --git a/3rd/cpu_io b/3rd/cpu_io index 4aed17eb4..d7be3e392 160000 --- a/3rd/cpu_io +++ b/3rd/cpu_io @@ -1 +1 @@ -Subproject commit 4aed17eb4f57a0ebc99e94f136808e2613ef8d64 +Subproject commit d7be3e392921ff4500d96c5b4f5120e4bfa56ffd diff --git a/3rd/device_framework b/3rd/device_framework new file mode 160000 index 000000000..9d87e5a01 --- /dev/null +++ b/3rd/device_framework @@ -0,0 +1 @@ +Subproject commit 9d87e5a013a2e11b55350a2e2f24c49af6d851e9 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..f1a5e922c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,254 @@ +# AGENTS.md — SimpleKernel + +## 项目概述 + +SimpleKernel 是一个**面向 AI 辅助学习的现代化操作系统内核项目**。使用 **C++23/C23** 编写,支持 **x86_64**、**RISC-V 64** 和 **AArch64** 三种架构。 + +### 核心设计理念:接口驱动(Interface-Driven) + +- **项目主体是接口定义**——头文件(`.h/.hpp`)只包含类声明、纯虚接口、类型定义和 Doxygen 文档 +- **实现由 AI 完成**——用户阅读接口契约,AI 根据头文件生成 `.cpp` 实现 +- **现有代码是参考实现**——用于验证 AI 生成代码的正确性,不是唯一正确答案 +- **测试套件验证契约**——GoogleTest 测试用例验证实现是否符合接口要求 + +### 你作为 AI 的角色 + +当用户要求你实现某个模块时: +1. **先阅读对应的接口头文件**——理解类声明、纯虚方法、Doxygen 契约(`@pre`/`@post`/`@note`) +2. **在独立的 `.cpp` 文件中生成实现**——不要修改头文件中的接口定义 +3. **确保实现符合契约**——满足前置条件检查、后置条件保证、不引入额外的公共接口 +4. **运行测试验证**——`make unit-test` 或 `make SimpleKernel && make run` + +### 关键接口文件(你最常需要阅读的文件) + +| 接口文件 | 职责 | 对应实现 | +|---------|------|---------| +| `src/arch/arch.h` | 架构无关的统一入口 | `src/arch/{arch}/` 各文件 | +| `src/include/interrupt_base.h` | 中断子系统抽象基类 | `src/arch/{arch}/interrupt.cpp` | +| `src/device/include/device_manager.hpp` | 设备管理器 | header-only | +| `src/device/include/driver_registry.hpp` | 驱动注册中心 | header-only | +| `src/device/include/platform_bus.hpp` | 平台总线(FDT 枚举) | header-only | +| `src/device/include/driver/ns16550a_driver.hpp` | NS16550A UART 驱动 | header-only(Probe/Remove 模式) | +| `src/include/virtual_memory.hpp` | 虚拟内存管理 | `src/virtual_memory.cpp` | +| `src/include/kernel_fdt.hpp` | 设备树解析 | `src/kernel_fdt.cpp` | +| `src/include/kernel_elf.hpp` | ELF 解析 | `src/kernel_elf.cpp` | +| `src/task/include/scheduler_base.hpp` | 调度器抽象基类 | `src/task/*_scheduler.cpp` | +| `src/include/spinlock.hpp` | 自旋锁 | header-only(性能要求) | +| `src/include/mutex.hpp` | 互斥锁 | `src/task/mutex.cpp` | + +## 构建命令 + +```bash +# 前置条件: GCC 交叉工具链, QEMU, CMake 3.27+ +# Docker 替代方案: ptrnull233/simple_kernel:latest +git submodule update --init --recursive # 首次克隆后必须执行 + +# 配置 (选择架构: riscv64 | aarch64 | x86_64) +cmake --preset build_riscv64 + +# 编译内核 (目标名称是 "SimpleKernel",不是 "kernel") +cd build_riscv64 && make SimpleKernel + +# 在 QEMU 中运行 +make run + +# 调试模式 (GDB 连接 localhost:1234) +make debug + +# 运行单元测试 (架构需要与 HOST 一致) +cmake --preset build_x86_64 +cd build_x86_64 && make unit-test + +# 单元测试 + 覆盖率报告 +cd build_x86_64 && make coverage + +# 运行单个测试 (需先构建 unit-test 目标) +cd build_x86_64 && ./tests/unit_test/unit-test --gtest_filter="TestSuiteName.TestName" + +# 格式检查 (pre-commit 在 commit 时自动运行) +pre-commit run --all-files +``` + +**aarch64 特殊要求**: 运行前需要先启动两个串口终端任务 (`::54320` 和 `::54321`)。 + +## 测试框架 + +- **GoogleTest**,使用 `gtest_discover_tests()`。测试二进制文件: `tests/unit_test/unit-test`。 +- 单元测试仅在宿主机运行 (`CMAKE_SYSTEM_PROCESSOR == CMAKE_HOST_SYSTEM_PROCESSOR`)。 +- 测试编译选项: `--coverage`、`-fsanitize=leak`、`-fsanitize=address`。 +- 系统测试在 QEMU 中运行;集成测试可能需要 OpenSBI/U-Boot。 +- 添加模块时: libc/libcxx 需要 unit-test + system-test;架构/内核代码需要 system-test。 + +## 编码规范 + +### 格式化与静态检查 + +- **C/C++**: Google Style,通过 `.clang-format` (`BasedOnStyle: Google`) 自动格式化,pre-commit 强制执行。 +- **静态分析**: `.clang-tidy` 启用 `bugprone-*`、`google-*`、`misc-*`、`modernize-*`、`performance-*`、`portability-*`、`readability-*`(含部分排除)。 +- **CMake**: `cmake-format` + `cmake-lint`,配置文件 `.cmake-format.json`。命令**大写**,关键字**大写**,4 空格缩进,80 字符行宽,函数名与 `(` 之间有空格。 +- **Shell**: `shellcheck`,通过 pre-commit 执行。 + +### 命名约定 + +| 元素 | 约定 | 示例 | +|------|------|------| +| 文件 | `snake_case` | `kernel_log.hpp` | +| 类/结构体 | `PascalCase` | `TaskManager` | +| 函数 | `PascalCase` 或 `snake_case` | `ArchInit()`、`sys_yield()` | +| 变量 | `snake_case` | `per_cpu_data` | +| 成员变量 | `snake_case_`(尾部下划线) | `locked_`、`core_id_` | +| 常量 | `kCamelCase` | `kPageSize`、`kReset` | +| 宏 | `SCREAMING_SNAKE_CASE` | `SIMPLEKERNEL_DEBUG` | +| 内核 libc/libcxx 头文件 | `sk_` 前缀 | `sk_cstdio`、`sk_vector` | + +### Header Guards + +使用 `#ifndef` / `#define` / `#endif`,格式为完整路径: +```cpp +#ifndef SIMPLEKERNEL_SRC_INCLUDE_KERNEL_LOG_HPP_ +#define SIMPLEKERNEL_SRC_INCLUDE_KERNEL_LOG_HPP_ +// ... +#endif /* SIMPLEKERNEL_SRC_INCLUDE_KERNEL_LOG_HPP_ */ +``` + +### 版权头 + +每个文件开头: +```cpp +/** + * @copyright Copyright The SimpleKernel Contributors + */ +``` +CMake 文件使用: `# Copyright The SimpleKernel Contributors` + +### Include 顺序 + +- 系统/标准头文件在前 (``、``),然后第三方 (``),最后项目头文件 (`"arch.h"`、`"kernel_log.hpp"`)。 +- 接口头文件只包含声明所需的头文件;实现所需的头文件放在 `.cpp` 中。 +- 使用内核自己的 libc/libcxx (`sk_cstdio`、`sk_vector`),**禁止**使用标准库的动态内存分配。 + +### 类型与语言特性 + +- **C++23** 和 **C23** 标准。编译选项: `-ffreestanding`、`-fno-rtti`、`-fno-exceptions`。 +- 禁止 RTTI 和异常。使用 `Expected`(基于 `std::expected`)进行错误处理。 +- 适当使用 `std::atomic`、`std::concepts`、`std::source_location`。 +- 尾部返回类型: `auto FunctionName() -> ReturnType`。 +- 性能关键函数使用 `__always_inline`,允许保留在头文件中(如 `SpinLock::Lock()`)。 +- 适当使用 `[[maybe_unused]]`、`[[nodiscard]]` 属性。 +- 不可达代码路径使用 `__builtin_unreachable()`。 + +### 错误处理 + +- 返回 `Expected`(`std::expected` 的别名)——禁止抛出异常。 +- `Error` 类型包含 `ErrorCode` 枚举和 `.message()` 方法。 +- 检查返回值;关键失败时记录日志并停机 (`while (true) { cpu_io::Pause(); }`)。 + +### 接口文件规范(最重要) + +- 头文件**只包含**: 类声明、纯虚接口、类型定义、常量。 +- **禁止在头文件中实现方法**(例外: 标记 `__always_inline` 的性能关键方法)。 +- 每个类和方法必须有 Doxygen 文档: `@brief`、`@pre`(前置条件)、`@post`(后置条件)。 +- 实现放在独立的 `.cpp` 文件中——禁止修改接口头文件来添加实现。 +- 例外:与内核核心无关的工具类(如 `kernel_elf`、`kernel_fdt` 等解析器)可以将实现直接内联在 `.hpp` 头文件中,无需单独的 `.cpp` 文件。 + +## Git Commit 规范 + +使用 --signoff 进行提交 + +``` +(): + +type: feat|fix|bug|docs|style|refactor|perf|test|build|revert|merge|sync|comment +scope: 可选,影响的模块 (如 arch, device, libc, task 等) +subject: 不超过50字符,不加句号 +``` + +## 项目结构 + +``` +src/include/ # 公共接口头文件(先读这里) +src/arch/ # 架构相关代码 (aarch64/riscv64/x86_64) +src/arch/arch.h # 架构无关统一接口 +src/device/ # 设备管理框架 +src/device/include/ # 设备框架接口 (DeviceManager, DriverRegistry, Bus 等) +src/device/include/driver/ # 具体驱动 (ns16550a_driver.hpp, virtio_blk_driver.hpp) +src/device/device.cpp # 设备初始化入口 (DeviceInit) +src/task/ # 任务管理 (调度器, TCB, 同步原语) +src/task/include/ # 调度器/任务接口 +src/libc/ # 内核 C 标准库 (sk_ 前缀) +src/libcxx/ # 内核 C++ 运行时 (sk_ 前缀) +tests/unit_test/ # GoogleTest 单元测试 (仅宿主机) +tests/integration_test/ # 集成测试 +tests/system_test/ # 系统测试 (QEMU) +cmake/ # 工具链文件、编译配置、辅助函数 +3rd/ # Git 子模块 (opensbi, u-boot, googletest, bmalloc, MPMCQueue, device_framework 等) +``` + +## 关键 API + +```cpp +// 日志系统 (kernel_log.hpp) +klog::Debug("msg %d", val); // Debug,带 source_location +klog::Info("msg\n"); // Info +klog::Warn("msg\n"); // Warning +klog::Err("msg\n"); // Error(注意:是 Err 不是 Error) +klog::info << "stream style\n"; // 流式 API + +// 单例模式 +Singleton::GetInstance().AddTask(task); + +// RAII 锁 +LockGuard guard(my_lock); + +// 系统调用 +sys_sleep(1000); sys_yield(); sys_exit(0); + +// 设备框架 +Singleton::GetInstance().Register(Ns16550aDriver::GetDescriptor()); +Singleton::GetInstance().RegisterBus(platform_bus); +Singleton::GetInstance().ProbeAll(); +``` + +## 架构开发指南 + +### 添加新架构功能 +1. 在 `src/arch/{arch}/` 目录下实现架构特定代码 +2. 更新 `src/arch/arch.h` 中的统一接口 +3. 必需文件: `boot.S`、`link.ld`、`arch_main.cpp` + +### 添加新驱动 +1. 在 `src/device/include/driver/` 下创建驱动头文件 +2. 实现 `Probe(DeviceNode&)` / `Remove(DeviceNode&)` 接口和 `GetDescriptor()` 静态方法 +3. 定义 `kMatchTable[]` 匹配表(FDT compatible 字符串) +4. 在 `DriverRegistry` 中注册驱动 + +### 添加测试 +1. 每当添加新模块时,在 `tests/` 下创建测试文件 +2. 使用 GoogleTest 编写测试用例 +3. 更新相应的 `CMakeLists.txt` 以包含新测试 +4. 如果是 libc/libcxx,需要创建 unit-test 与 system-test 两类测试 +5. 如果是架构相关代码或内核代码,需要创建 system-test 测试 + +### 引导链 + +| 架构 | 引导流程 | +|------|---------| +| x86_64 | U-Boot -> kernel.elf | +| riscv64 | U-Boot SPL -> OpenSBI -> U-Boot -> kernel.elf | +| aarch64 | U-Boot -> ATF -> OP-TEE -> kernel.elf | + +## 关键约束 + +- **禁止标准库堆分配** — 使用 `src/libc/` 和 `src/libcxx/` 中的实现。 +- **`-ffreestanding` 环境** — 仅 freestanding 头文件可用(见 cppreference)。 +- **禁止 RTTI 和异常** — 使用 `Expected` 进行错误处理。 +- **交叉编译** — 内核交叉编译;单元测试仅在宿主机架构运行。 +- **引导链因架构而异**: x86_64 (U-Boot)、riscv64 (U-Boot SPL -> OpenSBI -> U-Boot)、aarch64 (U-Boot -> ATF -> OP-TEE)。 + +## 资源链接 + +- **文档目录**: `doc/` (工具链、系统启动、调试输出、中断) +- **Docker 使用**: `doc/docker.md` +- **Git 规范**: `doc/git_commit.md` +- **接口重构计划**: `doc/TODO_interface_refactor.md` +- **调试信息**: `build_{arch}/bin` 目录会自动生成 objdump、nm、map、dts 等文件,QEMU 的运行日志也在这里 diff --git a/CMakePresets.json b/CMakePresets.json index f64baa1bd..107b18851 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -81,7 +81,11 @@ }, "QEMU_COMMON_FLAG": { "type": "STRING", - "value": "-nographic;-serial;stdio;-monitor;telnet::2333,server,nowait;-m;1024M;-smp;2;-netdev;user,id=net0,tftp=/srv/tftp;-device;e1000,netdev=net0;-d;guest_errors,int,cpu_reset" + "value": "-nographic;-serial;stdio;-monitor;telnet::2333,server,nowait;-m;1024M;-smp;2;-d;guest_errors,int,cpu_reset" + }, + "QEMU_DEVICE_FLAGS": { + "type": "STRING", + "value": "-global;virtio-mmio.force-legacy=false;-netdev;user,id=net0,tftp=/srv/tftp;-device;e1000,netdev=net0;-device;virtio-gpu-device" }, "KERNEL_ELF_OUTPUT_NAME": { "type": "STRING", @@ -126,7 +130,7 @@ }, "QEMU_MACHINE_FLAGS": { "type": "STRING", - "value": "" + "value": "-drive;file=${sourceDir}/build_x86_64/bin/rootfs.img,if=none,format=raw,id=hd0;-device;virtio-blk-device,drive=hd0" }, "SIMPLEKERNEL_EARLY_CONSOLE_BASE": { "type": "STRING", @@ -167,7 +171,7 @@ }, "QEMU_MACHINE_FLAGS": { "type": "STRING", - "value": "-machine;virt" + "value": "-machine;virt;-cpu;max;-drive;file=${sourceDir}/build_riscv64/bin/rootfs.img,if=none,format=raw,id=hd0;-device;virtio-blk-device,drive=hd0" }, "SIMPLEKERNEL_EARLY_CONSOLE_BASE": { "type": "STRING", @@ -204,7 +208,7 @@ }, "QEMU_MACHINE_FLAGS": { "type": "STRING", - "value": "-machine;virt,secure=on,gic_version=3;-cpu;cortex-a72" + "value": "-machine;virt,secure=on,gic_version=3;-cpu;cortex-a72;-drive;file=${sourceDir}/build_aarch64/bin/rootfs.img,if=none,format=raw,id=hd0;-device;virtio-blk-device,drive=hd0" }, "SIMPLEKERNEL_EARLY_CONSOLE_BASE": { "type": "STRING", diff --git a/README.md b/README.md index 645cb5150..cefc7bc48 100644 --- a/README.md +++ b/README.md @@ -86,21 +86,24 @@ SimpleKernel 提出一种新范式:**读接口 → 理解契约 → AI 实现 ```cpp /** - * @brief 控制台驱动抽象基类 + * @brief 中断子系统抽象基类 * - * 所有串口/控制台驱动必须实现此接口。 + * 所有架构的中断处理必须实现此接口。 * - * @pre 硬件已完成基本初始化(时钟使能、引脚配置) - * @post 调用 PutChar/GetChar 可进行字符级 I/O + * @pre 硬件中断控制器已初始化 + * @post 可通过 RegisterInterruptFunc 注册中断处理函数 * - * 已知实现:Ns16550a(RISC-V/x86_64)、Pl011(AArch64) + * 已知实现:PLIC(RISC-V)、GIC(AArch64)、APIC(x86_64) */ -class ConsoleDriver { +class InterruptBase { public: - virtual ~ConsoleDriver() = default; - virtual void PutChar(uint8_t c) const = 0; - [[nodiscard]] virtual auto GetChar() const -> uint8_t = 0; - [[nodiscard]] virtual auto TryGetChar() const -> uint8_t = 0; + virtual ~InterruptBase() = default; + + /// 执行中断处理 + virtual void Do(uint64_t cause, cpu_io::TrapContext* context) = 0; + + /// 注册中断处理函数 + virtual void RegisterInterruptFunc(uint64_t cause, InterruptFunc func) = 0; }; ``` @@ -151,9 +154,9 @@ SimpleKernel 的接口按功能分为以下层次: │ InterruptBase · RegisterInterruptFunc │ │ TimerInit · InterruptInit │ ├──────────────────────────────────────────┤ -│ 驱动层 │ -│ ConsoleDriver · Ns16550a · Pl011 │ -│ Gic · Plic · Apic · Timer drivers │ +│ 设备框架层 │ +│ DeviceManager · DriverRegistry │ +│ PlatformBus · Ns16550aDriver · VirtioBlk │ ├──────────────────────────────────────────┤ │ 架构抽象层 (arch.h) │ │ ArchInit · InterruptInit · TimerInit │ @@ -174,7 +177,10 @@ SimpleKernel 的接口按功能分为以下层次: |---------|------|---------| | `src/arch/arch.h` | 架构无关的统一入口 | 各 `src/arch/{arch}/` 目录 | | `src/include/interrupt_base.h` | 中断子系统抽象基类 | `src/arch/{arch}/interrupt.cpp` | -| `src/driver/include/console_driver.h` | 控制台驱动抽象 | `ns16550a.cpp` / `pl011.cpp` | +| `src/device/include/device_manager.hpp` | 设备管理器 | header-only | +| `src/device/include/driver_registry.hpp` | 驱动注册中心 | header-only | +| `src/device/include/platform_bus.hpp` | 平台总线(FDT 枚举) | header-only | +| `src/device/include/driver/ns16550a_driver.hpp` | NS16550A UART 驱动 | header-only(Probe/Remove 模式) | | `src/include/virtual_memory.hpp` | 虚拟内存管理接口 | `src/virtual_memory.cpp` | | `src/include/kernel_fdt.hpp` | 设备树解析接口 | `src/kernel_fdt.cpp` | | `src/include/kernel_elf.hpp` | ELF 解析接口 | `src/kernel_elf.cpp` | @@ -188,7 +194,7 @@ SimpleKernel 的接口按功能分为以下层次: | 架构 | 引导链 | 串口 | 中断控制器 | 时钟 | |:---:|:---:|:---:|:---:|:---:| -| **x86_64** | U-Boot | COM1 | 8259A PIC | 8253/8254 | +| **x86_64** | U-Boot | NS16550A | 8259A PIC | 8253/8254 | | **RISC-V 64** | U-Boot + OpenSBI | SBI Call | Direct 模式 | SBI Timer | | **AArch64** | U-Boot + ATF + OP-TEE | PL011 | GICv3 | Generic Timer | @@ -285,11 +291,10 @@ SimpleKernel/ │ │ ├── aarch64/ # AArch64 实现 │ │ ├── riscv64/ # RISC-V 64 实现 │ │ └── x86_64/ # x86_64 实现 -│ ├── driver/ # 设备驱动 -│ │ ├── include/ # 📐 驱动接口(ConsoleDriver 等) -│ │ ├── ns16550a/ # NS16550A 串口驱动实现 -│ │ ├── pl011/ # PL011 串口驱动实现 -│ │ └── ... +│ ├── device/ # 设备管理框架 +│ │ ├── include/ # 📐 设备框架接口(DeviceManager, DriverRegistry, Bus 等) +│ │ │ └── driver/ # 具体驱动(ns16550a_driver.hpp, virtio_blk_driver.hpp) +│ │ └── device.cpp # 设备初始化入口(DeviceInit) │ ├── task/ # 任务管理 │ │ ├── include/ # 📐 调度器接口(SchedulerBase 等) │ │ └── ... # 调度器实现 @@ -318,7 +323,7 @@ SimpleKernel/ | 模块 | 接口文件 | 难度 | 说明 | |------|---------|:---:|------| | Early Console | `src/arch/arch.h` 注释 | ⭐ | 最早期的输出,理解全局构造 | -| 串口驱动 | `console_driver.h` | ⭐⭐ | 实现 `PutChar`/`GetChar`,理解 MMIO | +| 串口驱动 | `ns16550a_driver.hpp` | ⭐⭐ | 实现 Probe/Remove,理解设备框架和 MMIO | | 设备树解析 | `kernel_fdt.hpp` | ⭐⭐ | 解析硬件信息,理解 FDT 格式 | | ELF 解析 | `kernel_elf.hpp` | ⭐⭐ | 符号表解析,用于栈回溯 | @@ -364,6 +369,9 @@ SimpleKernel/ | [OP-TEE/optee_os](https://github.com/OP-TEE/optee_os.git) | OP-TEE 操作系统 | | [ARM-software/arm-trusted-firmware](https://github.com/ARM-software/arm-trusted-firmware.git) | ARM 可信固件 | | [dtc/dtc](https://git.kernel.org/pub/scm/utils/dtc/dtc.git) | 设备树编译器 | +| [MRNIU/bmalloc](https://github.com/MRNIU/bmalloc.git) | 内存分配器 | +| [MRNIU/MPMCQueue](https://github.com/MRNIU/MPMCQueue.git) | 无锁 MPMC 队列 | +| [MRNIU/device_framework](https://github.com/MRNIU/device_framework.git) | 设备管理框架 | ## 📝 开发指南 @@ -392,7 +400,7 @@ SimpleKernel/ (): type: feat|fix|docs|style|refactor|perf|test|build|revert -scope: 可选,影响的模块 (arch, driver, libc) +scope: 可选,影响的模块 (arch, device, libc) subject: 不超过50字符,不加句号 ``` diff --git a/README_ENG.md b/README_ENG.md index e24cc8334..20029942d 100644 --- a/README_ENG.md +++ b/README_ENG.md @@ -86,21 +86,24 @@ Each module's header file contains complete interface documentation: ```cpp /** - * @brief Console driver abstract base class + * @brief Interrupt subsystem abstract base class * - * All serial/console drivers must implement this interface. + * All architecture interrupt handlers must implement this interface. * - * @pre Hardware has completed basic initialization (clock enable, pin config) - * @post PutChar/GetChar can be used for character-level I/O + * @pre Hardware interrupt controller has been initialized + * @post Can register interrupt handlers via RegisterInterruptFunc * - * Known implementations: Ns16550a (RISC-V/x86_64), Pl011 (AArch64) + * Known implementations: PLIC (RISC-V), GIC (AArch64), APIC (x86_64) */ -class ConsoleDriver { +class InterruptBase { public: - virtual ~ConsoleDriver() = default; - virtual void PutChar(uint8_t c) const = 0; - [[nodiscard]] virtual auto GetChar() const -> uint8_t = 0; - [[nodiscard]] virtual auto TryGetChar() const -> uint8_t = 0; + virtual ~InterruptBase() = default; + + /// Execute interrupt handling + virtual void Do(uint64_t cause, cpu_io::TrapContext* context) = 0; + + /// Register interrupt handler function + virtual void RegisterInterruptFunc(uint64_t cause, InterruptFunc func) = 0; }; ``` @@ -151,9 +154,9 @@ SimpleKernel's interfaces are organized into the following layers: │ InterruptBase · RegisterInterruptFunc │ │ TimerInit · InterruptInit │ ├──────────────────────────────────────────┤ -│ Driver Layer │ -│ ConsoleDriver · Ns16550a · Pl011 │ -│ Gic · Plic · Apic · Timer drivers │ +│ Device Framework Layer │ +│ DeviceManager · DriverRegistry │ +│ PlatformBus · Ns16550aDriver · VirtioBlk │ ├──────────────────────────────────────────┤ │ Architecture Abstraction (arch.h) │ │ ArchInit · InterruptInit · TimerInit │ @@ -175,7 +178,10 @@ SimpleKernel's interfaces are organized into the following layers: |---------------|---------------|-------------------| | `src/arch/arch.h` | Architecture-independent unified entry | Each `src/arch/{arch}/` directory | | `src/include/interrupt_base.h` | Interrupt subsystem abstract base class | `src/arch/{arch}/interrupt.cpp` | -| `src/driver/include/console_driver.h` | Console driver abstraction | `ns16550a.cpp` / `pl011.cpp` | +| `src/device/include/device_manager.hpp` | Device manager | header-only | +| `src/device/include/driver_registry.hpp` | Driver registry | header-only | +| `src/device/include/platform_bus.hpp` | Platform bus (FDT enumeration) | header-only | +| `src/device/include/driver/ns16550a_driver.hpp` | NS16550A UART driver | header-only (Probe/Remove pattern) | | `src/include/virtual_memory.hpp` | Virtual memory management interface | `src/virtual_memory.cpp` | | `src/include/kernel_fdt.hpp` | Device tree parsing interface | `src/kernel_fdt.cpp` | | `src/include/kernel_elf.hpp` | ELF parsing interface | `src/kernel_elf.cpp` | @@ -286,11 +292,10 @@ SimpleKernel/ │ │ ├── aarch64/ # AArch64 implementation │ │ ├── riscv64/ # RISC-V 64 implementation │ │ └── x86_64/ # x86_64 implementation -│ ├── driver/ # Device drivers -│ │ ├── include/ # 📐 Driver interfaces (ConsoleDriver, etc.) -│ │ ├── ns16550a/ # NS16550A serial driver implementation -│ │ ├── pl011/ # PL011 serial driver implementation -│ │ └── ... +│ ├── device/ # Device management framework +│ │ ├── include/ # 📐 Device framework interfaces (DeviceManager, DriverRegistry, Bus, etc.) +│ │ │ └── driver/ # Concrete drivers (ns16550a_driver.hpp, virtio_blk_driver.hpp) +│ │ └── device.cpp # Device initialization entry (DeviceInit) │ ├── task/ # Task management │ │ ├── include/ # 📐 Scheduler interfaces (SchedulerBase, etc.) │ │ └── ... # Scheduler implementations @@ -319,7 +324,7 @@ We recommend learning and implementing modules in the following order: | Module | Interface File | Difficulty | Description | |--------|---------------|:---:|-------------| | Early Console | `src/arch/arch.h` comments | ⭐ | Earliest output, understand global construction | -| Serial Driver | `console_driver.h` | ⭐⭐ | Implement `PutChar`/`GetChar`, understand MMIO | +| Serial Driver | `ns16550a_driver.hpp` | ⭐⭐ | Implement Probe/Remove, understand device framework and MMIO | | Device Tree Parsing | `kernel_fdt.hpp` | ⭐⭐ | Parse hardware info, understand FDT format | | ELF Parsing | `kernel_elf.hpp` | ⭐⭐ | Symbol table parsing, used for stack backtrace | @@ -365,6 +370,9 @@ We recommend learning and implementing modules in the following order: | [OP-TEE/optee_os](https://github.com/OP-TEE/optee_os.git) | OP-TEE operating system | | [ARM-software/arm-trusted-firmware](https://github.com/ARM-software/arm-trusted-firmware.git) | ARM Trusted Firmware | | [dtc/dtc](https://git.kernel.org/pub/scm/utils/dtc/dtc.git) | Device Tree Compiler | +| [MRNIU/bmalloc](https://github.com/MRNIU/bmalloc.git) | Memory allocator | +| [MRNIU/MPMCQueue](https://github.com/MRNIU/MPMCQueue.git) | Lock-free MPMC queue | +| [MRNIU/device_framework](https://github.com/MRNIU/device_framework.git) | Device management framework | ## 📝 Development Guide @@ -393,7 +401,7 @@ We recommend learning and implementing modules in the following order: (): type: feat|fix|docs|style|refactor|perf|test|build|revert -scope: optional, affected module (arch, driver, libc) +scope: optional, affected module (arch, device, libc) subject: max 50 chars, no period ``` diff --git a/cmake/3rd.cmake b/cmake/3rd.cmake index 2c347d72b..6b4fd2787 100644 --- a/cmake/3rd.cmake +++ b/cmake/3rd.cmake @@ -83,6 +83,9 @@ ADD_SUBDIRECTORY (3rd/cpu_io) # https://github.com/MRNIU/MPMCQueue.git ADD_SUBDIRECTORY (3rd/MPMCQueue) +# https://github.com/MRNIU/device_framework.git +ADD_SUBDIRECTORY (3rd/device_framework) + IF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "riscv64") # https://github.com/riscv-software-src/opensbi.git # 编译 opensbi diff --git a/cmake/clang.cmake b/cmake/clang.cmake index 15e048d98..2c63a9466 100644 --- a/cmake/clang.cmake +++ b/cmake/clang.cmake @@ -1,40 +1,43 @@ # Copyright The SimpleKernel Contributors # 目标为无操作系统的环境 -set(CMAKE_SYSTEM_NAME Generic) +SET (CMAKE_SYSTEM_NAME Generic) # 目标架构 -set(CMAKE_SYSTEM_PROCESSOR x86_64) +SET (CMAKE_SYSTEM_PROCESSOR x86_64) # @todo mac 测试 -if (APPLE) - message(STATUS "Now is Apple systems.") +IF(APPLE) + MESSAGE (STATUS "Now is Apple systems.") # @todo -elseif (UNIX) - message(STATUS "Now is UNIX-like OS's.") +ELSEIF(UNIX) + MESSAGE (STATUS "Now is UNIX-like OS's.") # clang - find_program(Compiler_clang++ clang++) - if (NOT Compiler_clang++) - message(FATAL_ERROR "clang++ not found.\n" + FIND_PROGRAM (Compiler_clang++ clang++) + IF(NOT Compiler_clang++) + MESSAGE ( + FATAL_ERROR + "clang++ not found.\n" "Run `sudo apt-get install -y clang clang++` to install.") - else () - message(STATUS "Found clang++ ${Compiler_clang++}") - endif () + ELSE() + MESSAGE (STATUS "Found clang++ ${Compiler_clang++}") + ENDIF() - set(CMAKE_C_COMPILER clang) - set(CMAKE_CXX_COMPILER clang++) - set(CMAKE_READELF readelf) - set(CMAKE_AR ar) - set(CMAKE_LINKER ld) - set(CMAKE_NM nm) - set(CMAKE_OBJDUMP objdump) - set(CMAKE_RANLIB ranlib) + SET (CMAKE_C_COMPILER clang) + SET (CMAKE_CXX_COMPILER clang++) + SET (CMAKE_READELF readelf) + SET (CMAKE_AR ar) + SET (CMAKE_LINKER ld) + SET (CMAKE_NM nm) + SET (CMAKE_OBJDUMP objdump) + SET (CMAKE_RANLIB ranlib) # qemu - find_program(QEMU qemu-system-x86_64) - if (NOT QEMU) - message(FATAL_ERROR "qemu not found.\n" - "Run `sudo apt-get install -y qemu-system` to install.") - else () - message(STATUS "Found qemu ${QEMU}") - endif () -endif () + FIND_PROGRAM (QEMU qemu-system-x86_64) + IF(NOT QEMU) + MESSAGE ( + FATAL_ERROR "qemu not found.\n" + "Run `sudo apt-get install -y qemu-system` to install.") + ELSE() + MESSAGE (STATUS "Found qemu ${QEMU}") + ENDIF() +ENDIF() diff --git a/cmake/compile_config.cmake b/cmake/compile_config.cmake index 23d9849b8..c1a89b7b9 100644 --- a/cmake/compile_config.cmake +++ b/cmake/compile_config.cmake @@ -5,6 +5,7 @@ ADD_LIBRARY (compile_definitions INTERFACE) TARGET_COMPILE_DEFINITIONS ( compile_definitions INTERFACE + _GLIBCXX_NO_ASSERTIONS $<$:SIMPLEKERNEL_RELEASE> $<$:SIMPLEKERNEL_DEBUG> $<$:SIMPLEKERNEL_MAX_CORE_COUNT=${SIMPLEKERNEL_MAX_CORE_COUNT}> @@ -40,7 +41,7 @@ TARGET_COMPILE_OPTIONS ( compile_options INTERFACE # 如果 CMAKE_BUILD_TYPE 为 Release 则使用 -O3 -Werror,否则使用 -O0 -ggdb -g # 在 Debug 模式下由 cmake 自动添加 - $<$:-O3;-Werror> + $<$:-O2;-Werror> $<$:-O0;-ggdb> # 打开全部警告 -Wall @@ -170,6 +171,7 @@ TARGET_LINK_LIBRARIES ( cpu_io bmalloc MPMCQueue + device_framework gcc $<$: opensbi_interface diff --git a/cmake/functions.cmake b/cmake/functions.cmake index 2cd54b84e..ec49986da 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -75,27 +75,29 @@ FUNCTION(add_run_target) $/$.bin ) + # 生成 rootfs.img + ADD_CUSTOM_COMMAND ( + OUTPUT ${CMAKE_BINARY_DIR}/bin/rootfs.img + COMMENT "Generating rootfs.img ..." + VERBATIM + WORKING_DIRECTORY $ + COMMAND dd if=/dev/zero of=${CMAKE_BINARY_DIR}/bin/rootfs.img bs=1M + count=64) + # 生成 QEMU DTS 和 DTB ADD_CUSTOM_COMMAND ( OUTPUT ${CMAKE_BINARY_DIR}/bin/qemu.dtb ${CMAKE_BINARY_DIR}/bin/qemu.dts COMMENT "Generating QEMU DTS and DTB ..." VERBATIM + DEPENDS ${CMAKE_BINARY_DIR}/bin/rootfs.img WORKING_DIRECTORY $ COMMAND qemu-system-${CMAKE_SYSTEM_PROCESSOR} ${QEMU_COMMON_FLAG} - ${QEMU_MACHINE_FLAGS} -machine + ${QEMU_DEVICE_FLAGS} ${QEMU_MACHINE_FLAGS} -machine dumpdtb=$/qemu.dtb COMMAND dtc -I dtb $/qemu.dtb -O dts -o $/qemu.dts) - # 生成空的 qemu.log 文件 - ADD_CUSTOM_COMMAND ( - OUTPUT ${CMAKE_BINARY_DIR}/bin/qemu.log - COMMENT "Generating qemu.log ..." - VERBATIM - WORKING_DIRECTORY $ - COMMAND echo ""> ${CMAKE_BINARY_DIR}/bin/qemu.log) - # 生成 U-BOOT FIT ADD_CUSTOM_TARGET ( ${ARG_TARGET}_gen_fit @@ -127,17 +129,19 @@ FUNCTION(add_run_target) ${ARG_NAME}run COMMENT "Run $ ..." DEPENDS ${ARG_DEPENDS} ${ARG_TARGET}_gen_fit - ${CMAKE_BINARY_DIR}/bin/qemu.log + ${CMAKE_BINARY_DIR}/bin/rootfs.img WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND qemu-system-${CMAKE_SYSTEM_PROCESSOR} ${QEMU_COMMON_FLAG} - ${QEMU_MACHINE_FLAGS} ${ARG_QEMU_BOOT_FLAGS}) + COMMAND + qemu-system-${CMAKE_SYSTEM_PROCESSOR} ${QEMU_COMMON_FLAG} + ${QEMU_DEVICE_FLAGS} ${QEMU_MACHINE_FLAGS} ${ARG_QEMU_BOOT_FLAGS}) ADD_CUSTOM_TARGET ( ${ARG_NAME}debug COMMENT "Debug $ ..." DEPENDS ${ARG_DEPENDS} ${ARG_TARGET}_gen_fit - ${CMAKE_BINARY_DIR}/bin/qemu.log + ${CMAKE_BINARY_DIR}/bin/rootfs.img WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND qemu-system-${CMAKE_SYSTEM_PROCESSOR} ${QEMU_COMMON_FLAG} - ${QEMU_MACHINE_FLAGS} ${QEMU_DEBUG_FLAGS} ${ARG_QEMU_BOOT_FLAGS}) + ${QEMU_DEVICE_FLAGS} ${QEMU_MACHINE_FLAGS} ${QEMU_DEBUG_FLAGS} + ${ARG_QEMU_BOOT_FLAGS}) ENDFUNCTION() diff --git a/cmake/riscv64-gcc.cmake b/cmake/riscv64-gcc.cmake index 9eafc65d2..8591458e7 100644 --- a/cmake/riscv64-gcc.cmake +++ b/cmake/riscv64-gcc.cmake @@ -1,52 +1,58 @@ # Copyright The SimpleKernel Contributors -if (NOT UNIX) - message(FATAL_ERROR "Only support Linux.") -endif () +IF(NOT UNIX) + MESSAGE (FATAL_ERROR "Only support Linux.") +ENDIF() -if (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "riscv64") +IF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "riscv64") # GCC - find_program(Compiler_gcc g++) - if (NOT Compiler_gcc) - message(FATAL_ERROR "g++ not found.\n" - "Run `sudo apt-get install -y gcc g++` to install.") - else () - message(STATUS "Found g++ ${Compiler_gcc}") - endif () -elseif (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64") - find_program(Compiler_gcc riscv64-linux-gnu-g++) - if (NOT Compiler_gcc) - message(FATAL_ERROR "riscv64-linux-gnu-g++ not found.\n" - "Run `sudo apt install -y gcc-riscv64-linux-gnu g++-riscv64-linux-gnu` to install.") - endif () + FIND_PROGRAM (Compiler_gcc g++) + IF(NOT Compiler_gcc) + MESSAGE ( + FATAL_ERROR "g++ not found.\n" + "Run `sudo apt-get install -y gcc g++` to install.") + ELSE() + MESSAGE (STATUS "Found g++ ${Compiler_gcc}") + ENDIF() +ELSEIF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64") + FIND_PROGRAM (Compiler_gcc riscv64-linux-gnu-g++) + IF(NOT Compiler_gcc) + MESSAGE ( + FATAL_ERROR + "riscv64-linux-gnu-g++ not found.\n" + "Run `sudo apt install -y gcc-riscv64-linux-gnu g++-riscv64-linux-gnu` to install." + ) + ENDIF() - set(TOOLCHAIN_PREFIX riscv64-linux-gnu-) - set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) - set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) - set(CMAKE_READELF ${TOOLCHAIN_PREFIX}readelf) - set(CMAKE_AR ${TOOLCHAIN_PREFIX}ar) - set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}ld) - set(CMAKE_NM ${TOOLCHAIN_PREFIX}nm) - set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump) - set(CMAKE_RANLIB ${TOOLCHAIN_PREFIX}ranlib) -elseif (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "aarch64") - find_program(Compiler_gcc_cr riscv64-linux-gnu-g++) - if (NOT Compiler_gcc_cr) - message(FATAL_ERROR "riscv64-linux-gnu-g++ not found.\n" + SET (TOOLCHAIN_PREFIX riscv64-linux-gnu-) + SET (CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) + SET (CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) + SET (CMAKE_READELF ${TOOLCHAIN_PREFIX}readelf) + SET (CMAKE_AR ${TOOLCHAIN_PREFIX}ar) + SET (CMAKE_LINKER ${TOOLCHAIN_PREFIX}ld) + SET (CMAKE_NM ${TOOLCHAIN_PREFIX}nm) + SET (CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump) + SET (CMAKE_RANLIB ${TOOLCHAIN_PREFIX}ranlib) +ELSEIF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "aarch64") + FIND_PROGRAM (Compiler_gcc_cr riscv64-linux-gnu-g++) + IF(NOT Compiler_gcc_cr) + MESSAGE ( + FATAL_ERROR + "riscv64-linux-gnu-g++ not found.\n" "Run `sudo apt install -y g++-riscv64-linux-gnu` to install.") - else () - message(STATUS "Found riscv64-linux-gnu-g++ ${Compiler_gcc_cr}") - endif () + ELSE() + MESSAGE (STATUS "Found riscv64-linux-gnu-g++ ${Compiler_gcc_cr}") + ENDIF() - set(TOOLCHAIN_PREFIX riscv64-linux-gnu-) - set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) - set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) - set(CMAKE_READELF ${TOOLCHAIN_PREFIX}readelf) - set(CMAKE_AR ${TOOLCHAIN_PREFIX}ar) - set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}ld) - set(CMAKE_NM ${TOOLCHAIN_PREFIX}nm) - set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump) - set(CMAKE_RANLIB ${TOOLCHAIN_PREFIX}ranlib) -else () - message(FATAL_ERROR "NOT support ${CMAKE_HOST_SYSTEM_PROCESSOR}") -endif () + SET (TOOLCHAIN_PREFIX riscv64-linux-gnu-) + SET (CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) + SET (CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) + SET (CMAKE_READELF ${TOOLCHAIN_PREFIX}readelf) + SET (CMAKE_AR ${TOOLCHAIN_PREFIX}ar) + SET (CMAKE_LINKER ${TOOLCHAIN_PREFIX}ld) + SET (CMAKE_NM ${TOOLCHAIN_PREFIX}nm) + SET (CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump) + SET (CMAKE_RANLIB ${TOOLCHAIN_PREFIX}ranlib) +ELSE() + MESSAGE (FATAL_ERROR "NOT support ${CMAKE_HOST_SYSTEM_PROCESSOR}") +ENDIF() diff --git a/cmake/x86_64-gcc.cmake b/cmake/x86_64-gcc.cmake index d2b9bccbb..5e3d3c769 100644 --- a/cmake/x86_64-gcc.cmake +++ b/cmake/x86_64-gcc.cmake @@ -1,36 +1,40 @@ # Copyright The SimpleKernel Contributors -if (NOT UNIX) - message(FATAL_ERROR "Only support Linux.") -endif () +IF(NOT UNIX) + MESSAGE (FATAL_ERROR "Only support Linux.") +ENDIF() -if (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64") +IF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64") # GCC - find_program(Compiler_gcc g++) - if (NOT Compiler_gcc) - message(FATAL_ERROR "g++ not found.\n" - "Run `sudo apt-get install -y gcc g++` to install.") - else () - message(STATUS "Found g++ ${Compiler_gcc}") - endif () -elseif (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "aarch64") - find_program(Compiler_gcc_cr x86_64-linux-gnu-g++) - if (NOT Compiler_gcc_cr) - message(FATAL_ERROR "aarch64-linux-gnu-g++ not found.\n" - "Run `sudo apt install -y g++-multilib-x86-64-linux-gnu` to install.") - else () - message(STATUS "Found x86_64-linux-gnu-g++ ${Compiler_gcc_cr}") - endif () + FIND_PROGRAM (Compiler_gcc g++) + IF(NOT Compiler_gcc) + MESSAGE ( + FATAL_ERROR "g++ not found.\n" + "Run `sudo apt-get install -y gcc g++` to install.") + ELSE() + MESSAGE (STATUS "Found g++ ${Compiler_gcc}") + ENDIF() +ELSEIF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "aarch64") + FIND_PROGRAM (Compiler_gcc_cr x86_64-linux-gnu-g++) + IF(NOT Compiler_gcc_cr) + MESSAGE ( + FATAL_ERROR + "aarch64-linux-gnu-g++ not found.\n" + "Run `sudo apt install -y g++-multilib-x86-64-linux-gnu` to install." + ) + ELSE() + MESSAGE (STATUS "Found x86_64-linux-gnu-g++ ${Compiler_gcc_cr}") + ENDIF() - set(TOOLCHAIN_PREFIX x86_64-linux-gnu-) - set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) - set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) - set(CMAKE_READELF ${TOOLCHAIN_PREFIX}readelf) - set(CMAKE_AR ${TOOLCHAIN_PREFIX}ar) - set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}ld) - set(CMAKE_NM ${TOOLCHAIN_PREFIX}nm) - set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump) - set(CMAKE_RANLIB ${TOOLCHAIN_PREFIX}ranlib) -else () - message(FATAL_ERROR "NOT support ${CMAKE_HOST_SYSTEM_PROCESSOR}") -endif () + SET (TOOLCHAIN_PREFIX x86_64-linux-gnu-) + SET (CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) + SET (CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) + SET (CMAKE_READELF ${TOOLCHAIN_PREFIX}readelf) + SET (CMAKE_AR ${TOOLCHAIN_PREFIX}ar) + SET (CMAKE_LINKER ${TOOLCHAIN_PREFIX}ld) + SET (CMAKE_NM ${TOOLCHAIN_PREFIX}nm) + SET (CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump) + SET (CMAKE_RANLIB ${TOOLCHAIN_PREFIX}ranlib) +ELSE() + MESSAGE (FATAL_ERROR "NOT support ${CMAKE_HOST_SYSTEM_PROCESSOR}") +ENDIF() diff --git a/doc/TODO_interface_refactor.md b/doc/TODO_interface_refactor.md deleted file mode 100644 index c0c142a28..000000000 --- a/doc/TODO_interface_refactor.md +++ /dev/null @@ -1,289 +0,0 @@ -# SimpleKernel 接口层重构 TODO - -> **目标**:将 SimpleKernel 转型为"面向 AI 的 OS learning 项目"。第一步是确保所有模块都有清晰的、与实现分离的接口层(.h/.hpp)。 -> -> **原则**: -> -> - 头文件只保留类声明、纯虚接口、类型定义、常量;实现移到 `.cpp` -> - 同类设备驱动应有共同的抽象基类 -> - 每个接口文件需要有完整的 Doxygen 注释,描述职责、前置条件、后置条件 -> - 现有代码作为**参考实现**保留,不改变功能行为 - ---- - -## 🔴 高优先级 - -### TODO-1: 新增 `ConsoleDriver` 抽象基类 - -**背景**:`Ns16550a`(RISC-V/x86_64 串口)和 `Pl011`(AArch64 串口)都实现了相同的接口(`PutChar`/`GetChar`/`TryGetChar`),但没有共同的基类。AI 无法从接口层知道"串口驱动需要实现哪些方法"。 - -**操作**: - -1. 创建 `src/driver/include/console_driver.h` -2. 定义纯虚基类 `ConsoleDriver`,包含以下纯虚方法: - - ```cpp - class ConsoleDriver { - public: - virtual ~ConsoleDriver() = default; - /// 输出一个字符 - virtual void PutChar(uint8_t c) const = 0; - /// 阻塞式读取一个字符 - [[nodiscard]] virtual auto GetChar() const -> uint8_t = 0; - /// 非阻塞式尝试读取一个字符,无数据返回 -1(实际为 0xFF) - [[nodiscard]] virtual auto TryGetChar() const -> uint8_t = 0; - }; - ``` -3. 修改 `src/driver/ns16550a/include/ns16550a.h`,让 `Ns16550a` 继承 `ConsoleDriver`,方法加 `override` -4. 修改 `src/driver/pl011/include/pl011.h`,让 `Pl011` 继承 `ConsoleDriver`,方法加 `override` -5. 确保三个架构的编译都通过 - -**涉及文件**: - -- 新建:`src/driver/include/console_driver.h` -- 修改:`src/driver/ns16550a/include/ns16550a.h` -- 修改:`src/driver/pl011/include/pl011.h` - ---- - -### TODO-2: 新增 `early_console` 接口声明 - -**背景**:三个架构各自在 `early_console.cpp` 中用匿名命名空间实现早期控制台初始化(设置 `sk_putchar` 回调),没有头文件声明。AI 不知道这个模块的存在和职责。 - -**当前实现方式**: - -- `src/arch/riscv64/early_console.cpp`:通过 OpenSBI 的 `sbi_debug_console_write_byte` 设置 `sk_putchar` -- `src/arch/aarch64/early_console.cpp`:通过直接写 PL011 寄存器设置 `sk_putchar` -- `src/arch/x86_64/early_console.cpp`:通过直接写 NS16550A 寄存器设置 `sk_putchar` - -三者都使用了相同的模式:在匿名命名空间中定义 `EarlyConsole` 结构体,在其构造函数中设置 `sk_putchar` 全局函数指针,然后定义一个全局静态实例触发构造。 - -**操作**: - -- 方案 A(推荐):在 `src/arch/arch.h` 中添加注释块说明 early_console 的契约: - - ```cpp - /** - * @brief 早期控制台初始化(由各架构在 early_console.cpp 中通过全局构造函数自动完成) - * - * 契约: - * - 在 _start 之前(全局构造阶段),必须设置 sk_putchar 函数指针(定义在 sk_stdio.h) - * - 设置后 sk_printf / klog 即可用于早期调试输出 - * - 不需要堆、不需要中断、不需要页表 - * - * 各架构实现参考: - * - riscv64: 通过 OpenSBI ecall (sbi_debug_console_write_byte) - * - aarch64: 直接写 PL011 MMIO 寄存器 - * - x86_64: 直接写 NS16550A IO 端口 - */ - ``` -- 方案 B(可选,更显式):创建 `src/arch/include/early_console.h`,声明一个显式的初始化函数 `void EarlyConsoleInit()`,然后修改三个架构的实现导出此函数,在 `_start` 中调用。这需要修改 `boot.S` 或 `arch_main.cpp`,改动较大。 - -**涉及文件**: - -- 方案 A:修改 `src/arch/arch.h`(添加注释) -- 方案 B:新建 `src/arch/include/early_console.h`,修改三个架构的 `early_console.cpp` - ---- - -### TODO-3: 重新设计 `driver.h` - -**背景**:当前 `src/driver/include/driver.h` 只声明了一个 `Driver()` 函数,且 `src/driver/driver.cpp` 的实现是空死循环,没有实际功能。 - -**操作**: - -- 评估是否需要驱动子系统入口。当前各驱动(串口、中断控制器)都是在 `arch_main.cpp` 中按架构直接初始化的 -- 如果保留 `driver.h`:重新定义其职责,例如作为驱动注册/查找的接口 -- 如果不需要:移除 `driver.h` 和 `driver.cpp`,在文档中说明驱动由各架构初始化代码直接管理 -- **建议**:至少将 `Driver()` 的签名和注释更新为有意义的内容,或者直接删除 - -**涉及文件**: - -- `src/driver/include/driver.h` -- `src/driver/driver.cpp` -- `src/driver/CMakeLists.txt`(如果删除的话) - ---- - -## 🟡 中优先级 - -### TODO-4: `VirtualMemory` 接口与实现分离 - -**背景**:`src/include/virtual_memory.hpp` 有 424 行,所有方法(`MapPage`、`UnmapPage`、`FindPageTableEntry`、`MapMMIO`、`GetMapping`、`DestroyPageDirectory`、`ClonePageDirectory`、`RecursiveFreePageTable`、`RecursiveClonePageTable`)全部内联实现在头文件中。这使得 AI 看到头文件就看到了全部答案。 - -**操作**: - -1. 在 `src/include/virtual_memory.hpp` 中只保留: - - - 类声明 - - public/private 方法签名 - - 成员变量定义 - - 常量定义 - - Doxygen 注释 -2. 新建 `src/memory/virtual_memory.cpp`(或放在 `src/` 目录下,与 `memory.cpp` 同级),将所有方法实现移过去 -3. 注意:构造函数中调用了 `MapMMIO`,移动实现时需要保持调用顺序 -4. 更新 `src/CMakeLists.txt` 添加新的源文件 - -**涉及文件**: - -- 修改:`src/include/virtual_memory.hpp`(剥离实现,只留声明) -- 新建:`src/virtual_memory.cpp`(或合适的位置) -- 修改:`src/CMakeLists.txt` - ---- - -### TODO-5: `KernelFdt` 接口与实现分离 - -**背景**:`src/include/kernel_fdt.hpp` 有 548 行,所有 FDT 解析逻辑全部在头文件中内联实现。 - -**操作**: - -1. 在 `src/include/kernel_fdt.hpp` 中只保留类声明和方法签名: - - - `GetCoreCount()`, `CheckPSCI()`, `GetMemory()`, `GetSerial()`, `GetTimebaseFrequency()` 等 public 方法 - - `FindNode()`, `GetRegProperty()`, `GetPsciMethod()` 等 private 方法的签名 - - 成员变量 `fdt_header_` -2. 新建 `src/kernel_fdt.cpp`,移入所有实现 -3. 更新 CMakeLists.txt - -**涉及文件**: - -- 修改:`src/include/kernel_fdt.hpp` -- 新建:`src/kernel_fdt.cpp` -- 修改:`src/CMakeLists.txt` - ---- - -### TODO-6: `KernelElf` 接口与实现分离 - -**背景**:`src/include/kernel_elf.hpp` 有 158 行,构造函数中的完整 ELF 解析逻辑在头文件中。 - -**操作**: - -1. 头文件只保留:类声明、`CheckElfIdentity()` 等方法签名、成员变量(`symtab_`, `strtab_`, `ehdr_`, `phdr_`, `shdr_`, `elf_`) -2. 新建 `src/kernel_elf.cpp`,移入构造函数实现和各方法实现 -3. 更新 CMakeLists.txt - -**涉及文件**: - -- 修改:`src/include/kernel_elf.hpp` -- 新建:`src/kernel_elf.cpp` -- 修改:`src/CMakeLists.txt` - ---- - -### TODO-7: 调度器实现从头文件剥离到 `.cpp` - -**背景**:四个调度器全部 header-only 实现: - -- `src/task/include/cfs_scheduler.hpp`(219 行) -- `src/task/include/fifo_scheduler.hpp`(约 100 行) -- `src/task/include/rr_scheduler.hpp`(124 行) -- `src/task/include/idle_scheduler.hpp`(118 行) - -基类 `scheduler_base.hpp`(153 行)已经是纯虚接口,这是好的。 - -**操作**: -对每个调度器: - -1. 头文件只保留类声明(继承关系、方法签名 + override、私有成员变量) -2. 新建对应 `.cpp` 文件: - - - `src/task/cfs_scheduler.cpp` - - `src/task/fifo_scheduler.cpp` - - `src/task/rr_scheduler.cpp` - - `src/task/idle_scheduler.cpp` -3. 移入所有方法实现 -4. 更新 `src/task/CMakeLists.txt` - -**注意**:当前 `src/task/` 目录下已有 `block.cpp`, `clone.cpp`, `exit.cpp`, `schedule.cpp`, `sleep.cpp`, `task_control_block.cpp`, `task_manager.cpp`, `tick_update.cpp`, `wait.cpp`, `wakeup.cpp`,说明任务管理器本身已经做了接口与实现分离,只是调度器没有。 - -**涉及文件**: - -- 修改:`src/task/include/cfs_scheduler.hpp`, `fifo_scheduler.hpp`, `rr_scheduler.hpp`, `idle_scheduler.hpp` -- 新建:`src/task/cfs_scheduler.cpp`, `fifo_scheduler.cpp`, `rr_scheduler.cpp`, `idle_scheduler.cpp` -- 修改:`src/task/CMakeLists.txt` - ---- - -### TODO-8: `SpinLock` 和 `Mutex` 实现从头文件剥离 - -**背景**: - -- `src/include/spinlock.hpp`(160 行):`Lock()`/`UnLock()` 方法标记为 `__always_inline`,完整实现在头文件中 -- `src/include/mutex.hpp`(215 行):`Lock()`/`UnLock()` 等完整实现在头文件中 - -**操作**: - -- `SpinLock`:因为 `Lock()`/`UnLock()` 标记了 `__always_inline`,性能上需要内联。**建议保留 header-only,但添加更详细的 Doxygen 契约注释**(前置条件、后置条件、副作用)。如果要分离,需要移除 `__always_inline` 并测试性能影响 -- `Mutex`:可以安全地将实现移到 `src/task/mutex.cpp`,头文件只留声明 - -**涉及文件**: - -- `src/include/spinlock.hpp`(增强注释或保持不变) -- `src/include/mutex.hpp`(剥离实现) -- 新建:`src/task/mutex.cpp`(如果分离 Mutex) -- 修改:`src/task/CMakeLists.txt` - ---- - -## 🟢 低优先级 - -### TODO-9: 考虑为定时器添加接口 - -**背景**:`TimerInit()` / `TimerInitSMP()` 在 `src/arch/arch.h` 中声明为自由函数,各架构在 `timer.cpp` 中实现。没有 `Timer` 类或接口。 - -**当前实现**: - -- `src/arch/riscv64/timer.cpp`:通过 OpenSBI 的 `sbi_set_timer` 设置定时器,注册时钟中断处理函数调用 `TaskManager::TickUpdate()` -- `src/arch/aarch64/timer.cpp`:类似,通过 GIC 和系统寄存器 -- `src/arch/x86_64/timer.cpp`:通过 Local APIC Timer - -**操作**(可选): - -- 当前的自由函数声明在 `arch.h` 中已经足够清晰 -- 如果要增加抽象,可以创建 `TimerDriver` 基类,但 OS 内核中定时器通常与架构强耦合,过度抽象可能不合适 -- **建议**:在 `arch.h` 的 `TimerInit` 声明处添加详细的契约注释即可,说明: - - - 前置条件:中断系统已初始化(`InterruptInit` 已调用) - - 后置条件:时钟中断以 `SIMPLEKERNEL_TICK` Hz 频率触发,每次触发调用 `TaskManager::TickUpdate()` - - 依赖:`BasicInfo::interval`(时钟频率) - -**涉及文件**: - -- 修改:`src/arch/arch.h`(增强注释) - ---- - -### TODO-10: 考虑为中断控制器驱动添加统一基类 - -**背景**:`Plic`(RISC-V)、`Gic`(AArch64)、`Apic`(x86_64)是三种不同的中断控制器驱动,各自独立,没有共同基类。 - -**分析**: - -- 这三者的接口差异很大(PLIC 按 source_id/hart_id 管理,GIC 按 SGI/PPI/SPI 分类,APIC 分 Local/IO 两部分) -- 架构层已经通过 `InterruptBase`(`src/include/interrupt_base.h`)做了统一抽象 -- 驱动层强制统一可能导致接口过于泛化或不自然 - -**建议**:暂不添加。当前的分层已经合理: - -- 驱动层(`Plic`/`Gic`/`Apic`):提供硬件特定的操作接口 -- 架构层(`Interrupt : InterruptBase`):封装驱动,提供统一的 `Do`/`RegisterInterruptFunc`/`SendIpi` 接口 - -如果未来需要,可以提取一个轻量级的 `InterruptControllerDriver` 接口,只定义 `Enable(irq)`/`Disable(irq)`/`Ack(irq)` 等最基础操作。 - -**涉及文件**:无(暂不操作) - ---- - -## 📋 验证清单 - -完成以上 TODO 后,需要确保: - -- [ ] `cmake --preset build_riscv64 && cd build_riscv64 && make SimpleKernel` 编译通过 -- [ ] `cmake --preset build_aarch64 && cd build_aarch64 && make SimpleKernel` 编译通过 -- [ ] `cmake --preset build_x86_64 && cd build_x86_64 && make SimpleKernel` 编译通过 -- [ ] 三个架构 `make run` 功能行为不变 -- [ ] 每个头文件只包含:类/函数声明、类型定义、常量、Doxygen 注释 -- [ ] 每个头文件的 Doxygen 注释包含:职责描述、前置条件、后置条件、使用示例(可选) -- [ ] 所有新增/修改的文件符合 `.clang-format` 格式 diff --git a/doc/task_unit_test_new_design.md b/doc/task_unit_test_new_design.md deleted file mode 100644 index 194b90c32..000000000 --- a/doc/task_unit_test_new_design.md +++ /dev/null @@ -1,59 +0,0 @@ -# Task 模块 Unit Test 设计(精简版) - -## 1. 目标 -1) 支持单核/多核调度测试。 2) 每个测试独立、无全局污染。 3) 处理死循环 `while(1) cpu_io::Pause()`。 4) 使用 `std::thread` 模拟核心。 5) 最小化 Mock,尽量复用现有 `TaskManager/PerCpu/Scheduler`。 - -## 2. 架构 -三层结构,职责清晰: -1) `TaskTestHarness`(Fixture):统一 SetUp/TearDown,重置全局状态。 -2) `TaskTestEnvironment`:管理多核线程、任务创建、等待条件。 -3) `SchedulingWorker`:每核一个线程,循环调用 `Schedule()`/`TickUpdate()`。 - -依赖现有:`TaskManager`、`PerCpu`、`FifoScheduler`、`RoundRobinScheduler`、`IdleScheduler`。 Mock 仅覆盖架构相关接口与核心 ID 映射。 - -## 3. 关键机制 -### 3.1 测试隔离 -`TaskTestHarness::SetUp()` 完成: -- 重置 `TaskManager`(新增 `ResetForTesting()`) -- 清理 PID 分配器与全局任务表 -- 重置 `PerCpu` 数组 -- 重新初始化测试环境 - -### 3.2 多核支持与亲和性 -- `MockCoreManager` 管理线程→核心 ID 映射。 `cpu_io::GetCurrentCoreId()` 从这里取值。 -- `TaskTestEnvironment::AddTask()`: - - 若 `task->cpu_affinity` 指定,放入允许的核心(可选择负载最低核心)。 - - 若未指定,按各核心队列长度/负载均衡选择。 - -### 3.3 `switch_to` 处理(架构无关) -单测中不依赖汇编实现,提供"软切换"测试桩: -- 记录切换事件(from/to/时间戳)。 -- 更新 `per_cpu::running_task`。 -- 不执行真实栈切换。 -调度逻辑仍由 `TaskManager::Schedule()` 真实执行,因此可验证调度策略。 - -### 3.4 超时保护(避免死循环) -`TimeoutGuard` 在 4 秒内监控测试: -- 超时仅设置原子标志并停止调度线程(不直接调用 GTest 失败)。 -- 主线程检查 `IsTimeout()` 并决定失败。 - -## 4. 文件结构(最小集) -`tests/unit_test/task/` -- `fixtures/`:`task_test_harness.*`, `task_test_environment.*`, `scheduling_worker.*`, `mock_core_manager.*`, `timeout_guard.*` -- `unit_tests/`:调度器、生命周期、多核、阻塞/唤醒等测试 - -## 5. 必要的最小改动 -1) `TaskManager::ResetForTesting()`:清空调度器、任务表、统计信息。 -2) `TaskControlBlock` 可选提供便捷构造(便于测试创建任务)。 -3) 单测目标链接测试桩版 `switch_to`(不走架构汇编)。 - -## 6. 典型测试覆盖 -- 单核:RR/FIFO 时间片与顺序。 -- 多核:负载均衡与亲和性。 -- 状态:Ready→Running→Blocked/Sleeping→Ready。 -- 异常:超时处理、死锁/无限循环检测。 - -## 7. 为什么这样做 -- 调度逻辑真实运行,Mock 最少。 -- 代码量小、职责清晰、维护成本低。 -- 多核与异常场景可复现且可控。 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 98e38b993..d2a1eb1bd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,17 +9,26 @@ ENABLE_LANGUAGE (CXX) ADD_SUBDIRECTORY (libc) ADD_SUBDIRECTORY (libcxx) ADD_SUBDIRECTORY (arch) -ADD_SUBDIRECTORY (driver) +ADD_SUBDIRECTORY (device) ADD_SUBDIRECTORY (task) +ADD_SUBDIRECTORY (filesystem) -ADD_EXECUTABLE (${PROJECT_NAME} main.cpp memory.cpp syscall.cpp) +ADD_EXECUTABLE (${PROJECT_NAME} main.cpp virtual_memory.cpp io_buffer.cpp + memory.cpp syscall.cpp) # 添加头文件 TARGET_INCLUDE_DIRECTORIES (${PROJECT_NAME} PRIVATE ./ include) # 添加要链接的库 -TARGET_LINK_LIBRARIES (${PROJECT_NAME} PRIVATE kernel_link_libraries libc - libcxx arch driver task) +TARGET_LINK_LIBRARIES ( + ${PROJECT_NAME} + PRIVATE kernel_link_libraries + libc + libcxx + arch + device + task + filesystem) SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${KERNEL_ELF_OUTPUT_NAME}) diff --git a/src/arch/CMakeLists.txt b/src/arch/CMakeLists.txt index 775dee646..0df555ef9 100644 --- a/src/arch/CMakeLists.txt +++ b/src/arch/CMakeLists.txt @@ -17,3 +17,17 @@ TARGET_SOURCES ( ${CMAKE_SYSTEM_PROCESSOR}/timer.cpp ${CMAKE_SYSTEM_PROCESSOR}/interrupt_main.cpp ${CMAKE_SYSTEM_PROCESSOR}/syscall.cpp) + +IF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "riscv64") + ADD_SUBDIRECTORY (${CMAKE_SYSTEM_PROCESSOR}/plic) +ELSEIF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64") + ADD_SUBDIRECTORY (${CMAKE_SYSTEM_PROCESSOR}/gic) +ELSEIF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") + ADD_SUBDIRECTORY (${CMAKE_SYSTEM_PROCESSOR}/apic) +ENDIF() + +TARGET_LINK_LIBRARIES ( + arch + INTERFACE $<$: plic> + $<$: gic> + $<$: apic>) diff --git a/src/arch/aarch64/boot.S b/src/arch/aarch64/boot.S index 75f3aba96..33dc42196 100644 --- a/src/arch/aarch64/boot.S +++ b/src/arch/aarch64/boot.S @@ -13,7 +13,9 @@ _boot: // 按照每个 core 设置栈地址 add x10, x10, #1 - lsl x10, x10, #12 + // 根据 SIMPLEKERNEL_DEFAULT_STACK_SIZE 计算每核栈偏移 + ldr x12, =SIMPLEKERNEL_DEFAULT_STACK_SIZE + mul x10, x10, x12 ldr x11, =stack_top add x11, x11, x10 mov sp, x11 diff --git a/src/arch/aarch64/early_console.cpp b/src/arch/aarch64/early_console.cpp index 7ebba9867..01d204eb1 100644 --- a/src/arch/aarch64/early_console.cpp +++ b/src/arch/aarch64/early_console.cpp @@ -2,13 +2,14 @@ * @copyright Copyright The SimpleKernel Contributors */ -#include "pl011.h" +#include + #include "singleton.hpp" #include "sk_cstdio" namespace { -Pl011* pl011 = nullptr; +device_framework::pl011::Pl011Device* pl011 = nullptr; void console_putchar(int c, [[maybe_unused]] void* ctx) { if (pl011) { @@ -18,10 +19,14 @@ void console_putchar(int c, [[maybe_unused]] void* ctx) { struct EarlyConsole { EarlyConsole() { - Singleton::GetInstance() = Pl011(SIMPLEKERNEL_EARLY_CONSOLE_BASE); - pl011 = &Singleton::GetInstance(); - - sk_putchar = console_putchar; + Singleton::GetInstance() = std::move( + device_framework::pl011::Pl011Device(SIMPLEKERNEL_EARLY_CONSOLE_BASE)); + pl011 = &Singleton::GetInstance(); + auto result = pl011->Open( + device_framework::OpenFlags(device_framework::OpenFlags::kReadWrite)); + if (result) { + sk_putchar = console_putchar; + } } }; diff --git a/src/driver/gic/CMakeLists.txt b/src/arch/aarch64/gic/CMakeLists.txt similarity index 100% rename from src/driver/gic/CMakeLists.txt rename to src/arch/aarch64/gic/CMakeLists.txt diff --git a/src/driver/gic/README.md b/src/arch/aarch64/gic/README.md similarity index 100% rename from src/driver/gic/README.md rename to src/arch/aarch64/gic/README.md diff --git a/src/driver/gic/gic.cpp b/src/arch/aarch64/gic/gic.cpp similarity index 100% rename from src/driver/gic/gic.cpp rename to src/arch/aarch64/gic/gic.cpp diff --git a/src/driver/gic/include/gic.h b/src/arch/aarch64/gic/include/gic.h similarity index 93% rename from src/driver/gic/include/gic.h rename to src/arch/aarch64/gic/include/gic.h index bbc091652..4e4683da6 100644 --- a/src/driver/gic/include/gic.h +++ b/src/arch/aarch64/gic/include/gic.h @@ -33,7 +33,7 @@ class Gic { /// @see /// https://developer.arm.com/documentation/101206/0003/Programmers-model/Distributor-registers--GICD-GICDA--summary /// GICD Register offsets - /// Configuration dependent Distributor Control Register, RW + /// Configuration dependent Distributor Control Register, RW static constexpr uint32_t kCTLR = 0x0000; static constexpr uint32_t kCTLR_EnableGrp1NS = 0x2; /** @@ -45,24 +45,24 @@ class Gic { // [31] Register Write Pending: uint32_t rwp : 1; uint32_t reserved1 : 23; - // [7] E1NWF Enable 1 of N Wakeup Functionality + // [7] E1NWF Enable 1 of N Wakeup Functionality uint32_t e1nwf : 1; - // [6] DS Disable Security + // [6] DS Disable Security uint32_t ds : 1; - // [5] ARE_NS Affinity Routing Enable, Non-secure state + // [5] ARE_NS Affinity Routing Enable, Non-secure state uint32_t are_ns : 1; - // [4] ARE_S Affinity Routing Enable, Secure state + // [4] ARE_S Affinity Routing Enable, Secure state uint32_t are_s : 1; uint32_t reserved0 : 1; - // [2] EnableGrp1S Enable Secure Group 1 interrupts + // [2] EnableGrp1S Enable Secure Group 1 interrupts uint32_t enable_grp1_s : 1; - // [1] EnableGrp1NS Enable Non-secure Group 1 interrupts + // [1] EnableGrp1NS Enable Non-secure Group 1 interrupts uint32_t enable_grp1_ns : 1; - // [0] EnableGrp0 Enable Group 0 interrupts + // [0] EnableGrp0 Enable Group 0 interrupts uint32_t enable_grp0 : 1; }; - /// Configuration dependent Interrupt Controller Type Register, RO + /// Configuration dependent Interrupt Controller Type Register, RO static constexpr uint32_t kTYPER = 0x0004; static constexpr uint32_t kTYPER_ITLinesNumberMask = 0x1F; /** @@ -71,37 +71,37 @@ class Gic { * https://developer.arm.com/documentation/101206/0003/Programmers-model/Distributor-registers--GICD-GICDA--summary/GICD-TYPER--Interrupt-Controller-Type-Register?lang=en */ struct GICD_TYPER { - // [31:26] Reserved, returns 0b000000 + // [31:26] Reserved, returns 0b000000 uint32_t reserved1 : 6; - // [25] No1N 1 of N SPI + // [25] No1N 1 of N SPI uint32_t no1n : 1; - // [24] A3V Affinity level 3 values. + // [24] A3V Affinity level 3 values. uint32_t a3v : 1; - // [23:19] IDbits Interrupt identifier bits + // [23:19] IDbits Interrupt identifier bits uint32_t idbits : 5; - // [18] DVIS Direct virtual LPI injection support + // [18] DVIS Direct virtual LPI injection support uint32_t dvis : 1; - // [17] LPIS Indicates whether the implementation supports LPIs. + // [17] LPIS Indicates whether the implementation supports LPIs. uint32_t lpis : 1; - // [16] MBIS Message-based interrupt support + // [16] MBIS Message-based interrupt support uint32_t mbis : 1; - // [15:11] num_LPIs Returns 0b00000 because + // [15:11] num_LPIs Returns 0b00000 because // GICD_TYPER.IDbits indicates the number of LPIs that the GIC supports. uint32_t num_lpis : 5; - // [10] SecurityExtn Security state support. + // [10] SecurityExtn Security state support. uint32_t security_extn : 1; - // [9:8] - Reserved, returns 0b00000 + // [9:8] - Reserved, returns 0b00000 uint32_t reserved0 : 2; - // [7:5] CPUNumber Returns 0b000 because GICD_CTLR.ARE==1 (ARE_NS & + // [7:5] CPUNumber Returns 0b000 because GICD_CTLR.ARE==1 (ARE_NS & // ARE_S). uint32_t cpu_number : 3; - // [4:0] ITLinesNumber Returns the maximum SPI INTID that this + // [4:0] ITLinesNumber Returns the maximum SPI INTID that this // GIC-600AE implementation supports, and is given by ×ばつ(ITLinesNumber + // 1) − 1. uint32_t it_lines_number : 5; }; - /// Configuration dependent Distributor Implementer Identification Register, + /// Configuration dependent Distributor Implementer Identification Register, /// RO static constexpr uint32_t kIIDR = 0x0008; /** @@ -127,7 +127,7 @@ class Gic { /// Function Control Register, RW static constexpr uint32_t kFCTLR = 0x0020; - /// Tie-off dependentb Secure Access Control register, RW + /// Tie-off dependentb Secure Access Control register, RW static constexpr uint32_t kSAC = 0x0024; /// Non-secure SPI Set Register, WO static constexpr uint32_t kSETSPI_NSR = 0x0040; @@ -226,7 +226,7 @@ class Gic { /// Interrupt Routing Registers, n = 0-991, but n=0-31 are Reserved when /// affinity routing is enabled. static constexpr uint32_t kIROUTERn = 0x6000; - /// P-Channel dependent Chip Status Register, RW + /// P-Channel dependent Chip Status Register, RW static constexpr uint32_t kCHIPSR = 0xC000; /// Default Chip Register, RW static constexpr uint32_t kDCHIPR = 0xC004; @@ -236,9 +236,9 @@ class Gic { static constexpr uint32_t kICLARn = 0xE000; /// Interrupt Clear Error Registers, n = 0-31, but n=0 is Reserved static constexpr uint32_t kICERRRn = 0xE100; - /// Configuration dependent Configuration ID Register, RO + /// Configuration dependent Configuration ID Register, RO static constexpr uint64_t kCFGID = 0xF000; - /// Peripheral ID4 register , RO + /// Peripheral ID4 register , RO static constexpr uint32_t kPIDR4 = 0xFFD0; /// Peripheral ID 5 Register, RO static constexpr uint32_t kPIDR5 = 0xFFD4; @@ -464,9 +464,9 @@ class Gic { /// https://developer.arm.com/documentation/ddi0601/2024-12/External-Registers/GICR-IGRPMODR0--Interrupt-Group-Modifier-Register-0?lang=en static constexpr uint32_t kIGRPMODR0 = 0x0D00; // kIGRPMODR0 kIGROUPR0 Definition - // 0b0 0b0 Secure Group 0 G0S - // 0b0 0b1 Non-secure Group 1 G1NS - // 0b1 0b0 Secure Group 1 G1S + // 0b0 0b0 Secure Group 0 G0S + // 0b0 0b1 Non-secure Group 1 G1NS + // 0b1 0b0 Secure Group 1 G1S static constexpr uint32_t kIGRPMODR0_Clear = 0; static constexpr uint32_t kIGRPMODR0_Set = UINT32_MAX; diff --git a/src/arch/aarch64/include/interrupt.h b/src/arch/aarch64/include/interrupt.h index 49bc1121d..4959138f2 100644 --- a/src/arch/aarch64/include/interrupt.h +++ b/src/arch/aarch64/include/interrupt.h @@ -33,6 +33,9 @@ class Interrupt final : public InterruptBase { void RegisterInterruptFunc(uint64_t cause, InterruptFunc func) override; auto SendIpi(uint64_t target_cpu_mask) -> Expected override; auto BroadcastIpi() -> Expected override; + auto RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, + uint32_t priority, InterruptFunc handler) + -> Expected override; __always_inline void SetUP() const { gic_.SetUP(); } __always_inline void SPI(uint32_t intid, uint32_t cpuid) const { diff --git a/src/arch/aarch64/interrupt.cpp b/src/arch/aarch64/interrupt.cpp index 69241d4c1..c87f1bfc3 100644 --- a/src/arch/aarch64/interrupt.cpp +++ b/src/arch/aarch64/interrupt.cpp @@ -83,3 +83,23 @@ auto Interrupt::BroadcastIpi() -> Expected { return {}; } + +auto Interrupt::RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, + uint32_t priority, + InterruptFunc handler) + -> Expected { + // irq 为 GIC INTID(已含 kSPIBase 偏移) + if (irq>= kMaxInterrupt) { + return std::unexpected(Error(ErrorCode::kIrqChipInvalidIrq)); + } + + // 先注册处理函数 + RegisterInterruptFunc(irq, handler); + + // 再在 GIC 上为指定核心配置并启用 SPI + gic_.SPI(irq, cpu_id); + + klog::Info("RegisterExternalInterrupt: INTID %u, cpu %u, priority %u\n", irq, + cpu_id, priority); + return {}; +} diff --git a/src/arch/aarch64/interrupt_main.cpp b/src/arch/aarch64/interrupt_main.cpp index f5c462bc8..e7393a431 100644 --- a/src/arch/aarch64/interrupt_main.cpp +++ b/src/arch/aarch64/interrupt_main.cpp @@ -4,13 +4,14 @@ #include +#include + #include "arch.h" #include "basic_info.hpp" #include "interrupt.h" #include "io.hpp" #include "kernel_fdt.hpp" #include "kernel_log.hpp" -#include "pl011.h" #include "sk_cstdio" namespace { @@ -139,7 +140,8 @@ extern "C" void error_lower_el_aarch32_handler(cpu_io::TrapContext* context) { } auto uart_handler(uint64_t cause, cpu_io::TrapContext*) -> uint64_t { - sk_putchar(Singleton::GetInstance().TryGetChar(), nullptr); + Singleton::GetInstance() + .HandleInterrupt([](uint8_t ch) { sk_putchar(ch, nullptr); }); return cause; } @@ -152,12 +154,14 @@ void InterruptInit(int, const char**) { klog::Info("uart_intid: %d\n", uart_intid); - Singleton::GetInstance().SPI(uart_intid, - cpu_io::GetCurrentCoreId()); - - // 注册 uart 中断处理函数 - Singleton::GetInstance().RegisterInterruptFunc(uart_intid, - uart_handler); + // 通过统一接口注册 UART 外部中断(先注册 handler,再启用 GIC SPI) + Singleton::GetInstance() + .RegisterExternalInterrupt(uart_intid, cpu_io::GetCurrentCoreId(), 0, + uart_handler) + .or_else([](Error err) -> Expected { + klog::Err("Failed to register UART IRQ: %s\n", err.message()); + return std::unexpected(err); + }); cpu_io::EnableInterrupt(); diff --git a/src/arch/arch.h b/src/arch/arch.h index 538c332ad..c7879b978 100644 --- a/src/arch/arch.h +++ b/src/arch/arch.h @@ -25,44 +25,77 @@ extern "C" void trap_return(void*); extern "C" void trap_entry(); /** - * 体系结构相关初始化 + * @brief 体系结构相关初始化 * @param argc 在不同体系结构有不同含义,同 _start * @param argv 在不同体系结构有不同含义,同 _start + * @pre 引导程序已完成基本硬件初始化 + * @post 架构相关硬件(串口、内存、设备树等)已初始化 */ void ArchInit(int argc, const char** argv); +/** + * @brief 从核的体系结构相关初始化 + * @param argc 在不同体系结构有不同含义,同 _start + * @param argv 在不同体系结构有不同含义,同 _start + * @pre 主核已完成 ArchInit + * @post 从核的架构相关硬件已初始化 + */ void ArchInitSMP(int argc, const char** argv); /** - * 唤醒其余 core + * @brief 唤醒其余 core + * @pre 主核已完成初始化 + * @post 所有从核开始执行 ArchInitSMP */ void WakeUpOtherCores(); /** - * 体系结构相关中断初始化 + * @brief 体系结构相关中断初始化 * @param argc 在不同体系结构有不同含义,同 _start * @param argv 在不同体系结构有不同含义,同 _start + * @pre ArchInit 已完成 + * @post 中断控制器已初始化,中断向量表已设置 */ void InterruptInit(int argc, const char** argv); +/** + * @brief 从核的体系结构相关中断初始化 + * @param argc 在不同体系结构有不同含义,同 _start + * @param argv 在不同体系结构有不同含义,同 _start + * @pre 主核已完成 InterruptInit + * @post 从核的中断控制器已初始化 + */ void InterruptInitSMP(int argc, const char** argv); - +/** + * @brief 初始化定时器 + * @pre InterruptInit 已完成 + * @post 定时器中断已启用,系统 tick 开始计数 + */ void TimerInit(); +/** + * @brief 从核的定时器初始化 + * @pre 主核已完成 TimerInit,从核已完成 InterruptInitSMP + * @post 从核的定时器中断已启用 + */ void TimerInitSMP(); /** - * 初始化内核线程的任务上下文(重载1) + * @brief 初始化内核线程的任务上下文(重载1) * @param task_context 指向任务上下文的指针 * @param entry 线程入口函数 * @param arg 传递给线程的参数 * @param stack_top 内核栈顶地址 + * @pre task_context 不为 nullptr,stack_top 已按架构要求对齐 + * @post task_context 已设置为可被 switch_to 恢复的状态 */ void InitTaskContext(cpu_io::CalleeSavedContext* task_context, void (*entry)(void*), void* arg, uint64_t stack_top); /** - * 初始化用户线程的任务上下文(重载2) + * @brief 初始化用户线程的任务上下文(重载2) * @param task_context 指向任务上下文的指针 * @param trap_context_ptr 指向 Trap 上下文的指针 * @param stack_top 内核栈顶地址 + * @pre task_context 不为 nullptr,trap_context_ptr 已填充用户态寄存器 + * @post task_context 已设置为经由 trap_return 返回用户态的状态 */ void InitTaskContext(cpu_io::CalleeSavedContext* task_context, cpu_io::TrapContext* trap_context_ptr, uint64_t stack_top); @@ -71,15 +104,18 @@ void InitTaskContext(cpu_io::CalleeSavedContext* task_context, static constexpr size_t kMaxFrameCount = 128; /** - * 获取调用栈 + * @brief 获取调用栈 * @param buffer 指向一个数组,该数组用于存储调用栈中的返回地址 * @param size 数组的大小,即调用栈中最多存储多少个返回地址 - * @return int 成功时返回实际写入数组中的地址数量,失败时返回 -1 + * @return 成功时返回实际写入数组中的地址数量,失败时返回 -1 + * @pre buffer 不为 nullptr,size> 0 + * @post buffer 中包含从当前帧开始的返回地址序列 */ __always_inline auto backtrace(void** buffer, int size) -> int; /** - * 打印调用栈 + * @brief 打印调用栈 + * @note 调用 backtrace 获取调用栈并解析符号后输出到日志 */ void DumpStack(); diff --git a/src/arch/riscv64/boot.S b/src/arch/riscv64/boot.S index e3a98c492..1143156dd 100644 --- a/src/arch/riscv64/boot.S +++ b/src/arch/riscv64/boot.S @@ -28,7 +28,9 @@ _boot: 2: // 按照每个 core 设置栈地址 add t0, a0, 1 - slli t0, t0, 12 + // 根据 SIMPLEKERNEL_DEFAULT_STACK_SIZE 计算每核栈偏移 + li t1, SIMPLEKERNEL_DEFAULT_STACK_SIZE + mul t0, t0, t1 la sp, stack_top add sp, sp, t0 diff --git a/src/arch/riscv64/include/interrupt.h b/src/arch/riscv64/include/interrupt.h index 0ad7062cb..66ecd7ce8 100644 --- a/src/arch/riscv64/include/interrupt.h +++ b/src/arch/riscv64/include/interrupt.h @@ -33,6 +33,9 @@ class Interrupt final : public InterruptBase { void RegisterInterruptFunc(uint64_t cause, InterruptFunc func) override; auto SendIpi(uint64_t target_cpu_mask) -> Expected override; auto BroadcastIpi() -> Expected override; + auto RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, + uint32_t priority, InterruptFunc handler) + -> Expected override; private: /// 中断处理函数数组 diff --git a/src/arch/riscv64/interrupt.cpp b/src/arch/riscv64/interrupt.cpp index cd1c9e5a3..6278ea433 100644 --- a/src/arch/riscv64/interrupt.cpp +++ b/src/arch/riscv64/interrupt.cpp @@ -123,3 +123,23 @@ auto Interrupt::BroadcastIpi() -> Expected { return SendIpi(mask); } + +auto Interrupt::RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, + uint32_t priority, + InterruptFunc handler) + -> Expected { + if (irq>= Plic::kInterruptMaxCount) { + return std::unexpected(Error(ErrorCode::kIrqChipInvalidIrq)); + } + + // 先注册处理函数 + Singleton::GetInstance().RegisterInterruptFunc( + static_cast(irq), handler); + + // 再在 PLIC 上为指定核心启用该中断 + Singleton::GetInstance().Set(cpu_id, irq, priority, true); + + klog::Info("RegisterExternalInterrupt: IRQ %u, cpu %u, priority %u\n", irq, + cpu_id, priority); + return {}; +} diff --git a/src/arch/riscv64/interrupt_main.cpp b/src/arch/riscv64/interrupt_main.cpp index 890114b1d..6e47df3f5 100644 --- a/src/arch/riscv64/interrupt_main.cpp +++ b/src/arch/riscv64/interrupt_main.cpp @@ -4,13 +4,17 @@ #include +#include + #include "arch.h" #include "basic_info.hpp" +#include "driver/virtio_blk_driver.hpp" +#include "driver_registry.hpp" #include "interrupt.h" #include "kernel_fdt.hpp" #include "kernel_log.hpp" -#include "ns16550a.h" #include "opensbi_interface.h" +#include "platform_traits.hpp" #include "sk_cstdio" #include "sk_iostream" #include "syscall.hpp" @@ -19,28 +23,36 @@ namespace { void RegisterInterrupts() { - // 注册外部中断 + // 注册外部中断分发器:CPU 外部中断 -> PLIC -> 设备 handler Singleton::GetInstance().RegisterInterruptFunc( cpu_io::detail::register_info::csr::ScauseInfo:: kSupervisorExternalInterrupt, - [](uint64_t, cpu_io::TrapContext*) -> uint64_t { + [](uint64_t, cpu_io::TrapContext* context) -> uint64_t { // 获取触发中断的源 ID auto source_id = Singleton::GetInstance().Which(); - Singleton::GetInstance().Do(source_id, nullptr); + Singleton::GetInstance().Do(source_id, context); Singleton::GetInstance().Done(source_id); return 0; }); auto [base, size, irq] = Singleton::GetInstance().GetSerial().value(); - Singleton::GetInstance() = std::move(Ns16550a(base)); - - // 注册串口中断 - Singleton::GetInstance().RegisterInterruptFunc( - std::get<2>(Singleton::GetInstance().GetSerial().value()), - [](uint64_t, uint8_t*) -> uint64_t { - sk_putchar(Singleton::GetInstance().TryGetChar(), nullptr); - return 0; + auto uart_result = device_framework::ns16550a::Ns16550aDevice::Create(base); + if (uart_result) { + Singleton::GetInstance() = + std::move(*uart_result); + } else { + klog::Err("Failed to create Ns16550aDevice: %d\n", + static_cast(uart_result.error().code)); + } + Singleton::GetInstance() + .OpenReadWrite() + .or_else([](device_framework::Error e) + -> device_framework::Expected { + klog::Err( + "Failed to open device_framework::ns16550a::Ns16550aDevice: %d\n", + static_cast(e.code)); + return device_framework::Expected{}; }); // 注册 ebreak 中断 @@ -144,11 +156,48 @@ void InterruptInit(int, const char**) { // 开启外部中断 cpu_io::Sie::Seie::Set(); - // 为当前 core 开启串口中断 - Singleton::GetInstance().Set( - cpu_io::GetCurrentCoreId(), - std::get<2>(Singleton::GetInstance().GetSerial().value()), 1, - true); + // 通过统一接口注册串口外部中断(先注册 handler,再启用 PLIC) + auto serial_irq = + std::get<2>(Singleton::GetInstance().GetSerial().value()); + Singleton::GetInstance() + .RegisterExternalInterrupt( + serial_irq, cpu_io::GetCurrentCoreId(), 1, + [](uint64_t, cpu_io::TrapContext*) -> uint64_t { + Singleton::GetInstance() + .HandleInterrupt([](uint8_t ch) { sk_putchar(ch, nullptr); }); + return 0; + }) + .or_else([](Error err) -> Expected { + klog::Err("Failed to register serial IRQ: %s\n", err.message()); + return std::unexpected(err); + }); + + // 通过统一接口注册 virtio-blk 外部中断 + using BlkDriver = VirtioBlkDriver; + auto& blk_driver = DriverRegistry::GetDriverInstance(); + auto blk_irq = blk_driver.GetIrq(); + if (blk_irq != 0) { + Singleton::GetInstance() + .RegisterExternalInterrupt( + blk_irq, cpu_io::GetCurrentCoreId(), 1, + [](uint64_t, cpu_io::TrapContext*) -> uint64_t { + DriverRegistry::GetDriverInstance< + VirtioBlkDriver>() + .HandleInterrupt( + [](void* /*token*/, device_framework::ErrorCode status) { + if (status != device_framework::ErrorCode::kSuccess) { + klog::Err("VirtIO blk IO error: %d\n", + static_cast(status)); + } + }); + return 0; + }) + .or_else([blk_irq](Error err) -> Expected { + klog::Err("Failed to register virtio-blk IRQ %u: %s\n", blk_irq, + err.message()); + return std::unexpected(err); + }); + } // 初始化定时器 TimerInit(); diff --git a/src/driver/plic/CMakeLists.txt b/src/arch/riscv64/plic/CMakeLists.txt similarity index 100% rename from src/driver/plic/CMakeLists.txt rename to src/arch/riscv64/plic/CMakeLists.txt diff --git a/src/driver/plic/README.md b/src/arch/riscv64/plic/README.md similarity index 100% rename from src/driver/plic/README.md rename to src/arch/riscv64/plic/README.md diff --git a/src/driver/plic/include/plic.h b/src/arch/riscv64/plic/include/plic.h similarity index 95% rename from src/driver/plic/include/plic.h rename to src/arch/riscv64/plic/include/plic.h index ccddef192..98c49c62c 100644 --- a/src/driver/plic/include/plic.h +++ b/src/arch/riscv64/plic/include/plic.h @@ -5,6 +5,8 @@ #ifndef SIMPLEKERNEL_SRC_DRIVER_PLIC_INCLUDE_PLIC_H_ #define SIMPLEKERNEL_SRC_DRIVER_PLIC_INCLUDE_PLIC_H_ +#include + #include #include #include @@ -21,7 +23,11 @@ class Plic { * @param cause 中断号 * @param context 中断上下文 */ - typedef uint64_t (*InterruptFunc)(uint64_t cause, uint8_t* context); + typedef uint64_t (*InterruptFunc)(uint64_t cause, + cpu_io::TrapContext* context); + + /// 最大外部中断数量 + static constexpr size_t kInterruptMaxCount = 16; /** * 构造函数 @@ -46,7 +52,7 @@ class Plic { * @param cause 中断或异常号 * @param context 中断上下文 */ - void Do(uint64_t cause, uint8_t* context) const; + void Do(uint64_t cause, cpu_io::TrapContext* context) const; /** * @brief 向 Plic 询问中断 @@ -91,7 +97,7 @@ class Plic { * @param cause 外部中断号 * @param context 中断上下文 */ - void Do(uint64_t cause, uint8_t* context); + void Do(uint64_t cause, cpu_io::TrapContext* context); private: static constexpr uint64_t kSourcePriorityOffset = 0x000000; @@ -107,9 +113,6 @@ class Plic { // Enable bits 每个 context 的大小 (最多支持 1024 个中断源) static constexpr uint64_t kEnableSize = 0x80; - /// 最大外部中断数量 - static constexpr size_t kInterruptMaxCount = 16; - /// 外部中断处理函数数组 static std::array interrupt_handlers_; diff --git a/src/driver/plic/plic.cpp b/src/arch/riscv64/plic/plic.cpp similarity index 98% rename from src/driver/plic/plic.cpp rename to src/arch/riscv64/plic/plic.cpp index a22b192e1..7a1a676a7 100644 --- a/src/driver/plic/plic.cpp +++ b/src/arch/riscv64/plic/plic.cpp @@ -50,7 +50,7 @@ void Plic::RegisterInterruptFunc(uint8_t cause, InterruptFunc func) { interrupt_handlers_[cause] = func; } -void Plic::Do(uint64_t cause, uint8_t* context) { +void Plic::Do(uint64_t cause, cpu_io::TrapContext* context) { interrupt_handlers_[cause](cause, context); } diff --git a/src/driver/apic/CMakeLists.txt b/src/arch/x86_64/apic/CMakeLists.txt similarity index 100% rename from src/driver/apic/CMakeLists.txt rename to src/arch/x86_64/apic/CMakeLists.txt diff --git a/src/driver/apic/README.md b/src/arch/x86_64/apic/README.md similarity index 100% rename from src/driver/apic/README.md rename to src/arch/x86_64/apic/README.md diff --git a/src/driver/apic/apic.cpp b/src/arch/x86_64/apic/apic.cpp similarity index 100% rename from src/driver/apic/apic.cpp rename to src/arch/x86_64/apic/apic.cpp diff --git a/src/driver/apic/include/apic.h b/src/arch/x86_64/apic/include/apic.h similarity index 100% rename from src/driver/apic/include/apic.h rename to src/arch/x86_64/apic/include/apic.h diff --git a/src/driver/apic/include/io_apic.h b/src/arch/x86_64/apic/include/io_apic.h similarity index 100% rename from src/driver/apic/include/io_apic.h rename to src/arch/x86_64/apic/include/io_apic.h diff --git a/src/driver/apic/include/local_apic.h b/src/arch/x86_64/apic/include/local_apic.h similarity index 100% rename from src/driver/apic/include/local_apic.h rename to src/arch/x86_64/apic/include/local_apic.h diff --git a/src/driver/apic/io_apic.cpp b/src/arch/x86_64/apic/io_apic.cpp similarity index 100% rename from src/driver/apic/io_apic.cpp rename to src/arch/x86_64/apic/io_apic.cpp diff --git a/src/driver/apic/local_apic.cpp b/src/arch/x86_64/apic/local_apic.cpp similarity index 100% rename from src/driver/apic/local_apic.cpp rename to src/arch/x86_64/apic/local_apic.cpp diff --git a/src/arch/x86_64/include/interrupt.h b/src/arch/x86_64/include/interrupt.h index 9104991c4..77a323021 100644 --- a/src/arch/x86_64/include/interrupt.h +++ b/src/arch/x86_64/include/interrupt.h @@ -33,6 +33,12 @@ class Interrupt final : public InterruptBase { void RegisterInterruptFunc(uint64_t cause, InterruptFunc func) override; auto SendIpi(uint64_t target_cpu_mask) -> Expected override; auto BroadcastIpi() -> Expected override; + auto RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, + uint32_t priority, InterruptFunc handler) + -> Expected override; + + /// 外部中断向量基址(IO APIC IRQ 到 IDT 向量的映射) + static constexpr uint8_t kExternalVectorBase = 0x20; /** * @brief 初始化 idtr diff --git a/src/arch/x86_64/include/sipi.h b/src/arch/x86_64/include/sipi.h index 7b01ca241..14839e972 100644 --- a/src/arch/x86_64/include/sipi.h +++ b/src/arch/x86_64/include/sipi.h @@ -13,9 +13,9 @@ /// 启动 APs 的默认地址 static constexpr uint64_t kDefaultAPBase = 0x30000; -extern "C" void *ap_start16[]; -extern "C" void *ap_start64_end[]; -extern "C" void *sipi_params[]; +extern "C" void* ap_start16[]; +extern "C" void* ap_start64_end[]; +extern "C" void* sipi_params[]; struct sipi_params_t { uint32_t cr3; diff --git a/src/arch/x86_64/interrupt.cpp b/src/arch/x86_64/interrupt.cpp index 4afaaaa8c..c75df736b 100644 --- a/src/arch/x86_64/interrupt.cpp +++ b/src/arch/x86_64/interrupt.cpp @@ -111,3 +111,29 @@ auto Interrupt::BroadcastIpi() -> Expected { /// @todo return std::unexpected(Error(ErrorCode::kIpiSendFailed)); } + +auto Interrupt::RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, + uint32_t priority, + InterruptFunc handler) + -> Expected { + // 计算 IDT 向量号:kExternalVectorBase + irq + auto vector = static_cast(kExternalVectorBase + irq); + if (vector>= cpu_io::detail::register_info::IdtrInfo::kInterruptMaxCount) { + return std::unexpected(Error(ErrorCode::kApicInvalidIrq)); + } + + // 先注册处理函数 + RegisterInterruptFunc(vector, handler); + + // 再在 IO APIC 上启用 IRQ 重定向到指定核心 + // 注: x86 APIC 优先级由向量号隐含决定,priority 参数不直接使用 + auto result = Singleton::GetInstance().SetIrqRedirection( + static_cast(irq), static_cast(vector), cpu_id, false); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + + klog::Info("RegisterExternalInterrupt: IRQ %u -> vector 0x%X, cpu %u\n", irq, + static_cast(vector), cpu_id); + return {}; +} diff --git a/src/arch/x86_64/interrupt_main.cpp b/src/arch/x86_64/interrupt_main.cpp index ae92e52c6..b373ddcd1 100644 --- a/src/arch/x86_64/interrupt_main.cpp +++ b/src/arch/x86_64/interrupt_main.cpp @@ -16,9 +16,6 @@ namespace { constexpr uint8_t kApicTimerVector = 0xF0; constexpr uint32_t kApicTimerFrequencyHz = 100; -// 定义键盘中断向量号 -constexpr uint8_t kKeyboardVector = 0xF1; - /** * @brief APIC 时钟中断处理函数 * @param cause 中断原因 @@ -78,44 +75,25 @@ uint64_t KeyboardHandler(uint64_t cause, cpu_io::TrapContext* context) { return 0; } -bool EnableKeyboardInterrupt(uint8_t vector) { - klog::Info("Enabling keyboard interrupt with vector 0x%x\n", vector); - - // 键盘使用 IRQ 1 (传统 PS/2 键盘) - constexpr uint8_t kKeyboardIrq = 1; - - // 获取当前 CPU 的 APIC ID 作为目标 - uint32_t destination_apic_id = cpu_io::GetCurrentCoreId(); - klog::Info("Target APIC ID: 0x%x\n", destination_apic_id); - - // 通过 APIC 设置 IRQ 重定向到指定向量 - auto result = Singleton::GetInstance().SetIrqRedirection( - kKeyboardIrq, vector, destination_apic_id, false); - - if (result.has_value()) { - klog::Info("Keyboard interrupt enabled successfully\n"); - return true; - } else { - klog::Err("Failed to enable keyboard interrupt\n"); - return false; - } -} - }; // namespace void InterruptInit(int, const char**) { Singleton::GetInstance().SetUpIdtr(); - // 注册中断处理函数 + // 注册 APIC Timer 中断处理函数(Local APIC 内部中断,不走 IO APIC) Singleton::GetInstance().RegisterInterruptFunc(kApicTimerVector, ApicTimerHandler); - // 注册键盘中断处理函数 - Singleton::GetInstance().RegisterInterruptFunc(kKeyboardVector, - KeyboardHandler); - - // 启用键盘中断 - EnableKeyboardInterrupt(kKeyboardVector); + // 通过统一接口注册键盘外部中断(IRQ 1 = PS/2 键盘,先注册 handler 再启用 IO + // APIC) + constexpr uint8_t kKeyboardIrq = 1; + Singleton::GetInstance() + .RegisterExternalInterrupt(kKeyboardIrq, cpu_io::GetCurrentCoreId(), 0, + KeyboardHandler) + .or_else([](Error err) -> Expected { + klog::Err("Failed to register keyboard IRQ: %s\n", err.message()); + return std::unexpected(err); + }); // 启用 Local APIC 定时器 Singleton::GetInstance().SetupPeriodicTimer(kApicTimerFrequencyHz, diff --git a/src/arch/x86_64/sipi.h b/src/arch/x86_64/sipi.h index 7b01ca241..14839e972 100644 --- a/src/arch/x86_64/sipi.h +++ b/src/arch/x86_64/sipi.h @@ -13,9 +13,9 @@ /// 启动 APs 的默认地址 static constexpr uint64_t kDefaultAPBase = 0x30000; -extern "C" void *ap_start16[]; -extern "C" void *ap_start64_end[]; -extern "C" void *sipi_params[]; +extern "C" void* ap_start16[]; +extern "C" void* ap_start64_end[]; +extern "C" void* sipi_params[]; struct sipi_params_t { uint32_t cr3; diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt new file mode 100644 index 000000000..a79999948 --- /dev/null +++ b/src/device/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright The SimpleKernel Contributors + +ADD_LIBRARY (device INTERFACE) + +TARGET_INCLUDE_DIRECTORIES (device INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + include) + +TARGET_SOURCES (device INTERFACE device.cpp) diff --git a/src/device/device.cpp b/src/device/device.cpp new file mode 100644 index 000000000..8e348d48b --- /dev/null +++ b/src/device/device.cpp @@ -0,0 +1,54 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include + +#include "device_manager.hpp" +#include "driver/ns16550a_driver.hpp" +#include "driver/virtio_blk_driver.hpp" +#include "kernel_fdt.hpp" +#include "kernel_log.hpp" +#include "platform_bus.hpp" +#include "platform_traits.hpp" +#include "singleton.hpp" + +/// 设备初始化入口 +auto DeviceInit() -> void { + auto& dm = Singleton::GetInstance(); + auto& fdt = Singleton::GetInstance(); + + // 注册驱动 + auto reg_uart = dm.GetRegistry().Register(); + if (!reg_uart.has_value()) { + klog::Err("DeviceInit: failed to register Ns16550aDriver: %s\n", + reg_uart.error().message()); + return; + } + + auto reg_blk = dm.GetRegistry().Register>(); + if (!reg_blk.has_value()) { + klog::Err("DeviceInit: failed to register VirtioBlkDriver: %s\n", + reg_blk.error().message()); + return; + } + + // 注册 Platform 总线并枚举设备 + PlatformBus platform_bus(fdt); + auto bus_result = dm.RegisterBus(platform_bus); + if (!bus_result.has_value()) { + klog::Err("DeviceInit: PlatformBus enumeration failed: %s\n", + bus_result.error().message()); + return; + } + + // 匹配驱动并 Probe + auto probe_result = dm.ProbeAll(); + if (!probe_result.has_value()) { + klog::Err("DeviceInit: ProbeAll failed: %s\n", + probe_result.error().message()); + return; + } + + klog::Info("DeviceInit: complete\n"); +} diff --git a/src/device/include/acpi_bus.hpp b/src/device/include/acpi_bus.hpp new file mode 100644 index 000000000..d84fa6d0d --- /dev/null +++ b/src/device/include/acpi_bus.hpp @@ -0,0 +1,59 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief ACPI 总线 — 通过 ACPI 表枚举设备 + * @todo 实现 ACPI DSDT/SSDT 设备枚举 + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_ACPI_BUS_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_ACPI_BUS_HPP_ + +#include "bus.hpp" +#include "device_node.hpp" +#include "expected.hpp" + +/** + * @brief ACPI 总线 — 通过 ACPI 表枚举设备 + * @todo 实现 ACPI 总线枚举 + */ +class AcpiBus { + public: + /** + * @brief 构造函数 + * @param rsdp RSDP 地址(由 firmware/bootloader 传入) + */ + explicit AcpiBus(uint64_t rsdp) : rsdp_(rsdp) {} + + /** + * @brief 获取总线名称 + * @return const char* 总线名称 + */ + static auto GetName() -> const char* { return "acpi"; } + + /** + * @brief 设备枚举 + * @todo 实现 ACPI 设备枚举 + * @param out 输出设备节点指针数组 + * @param max 最大枚举数量 + * @return Expected 成功时返回实际枚举数量,失败时返回错误 + */ + auto Enumerate([[maybe_unused]] DeviceNode* out, [[maybe_unused]] size_t max) + -> Expected { + // ACPI 表解析尚未实现 + return static_cast(0); + } + + /// @name 构造/析构函数 + /// @{ + AcpiBus() = delete; + ~AcpiBus() = default; + AcpiBus(const AcpiBus&) = delete; + AcpiBus(AcpiBus&&) = delete; + auto operator=(const AcpiBus&) -> AcpiBus& = delete; + auto operator=(AcpiBus&&) -> AcpiBus& = delete; + /// @} + + private: + uint64_t rsdp_; +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_ACPI_BUS_HPP_ */ diff --git a/src/device/include/bus.hpp b/src/device/include/bus.hpp new file mode 100644 index 000000000..94a82cbb1 --- /dev/null +++ b/src/device/include/bus.hpp @@ -0,0 +1,23 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_BUS_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_BUS_HPP_ + +#include +#include + +#include "device_node.hpp" +#include "expected.hpp" + +/// 总线 concept — 每种总线负责枚举自己管辖范围内的设备 +template +concept Bus = requires(B b, DeviceNode* out, size_t max) { + /// 枚举该总线上所有设备,填充到 out[],返回发现的设备数 + { b.Enumerate(out, max) } -> std::same_as>; + /// 返回总线名称(用于日志) + { B::GetName() } -> std::same_as; +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_BUS_HPP_ */ diff --git a/src/device/include/device_manager.hpp b/src/device/include/device_manager.hpp new file mode 100644 index 000000000..ae1bf3bdb --- /dev/null +++ b/src/device/include/device_manager.hpp @@ -0,0 +1,155 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief 设备管理器 + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DEVICE_MANAGER_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DEVICE_MANAGER_HPP_ + +#include "bus.hpp" +#include "device_node.hpp" +#include "driver_registry.hpp" +#include "expected.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "spinlock.hpp" + +/** + * @brief 设备管理器 — 管理所有设备节点和驱动 + */ +class DeviceManager { + public: + /** + * @brief 注册一条总线并立即枚举其上的设备 + * @tparam B 总线类型 + * @param bus 总线实例 + * @return Expected 成功时返回 void,失败时返回错误 + */ + template + auto RegisterBus(B& bus) -> Expected { + LockGuard guard(lock_); + + if (device_count_>= kMaxDevices) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + size_t remaining = kMaxDevices - device_count_; + auto result = bus.Enumerate(devices_ + device_count_, remaining); + if (!result.has_value()) { + klog::Err("DeviceManager: bus '%s' enumeration failed: %s\n", + B::GetName(), result.error().message()); + return std::unexpected(result.error()); + } + + size_t count = result.value(); + // 分配全局设备编号 + for (size_t i = 0; i < count; ++i) { + devices_[device_count_ + i].dev_id = next_dev_id_++; + } + device_count_ += count; + + klog::Info("DeviceManager: bus '%s' enumerated %zu device(s)\n", + B::GetName(), count); + return {}; + } + + /** + * @brief 匹配已注册驱动并 Probe 所有未绑定设备 + * @return Expected 成功时返回 void,失败时返回错误 + */ + auto ProbeAll() -> Expected { + LockGuard guard(lock_); + + size_t probed = 0; + for (size_t i = 0; i < device_count_; ++i) { + if (devices_[i].bound.load(std::memory_order_acquire)) { + continue; + } + + auto* drv = registry_.FindDriver(devices_[i].resource); + if (drv == nullptr) { + klog::Debug("DeviceManager: no driver for '%s'\n", devices_[i].name); + continue; + } + + klog::Info("DeviceManager: probing '%s' with driver '%s'\n", + devices_[i].name, drv->descriptor->name); + + auto result = drv->probe(devices_[i]); + if (!result.has_value()) { + klog::Err("DeviceManager: probe '%s' failed: %s\n", devices_[i].name, + result.error().message()); + continue; + } + + devices_[i].bound.store(true, std::memory_order_release); + ++probed; + klog::Info("DeviceManager: '%s' bound to '%s'\n", devices_[i].name, + drv->descriptor->name); + } + + klog::Info("DeviceManager: probed %zu device(s)\n", probed); + return {}; + } + + /** + * @brief 通过名称查找设备 + * @note 并发安全:由于是只读路径,在设备枚举完成且设备表(device_count_ 和 + * devices_)稳定后,并发调用是安全的,无需加锁。 + * @param name 设备名称 + * @return Expected 成功时返回设备节点指针;失败时返回 + * kDeviceNotFound 错误 + */ + auto FindDevice(const char* name) -> Expected { + for (size_t i = 0; i < device_count_; ++i) { + if (strcmp(devices_[i].name, name) == 0) { + return &devices_[i]; + } + } + return std::unexpected(Error(ErrorCode::kDeviceNotFound)); + } + + /** + * @brief 通过类型枚举设备 + * @param type 设备类型 + * @param out 输出设备节点指针数组 + * @param max 最大枚举数量 + * @return size_t 实际找到的设备数量 + */ + auto FindDevicesByType(DeviceType type, DeviceNode** out, size_t max) + -> size_t { + size_t found = 0; + for (size_t i = 0; i < device_count_ && found < max; ++i) { + if (devices_[i].type == type) { + out[found++] = &devices_[i]; + } + } + return found; + } + + /** + * @brief 获取驱动注册表 + * @return DriverRegistry& 驱动注册表实例 + */ + auto GetRegistry() -> DriverRegistry& { return registry_; } + + /// @name 构造/析构函数 + /// @{ + DeviceManager() = default; + ~DeviceManager() = default; + DeviceManager(const DeviceManager&) = delete; + DeviceManager(DeviceManager&&) = delete; + auto operator=(const DeviceManager&) -> DeviceManager& = delete; + auto operator=(DeviceManager&&) -> DeviceManager& = delete; + /// @} + + private: + static constexpr size_t kMaxDevices = 64; + DeviceNode devices_[kMaxDevices]{}; + size_t device_count_{0}; + uint32_t next_dev_id_{0}; + DriverRegistry registry_; + SpinLock lock_{"device_manager"}; +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DEVICE_MANAGER_HPP_ */ diff --git a/src/device/include/device_node.hpp b/src/device/include/device_node.hpp new file mode 100644 index 000000000..428df286c --- /dev/null +++ b/src/device/include/device_node.hpp @@ -0,0 +1,144 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DEVICE_NODE_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DEVICE_NODE_HPP_ + +#include +#include +#include +#include + +#include "io_buffer.hpp" +#include "sk_cstring" + +/// Platform 设备标识(FDT compatible stringlist) +struct PlatformId { + /// 完整的 compatible stringlist(多个字符串以 '0円' 分隔) + char compatible[128]; + /// stringlist 的实际字节长度(包含所有 '0円' 分隔符) + size_t compatible_len{0}; +}; + +/// PCI 设备标识 +struct PciAddress { + /// PCI segment group + uint16_t segment; + uint8_t bus; + uint8_t device; + uint8_t function; + uint16_t vendor_id; + uint16_t device_id; + uint8_t class_code; + uint8_t subclass; +}; + +/// ACPI 设备标识 +struct AcpiId { + /// Hardware ID, e.g. "PNP0501" + char hid[16]; + /// Unique ID + char uid[16]; +}; + +/// 总线特定标识(类型安全 variant) +using BusId = std::variant; + +/// 设备类型 +enum class DeviceType : uint8_t { + /// 字符设备 + kChar, + /// 块设备 + kBlock, + /// 网络设备 + kNet, + /// 平台设备(中断控制器、定时器等) + kPlatform, +}; + +/// 设备硬件资源描述 +struct DeviceResource { + /// MMIO 区域 + struct MmioRegion { + uint64_t base; + size_t size; + }; + + /// PCI 最多 6 个 BAR + static constexpr size_t kMaxMmioRegions = 6; + MmioRegion mmio[kMaxMmioRegions]{}; + uint8_t mmio_count{0}; + + /// 中断资源 + static constexpr size_t kMaxIrqs = 4; + uint32_t irq[kMaxIrqs]{}; + uint8_t irq_count{0}; + + /// 总线特定标识 + BusId id; + + /// 便捷方法 + [[nodiscard]] auto IsPlatform() const { + return std::holds_alternative(id); + } + [[nodiscard]] auto IsPci() const { + return std::holds_alternative(id); + } + [[nodiscard]] auto IsAcpi() const { + return std::holds_alternative(id); + } +}; + +/// 设备节点 — 系统中每个设备的统一表示 +struct DeviceNode { + /// 设备名称 + char name[32]{}; + /// 设备类型 + DeviceType type{}; + /// 硬件资源 + DeviceResource resource{}; + /// 是否已绑定驱动 + std::atomic bound{false}; + /// 全局设备编号 + uint32_t dev_id{0}; + /// DMA 缓冲区 + IoBuffer* dma_buffer{nullptr}; + + /// 尝试绑定(CAS 保证幂等,防止重复绑定) + auto TryBind() -> bool { + bool expected = false; + return bound.compare_exchange_strong(expected, true, + std::memory_order_acq_rel); + } + + /// @name 构造/析构函数 + /// @{ + DeviceNode() = default; + ~DeviceNode() = default; + DeviceNode(const DeviceNode& other) + : type(other.type), + resource(other.resource), + bound(other.bound.load(std::memory_order_relaxed)), + dev_id(other.dev_id), + dma_buffer(other.dma_buffer) { + sk_std::memcpy(name, other.name, sizeof(name)); + } + auto operator=(const DeviceNode& other) -> DeviceNode& { + if (this != &other) { + sk_std::memcpy(name, other.name, sizeof(name)); + type = other.type; + resource = other.resource; + bound.store(other.bound.load(std::memory_order_relaxed), + std::memory_order_relaxed); + dev_id = other.dev_id; + dma_buffer = other.dma_buffer; + } + return *this; + } + DeviceNode(DeviceNode&&) = delete; + auto operator=(DeviceNode&&) -> DeviceNode& = delete; + /// @} +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DEVICE_NODE_HPP_ */ diff --git a/src/device/include/df_bridge.hpp b/src/device/include/df_bridge.hpp new file mode 100644 index 000000000..774ebc4ae --- /dev/null +++ b/src/device/include/df_bridge.hpp @@ -0,0 +1,217 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief device_framework ↔ SimpleKernel 桥接层 + * + * 提供错误类型映射和通用 Probe 辅助模板,消除每个驱动的重复样板。 + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DF_BRIDGE_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DF_BRIDGE_HPP_ + +#include +#include + +#include "device_node.hpp" +#include "expected.hpp" +#include "kernel_log.hpp" +#include "virtual_memory.hpp" + +namespace df_bridge { + +/** + * @brief 类型安全的设备存储 — 在固定内存中管理设备生命周期 + * + * 使用 placement new/delete 在内联缓冲区中构造和销毁设备实例, + * 替代手动 StorageBlock + reinterpret_cast 模式。 + * + * @tparam T 设备类型 + */ +template +class DeviceStorage { + public: + /** + * @brief 在存储中原地构造设备实例(若已有实例则先销毁) + * @tparam Args 构造参数类型 + * @param args 构造参数 + * @return T& 设备实例引用 + */ + template + auto Emplace(Args&&... args) -> T& { + Destroy(); + new (data_) T(static_cast(args)...); + valid_ = true; + return *reinterpret_cast(data_); + } + + /** + * @brief 获取设备指针(无效时返回 nullptr) + * @return T* 设备实例指针 + */ + [[nodiscard]] auto Get() -> T* { + return valid_ ? reinterpret_cast(data_) : nullptr; + } + + /** + * @brief 获取设备指针(const 版本) + * @return const T* 设备实例常量指针 + */ + [[nodiscard]] auto Get() const -> const T* { + return valid_ ? reinterpret_cast(data_) : nullptr; + } + + /** + * @brief 是否持有有效实例 + * @return bool 是否持有有效实例 + */ + [[nodiscard]] auto IsValid() const -> bool { return valid_; } + + /** + * @brief 销毁设备实例 + */ + auto Destroy() -> void { + if (valid_) { + reinterpret_cast(data_)->~T(); + valid_ = false; + } + } + + /// @name 构造/析构函数 + /// @{ + ~DeviceStorage() { Destroy(); } + DeviceStorage() = default; + DeviceStorage(const DeviceStorage&) = delete; + auto operator=(const DeviceStorage&) -> DeviceStorage& = delete; + DeviceStorage(DeviceStorage&&) = delete; + auto operator=(DeviceStorage&&) -> DeviceStorage& = delete; + /// @} + + private: + alignas(alignof(T)) uint8_t data_[sizeof(T)]{}; + bool valid_{false}; +}; + +/** + * @brief 将 device_framework::ErrorCode 映射为 SimpleKernel ErrorCode + * @param code device_framework 错误码 + * @return ErrorCode 内核错误码 + */ +constexpr auto ToKernelErrorCode(device_framework::ErrorCode code) + -> ErrorCode { + switch (code) { + case device_framework::ErrorCode::kSuccess: + return ErrorCode::kSuccess; + case device_framework::ErrorCode::kInvalidArgument: + return ErrorCode::kInvalidArgument; + case device_framework::ErrorCode::kOutOfMemory: + return ErrorCode::kOutOfMemory; + + // 设备操作层错误 → 内核 Device 错误 + case device_framework::ErrorCode::kDeviceAlreadyOpen: + return ErrorCode::kDeviceAlreadyOpen; + case device_framework::ErrorCode::kDeviceNotOpen: + return ErrorCode::kDeviceNotOpen; + case device_framework::ErrorCode::kDeviceNotSupported: + return ErrorCode::kDeviceNotSupported; + case device_framework::ErrorCode::kDevicePermissionDenied: + return ErrorCode::kDevicePermissionDenied; + case device_framework::ErrorCode::kDeviceBlockUnaligned: + return ErrorCode::kDeviceBlockUnaligned; + case device_framework::ErrorCode::kDeviceBlockOutOfRange: + return ErrorCode::kDeviceBlockOutOfRange; + case device_framework::ErrorCode::kDeviceReadFailed: + return ErrorCode::kDeviceReadFailed; + + // 通用设备错误 + case device_framework::ErrorCode::kDeviceError: + case device_framework::ErrorCode::kIoError: + return ErrorCode::kDeviceReadFailed; + case device_framework::ErrorCode::kNotSupported: + return ErrorCode::kDeviceNotSupported; + case device_framework::ErrorCode::kTimeout: + return ErrorCode::kDeviceBusy; + + // 传输层 / 队列错误 → 视为设备不可用 + default: + return ErrorCode::kDeviceNotFound; + } +} + +/** + * @brief 将 device_framework::Error 映射为 SimpleKernel Error + * @param err device_framework 错误 + * @return Error 内核 Error + */ +constexpr auto ToKernelError(const device_framework::Error& err) -> Error { + return Error(ToKernelErrorCode(err.code)); +} + +/** + * @brief MMIO Probe 上下文 — TryBind + 提取 MMIO + 映射的公共流程 + */ +struct MmioProbeContext { + uint64_t base; + size_t size; +}; + +/** + * @brief 执行通用 MMIO Probe 前置流程 + * @param node 设备节点 + * @param default_size 默认 MMIO 映射大小(当 FDT 未给出 size 时使用) + * @return Expected 成功时返回 + * MmioProbeContext;失败时节点已回滚 + */ +inline auto PrepareMmioProbe(DeviceNode& node, size_t default_size) + -> Expected { + if (!node.TryBind()) { + return std::unexpected(Error(ErrorCode::kDeviceNotFound)); + } + + uint64_t base = node.resource.mmio[0].base; + if (base == 0) { + klog::Err("df_bridge: no MMIO base for '%s'\n", node.name); + node.bound.store(false, std::memory_order_release); + return std::unexpected(Error(ErrorCode::kDeviceNotFound)); + } + + size_t size = node.resource.mmio[0].size> 0 ? node.resource.mmio[0].size + : default_size; + + // 映射 MMIO 区域到虚拟地址空间 + Singleton::GetInstance().MapMMIO(base, size); + + return MmioProbeContext{base, size}; +} + +/** + * @brief 通用 MMIO Probe 辅助 — TryBind → 提取 MMIO → 映射 → 调用 create_fn + * + * @tparam CreateFn 可调用类型: (uint64_t base) -> + * device_framework::Expected + * @param node 设备节点 + * @param default_size 默认 MMIO 映射大小 + * @param create_fn 设备工厂函数 + * @return Expected> + * 成功时返回设备实例;失败时节点已回滚 + */ +template +auto ProbeWithMmio(DeviceNode& node, size_t default_size, CreateFn&& create_fn) + -> Expected> { + auto ctx = PrepareMmioProbe(node, default_size); + if (!ctx) { + return std::unexpected(ctx.error()); + } + + auto result = create_fn(ctx->base); + if (!result) { + klog::Err("df_bridge: device creation failed at 0x%lX: %s\n", ctx->base, + result.error().message()); + node.bound.store(false, std::memory_order_release); + return std::unexpected(ToKernelError(result.error())); + } + + return std::move(*result); +} + +} // namespace df_bridge + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DF_BRIDGE_HPP_ */ diff --git a/src/device/include/driver/ns16550a_driver.hpp b/src/device/include/driver/ns16550a_driver.hpp new file mode 100644 index 000000000..c8a5007b1 --- /dev/null +++ b/src/device/include/driver/ns16550a_driver.hpp @@ -0,0 +1,61 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_NS16550A_DRIVER_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_NS16550A_DRIVER_HPP_ + +#include + +#include "device_node.hpp" +#include "df_bridge.hpp" +#include "driver_registry.hpp" +#include "expected.hpp" +#include "kernel_log.hpp" +#include "singleton.hpp" + +class Ns16550aDriver { + public: + using Ns16550aType = device_framework::ns16550a::Ns16550a; + + static constexpr PlatformCompatible kMatchNs16550a{"ns16550a"}; + static constexpr PlatformCompatible kMatchNs16550{"ns16550"}; + static constexpr MatchEntry kMatchTable[] = { + kMatchNs16550a, + kMatchNs16550, + }; + + static auto GetDescriptor() -> const DriverDescriptor& { + static const DriverDescriptor desc{ + .name = "ns16550a", + .match_table = kMatchTable, + .match_count = sizeof(kMatchTable) / sizeof(kMatchTable[0]), + }; + return desc; + } + + auto Probe(DeviceNode& node) -> Expected { + auto result = df_bridge::ProbeWithMmio( + node, 0x100, [](uint64_t base) { return Ns16550aType::Create(base); }); + if (!result) { + return std::unexpected(result.error()); + } + uart_ = std::move(*result); + + node.type = DeviceType::kChar; + klog::Info("Ns16550aDriver: UART at 0x%lX bound\n", + node.resource.mmio[0].base); + return {}; + } + + auto Remove([[maybe_unused]] DeviceNode& node) -> Expected { + return {}; + } + + [[nodiscard]] auto GetDevice() -> Ns16550aType* { return &uart_; } + + private: + Ns16550aType uart_; +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_NS16550A_DRIVER_HPP_ */ diff --git a/src/device/include/driver/virtio_blk_driver.hpp b/src/device/include/driver/virtio_blk_driver.hpp new file mode 100644 index 000000000..62ebf1995 --- /dev/null +++ b/src/device/include/driver/virtio_blk_driver.hpp @@ -0,0 +1,137 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_VIRTIO_BLK_DRIVER_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_VIRTIO_BLK_DRIVER_HPP_ + +#include + +#include "device_node.hpp" +#include "df_bridge.hpp" +#include "driver_registry.hpp" +#include "expected.hpp" +#include "kernel_log.hpp" +#include "singleton.hpp" + +template +class VirtioBlkDriver { + public: + using VirtioBlkType = device_framework::virtio::blk::VirtioBlk
; + + static constexpr PlatformCompatible kPlatformMatch{"virtio,mmio"}; + static constexpr MatchEntry kMatchTable[] = { + kPlatformMatch, + }; + static constexpr uint32_t kMmioRegionSize = 0x1000; + static constexpr uint32_t kDefaultQueueCount = 1; + static constexpr uint32_t kDefaultQueueSize = 128; + static constexpr size_t kMinDmaBufferSize = 32768; + + static auto GetDescriptor() -> const DriverDescriptor& { + static const DriverDescriptor desc{ + .name = "virtio-blk", + .match_table = kMatchTable, + .match_count = sizeof(kMatchTable) / sizeof(kMatchTable[0]), + }; + return desc; + } + + auto Probe(DeviceNode& node) -> Expected { + auto ctx = df_bridge::PrepareMmioProbe(node, kMmioRegionSize); + if (!ctx) { + return std::unexpected(ctx.error()); + } + + auto base = ctx->base; + + auto magic = *reinterpret_cast(base); + if (magic != device_framework::virtio::kMmioMagicValue) { + klog::Debug("VirtioBlkDriver: 0x%lX not a VirtIO device (magic=0x%X)\n", + base, magic); + return std::unexpected(Error(ErrorCode::kDeviceNotFound)); + } + + constexpr uint32_t kBlockDeviceId = 2; + auto device_id = *reinterpret_cast( + base + device_framework::virtio::MmioTransport::MmioReg::kDeviceId); + if (device_id != kBlockDeviceId) { + klog::Debug("VirtioBlkDriver: 0x%lX device_id=%u (not block)\n", base, + device_id); + return std::unexpected(Error(ErrorCode::kDeviceNotFound)); + } + + uint64_t extra_features = + static_cast( + device_framework::virtio::blk::BlkFeatureBit::kSegMax) | + static_cast( + device_framework::virtio::blk::BlkFeatureBit::kSizeMax) | + static_cast( + device_framework::virtio::blk::BlkFeatureBit::kBlkSize) | + static_cast( + device_framework::virtio::blk::BlkFeatureBit::kFlush) | + static_cast( + device_framework::virtio::blk::BlkFeatureBit::kGeometry); + + if (node.dma_buffer == nullptr || !node.dma_buffer->IsValid()) { + klog::Err( + "VirtioBlkDriver: Missing or invalid DMA buffer in DeviceNode at " + "0x%lX\n", + base); + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (node.dma_buffer->GetBuffer().second < kMinDmaBufferSize) { + klog::Err("VirtioBlkDriver: DMA buffer too small (%zu < %u)\n", + node.dma_buffer->GetBuffer().second, kMinDmaBufferSize); + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // Pass the pointer to Create (ensure the size meets virtio queue + // requirements) GetBuffer() returns std::pair + auto result = VirtioBlkType::Create( + base, node.dma_buffer->GetBuffer().first, kDefaultQueueCount, + kDefaultQueueSize, extra_features); + if (!result.has_value()) { + klog::Err("VirtioBlkDriver: Create failed at 0x%lX\n", base); + return std::unexpected(df_bridge::ToKernelError(result.error())); + } + + device_.Emplace(std::move(*result)); + node.type = DeviceType::kBlock; + + if (node.resource.irq_count> 0) { + irq_ = node.resource.irq[0]; + } + + auto capacity = device_.Get()->GetCapacity(); + klog::Info( + "VirtioBlkDriver: block device at 0x%lX, capacity=%lu sectors, " + "irq=%u\n", + base, capacity, irq_); + + return {}; + } + + auto Remove([[maybe_unused]] DeviceNode& node) -> Expected { + device_.Destroy(); + return {}; + } + + [[nodiscard]] auto GetDevice() -> VirtioBlkType* { return device_.Get(); } + + [[nodiscard]] auto GetIrq() const -> uint32_t { return irq_; } + + template + auto HandleInterrupt(CompletionCallback&& on_complete) -> void { + if (auto* dev = device_.Get(); dev != nullptr) { + dev->HandleInterrupt(static_cast(on_complete)); + } + } + + private: + df_bridge::DeviceStorage device_; + uint32_t irq_{0}; +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_VIRTIO_BLK_DRIVER_HPP_ */ diff --git a/src/device/include/driver_registry.hpp b/src/device/include/driver_registry.hpp new file mode 100644 index 000000000..57818acc9 --- /dev/null +++ b/src/device/include/driver_registry.hpp @@ -0,0 +1,166 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief 驱动注册表、Driver concept 与匹配机制 + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_REGISTRY_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_REGISTRY_HPP_ + +#include +#include +#include + +#include "device_node.hpp" +#include "expected.hpp" +#include "sk_cstring" +#include "spinlock.hpp" + +/// @name 驱动匹配键 tagged types +/// @{ + +/// Platform 匹配键(FDT compatible 字符串) +struct PlatformCompatible { + const char* compatible; +}; + +/// PCI 匹配键(vendor + device ID) +struct PciMatchKey { + uint16_t vendor_id; + uint16_t device_id; +}; + +/// ACPI 匹配键(Hardware ID) +struct AcpiHid { + const char* hid; +}; + +/// @} + +/// 驱动匹配表条目 +using MatchEntry = std::variant; + +/// 驱动描述符 +struct DriverDescriptor { + const char* name; + const MatchEntry* match_table; + size_t match_count; +}; + +/// 驱动 concept — 所有驱动必须满足 +template +concept Driver = requires(D d, DeviceNode& node) { + { D::GetDescriptor() } -> std::same_as; + { d.Probe(node) } -> std::same_as>; + { d.Remove(node) } -> std::same_as>; +}; + +/// 类型擦除的驱动注册项 +struct DriverEntry { + const DriverDescriptor* descriptor; + auto (*probe)(DeviceNode&) -> Expected; + auto (*remove)(DeviceNode&) -> Expected; +}; + +/// 驱动注册表 +class DriverRegistry { + public: + /// @brief 获取指定驱动类型的持久单例实例 + /// + /// 每个驱动类型 D 有且仅有一个实例,在首次调用时构造, + /// 生存期持续到程序结束。DriverRegistry 在 Probe/Remove 时 + /// 操作此实例,外部也可通过此方法访问驱动状态。 + /// + /// @tparam D 驱动类型(必须满足 Driver concept) + /// @return 驱动实例的引用 + template + static auto GetDriverInstance() -> D& { + static D instance; + return instance; + } + + /// 注册一个驱动(编译期类型安全,运行期类型擦除存储) + template + auto Register() -> Expected { + LockGuard guard(lock_); + if (count_>= kMaxDrivers) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + (void)GetDriverInstance(); + + drivers_[count_] = DriverEntry{ + .descriptor = &D::GetDescriptor(), + .probe = [](DeviceNode& node) -> Expected { + return GetDriverInstance().Probe(node); + }, + .remove = [](DeviceNode& node) -> Expected { + return GetDriverInstance().Remove(node); + }, + }; + ++count_; + return {}; + } + + /// 根据设备资源查找匹配的驱动 + auto FindDriver(const DeviceResource& resource) -> DriverEntry* { + for (size_t i = 0; i < count_; ++i) { + const auto* desc = drivers_[i].descriptor; + for (size_t j = 0; j < desc->match_count; ++j) { + if (Matches(desc->match_table[j], resource)) { + return &drivers_[i]; + } + } + } + return nullptr; + } + + /// @name 构造/析构函数 + /// @{ + DriverRegistry() = default; + ~DriverRegistry() = default; + DriverRegistry(const DriverRegistry&) = delete; + DriverRegistry(DriverRegistry&&) = delete; + auto operator=(const DriverRegistry&) -> DriverRegistry& = delete; + auto operator=(DriverRegistry&&) -> DriverRegistry& = delete; + /// @} + + private: + /// 检查匹配条目是否匹配设备资源 + static auto Matches(const MatchEntry& entry, const DeviceResource& resource) + -> bool { + return std::visit( + [&resource](const auto& key) -> bool { + using T = std::decay_t; + + if constexpr (std::is_same_v) { + if (!resource.IsPlatform()) return false; + const auto& plat = std::get(resource.id); + const char* p = plat.compatible; + const char* end = plat.compatible + plat.compatible_len; + while (p < end) { + if (strcmp(p, key.compatible) == 0) return true; + p += strlen(p) + 1; + } + return false; + } else if constexpr (std::is_same_v) { + if (!resource.IsPci()) return false; + const auto& pci = std::get(resource.id); + return pci.vendor_id == key.vendor_id && + pci.device_id == key.device_id; + } else if constexpr (std::is_same_v) { + if (!resource.IsAcpi()) return false; + const auto& acpi = std::get(resource.id); + return strcmp(acpi.hid, key.hid) == 0; + } + return false; + }, + entry); + } + + static constexpr size_t kMaxDrivers = 32; + DriverEntry drivers_[kMaxDrivers]{}; + size_t count_{0}; + SpinLock lock_{"driver_registry"}; +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_DRIVER_REGISTRY_HPP_ */ diff --git a/src/device/include/pci_bus.hpp b/src/device/include/pci_bus.hpp new file mode 100644 index 000000000..3c3f9f996 --- /dev/null +++ b/src/device/include/pci_bus.hpp @@ -0,0 +1,57 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PCI_BUS_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PCI_BUS_HPP_ + +#include "bus.hpp" +#include "device_node.hpp" +#include "expected.hpp" + +/** + * @brief PCI 总线枚举 + * @todo 实现 PCI 总线枚举 + */ +class PciBus { + public: + /** + * @brief 构造函数 + * @param ecam_base ECAM 基地址(从 FDT 或 ACPI MCFG 获取) + */ + explicit PciBus(uint64_t ecam_base) : ecam_base_(ecam_base) {} + + /** + * @brief 获取总线名称 + * @return const char* 总线名称 + */ + static auto GetName() -> const char* { return "pci"; } + + /** + * @brief 设备枚举 + * @todo 实现 PCI 设备枚举 + * @param out 输出设备节点指针数组 + * @param max 最大枚举数量 + * @return Expected 成功时返回实际枚举数量,失败时返回错误 + */ + auto Enumerate([[maybe_unused]] DeviceNode* out, [[maybe_unused]] size_t max) + -> Expected { + // PCI ECAM 扫描尚未实现 + return static_cast(0); + } + + /// @name 构造/析构函数 + /// @{ + PciBus() = delete; + ~PciBus() = default; + PciBus(const PciBus&) = delete; + PciBus(PciBus&&) = delete; + auto operator=(const PciBus&) -> PciBus& = delete; + auto operator=(PciBus&&) -> PciBus& = delete; + /// @} + + private: + uint64_t ecam_base_; +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PCI_BUS_HPP_ */ diff --git a/src/device/include/platform_bus.hpp b/src/device/include/platform_bus.hpp new file mode 100644 index 000000000..93a3c2862 --- /dev/null +++ b/src/device/include/platform_bus.hpp @@ -0,0 +1,99 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PLATFORM_BUS_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PLATFORM_BUS_HPP_ + +#include "bus.hpp" +#include "device_node.hpp" +#include "expected.hpp" +#include "kernel_fdt.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" + +/// Platform 总线 — FDT 驱动的设备发现 +class PlatformBus { + public: + explicit PlatformBus(KernelFdt& fdt) : fdt_(fdt) {} + + static auto GetName() -> const char* { return "platform"; } + + /// 枚举 FDT 中所有设备节点 + auto Enumerate(DeviceNode* out, size_t max) -> Expected { + size_t count = 0; + + auto result = fdt_.ForEachNode( + [&out, &count, max](const char* node_name, const char* compatible_data, + size_t compatible_len, uint64_t mmio_base, + size_t mmio_size, uint32_t irq) -> bool { + if (count>= max) { + return false; + } + + if (compatible_data == nullptr || compatible_len == 0) { + return true; + } + + auto& node = out[count]; + + strncpy(node.name, node_name, sizeof(node.name) - 1); + node.name[sizeof(node.name) - 1] = '0円'; + + node.type = DeviceType::kPlatform; + + if (mmio_base != 0) { + node.resource.mmio[0] = {mmio_base, mmio_size}; + node.resource.mmio_count = 1; + } + + if (irq != 0) { + node.resource.irq[0] = irq; + node.resource.irq_count = 1; + } + + PlatformId plat{}; + if (compatible_len> sizeof(plat.compatible)) { + klog::Warn( + "PlatformBus: compatible data truncated from %zu to %zu bytes " + "for node '%s'\\n", + compatible_len, sizeof(plat.compatible), node_name); + } + size_t copy_len = compatible_len < sizeof(plat.compatible) + ? compatible_len + : sizeof(plat.compatible); + memcpy(plat.compatible, compatible_data, copy_len); + plat.compatible_len = copy_len; + node.resource.id = plat; + + klog::Debug( + "PlatformBus: found '%s' compatible='%s' " + "mmio=0x%lX size=0x%lX irq=%u\n", + node_name, compatible_data, mmio_base, mmio_size, irq); + + ++count; + return true; + }); + + if (!result.has_value()) { + return std::unexpected(result.error()); + } + + return count; + } + + /// @name 构造/析构函数 + /// @{ + PlatformBus() = delete; + ~PlatformBus() = default; + PlatformBus(const PlatformBus&) = delete; + PlatformBus(PlatformBus&&) = delete; + auto operator=(const PlatformBus&) -> PlatformBus& = delete; + auto operator=(PlatformBus&&) -> PlatformBus& = delete; + /// @} + + private: + KernelFdt& fdt_; +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PLATFORM_BUS_HPP_ */ diff --git a/src/device/include/platform_traits.hpp b/src/device/include/platform_traits.hpp new file mode 100644 index 000000000..febb315db --- /dev/null +++ b/src/device/include/platform_traits.hpp @@ -0,0 +1,27 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief 平台特性 — 满足 device_framework VirtioTraits 约束 + */ + +#ifndef SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PLATFORM_TRAITS_HPP_ +#define SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PLATFORM_TRAITS_HPP_ + +#include + +#include + +/// 平台特性(满足 EnvironmentTraits + BarrierTraits + DmaTraits) +struct PlatformTraits { + static auto Log(const char* /*fmt*/, ...) -> int { return 0; } + static auto Mb() -> void { cpu_io::Mb(); } + static auto Rmb() -> void { cpu_io::Rmb(); } + static auto Wmb() -> void { cpu_io::Wmb(); } + static auto VirtToPhys(void* p) -> uintptr_t { + return reinterpret_cast(p); + } + static auto PhysToVirt(uintptr_t a) -> void* { + return reinterpret_cast(a); + } +}; + +#endif /* SIMPLEKERNEL_SRC_DEVICE_INCLUDE_PLATFORM_TRAITS_HPP_ */ diff --git a/src/driver/CMakeLists.txt b/src/driver/CMakeLists.txt deleted file mode 100644 index d062a5640..000000000 --- a/src/driver/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright The SimpleKernel Contributors - -ADD_LIBRARY (driver INTERFACE) - -TARGET_INCLUDE_DIRECTORIES (driver - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) - -TARGET_SOURCES (driver INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/driver.cpp) - -TARGET_LINK_LIBRARIES ( - driver - INTERFACE $<$: - ns16550a - plic> - $<$: - pl011 - gic> - $<$: - apic - acpi>) - -IF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "riscv64") - ADD_SUBDIRECTORY (ns16550a) - ADD_SUBDIRECTORY (plic) -ELSEIF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64") - ADD_SUBDIRECTORY (pl011) - ADD_SUBDIRECTORY (gic) -ELSEIF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") - ADD_SUBDIRECTORY (apic) - ADD_SUBDIRECTORY (acpi) -ENDIF() diff --git a/src/driver/README.md b/src/driver/README.md deleted file mode 100644 index 4a90148ba..000000000 --- a/src/driver/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# driver - -driver 放置了所有的驱动程序 diff --git a/src/driver/acpi/CMakeLists.txt b/src/driver/acpi/CMakeLists.txt deleted file mode 100644 index a9fe481af..000000000 --- a/src/driver/acpi/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright The SimpleKernel Contributors - -ADD_LIBRARY (acpi INTERFACE) - -TARGET_INCLUDE_DIRECTORIES (acpi INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) - -TARGET_SOURCES (acpi INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/acpi.cpp) diff --git a/src/driver/acpi/README.md b/src/driver/acpi/README.md deleted file mode 100644 index 1c5099ec0..000000000 --- a/src/driver/acpi/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# acpi - -acpi 驱动 - -https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf diff --git a/src/driver/acpi/acpi.cpp b/src/driver/acpi/acpi.cpp deleted file mode 100644 index 8163fb17a..000000000 --- a/src/driver/acpi/acpi.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "acpi.h" - -#include "io.hpp" - -Acpi::Acpi(uint64_t rsdp) : rsdp_addr_(rsdp) {} diff --git a/src/driver/acpi/include/acpi.h b/src/driver/acpi/include/acpi.h deleted file mode 100644 index ba63f6d94..000000000 --- a/src/driver/acpi/include/acpi.h +++ /dev/null @@ -1,170 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#ifndef SIMPLEKERNEL_SRC_DRIVER_ACPI_INCLUDE_ACPI_H_ -#define SIMPLEKERNEL_SRC_DRIVER_ACPI_INCLUDE_ACPI_H_ - -#include - -/** - * @brief acpi 驱动 - * @see https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf - */ -class Acpi { - public: - /** - * 构造函数 - * @param rsdp rsdp 地址 - */ - explicit Acpi(uint64_t rsdp); - - /// @name 默认构造/析构函数 - /// @{ - Acpi() = default; - Acpi(const Acpi&) = delete; - Acpi(Acpi&&) = default; - auto operator=(const Acpi&) -> Acpi& = delete; - auto operator=(Acpi&&) -> Acpi& = default; - ~Acpi() = default; - /// @} - - private: - /** - * @brief Generic Address Structure - * @see ACPI_Spec_6_5_Aug29.pdf#5.2.3.2 - */ - struct GenericAddressStructure { - uint8_t address_space_id; - uint8_t register_bit_width; - uint8_t register_bit_offset; - uint8_t access_size; - uint64_t address; - } __attribute__((packed)); - - /** - * @brief Root System Description Pointer (RSDP) Structure - * @see ACPI_Spec_6_5_Aug29.pdf#5.2.5.3 - */ - struct Rsdp { - char signature[8]; - uint8_t checksum; - char oemid[6]; - uint8_t revision; - uint32_t rsdt_address; - uint32_t length; - uint64_t xsdt_address; - uint8_t extended_checksum; - uint8_t reserved[3]; - } __attribute__((packed)); - - /** - * @brief System Description Table Header - * @see ACPI_Spec_6_5_Aug29.pdf#5.2.6 - */ - struct Description_header { - char signature[4]; - uint32_t length; - uint8_t revision; - uint8_t checksum; - char oemid[6]; - char oem_table_id[8]; - uint32_t oem_revision; - uint32_t creator_id; - uint32_t creator_revision; - } __attribute__((packed)); - - /** - * @brief Root System Description Table (RSDT) - * @see ACPI_Spec_6_5_Aug29.pdf#5.2.7 - */ - struct Rsdt { - Description_header header; - uint32_t* entry; - } __attribute__((packed)); - - /** - * @brief Extended System Description Table (XSDT) - * @see ACPI_Spec_6_5_Aug29.pdf#5.2.8 - */ - struct Xsdt { - Description_header header; - uint64_t* entry; - } __attribute__((packed)); - - /** - * @brief Fixed ACPI Description Table (FADT) - * @see ACPI_Spec_6_5_Aug29.pdf#5.2.9 - */ - struct Fadt { - Description_header header; - uint32_t firmware_ctrl; - uint32_t dsdt; - uint8_t reserved; - uint8_t preferred_pm_profile; - uint16_t sci_int; - uint32_t smi_cmd; - uint8_t acpi_enable; - uint8_t acpi_disable; - uint8_t s4bios_req; - uint8_t pstate_cnt; - uint32_t pm1a_evt_blk; - uint32_t pm1b_evt_blk; - uint32_t pm1a_cnt_blk; - uint32_t pm1b_cnt_blk; - uint32_t pm2_cnt_blk; - uint32_t pm_tmr_blk; - uint32_t gpe0_blk; - uint32_t gpe1_blk; - uint8_t pm1_evt_len; - uint8_t pm1_cnt_len; - uint8_t pm2_cnt_len; - uint8_t pm_tmr_len; - uint8_t gpe0_blk_len; - uint8_t gpe1_blk_len; - uint8_t gpe1_base; - uint8_t cst_cnt; - uint16_t p_lvl2_lat; - uint16_t p_lvl3_lat; - uint16_t flush_size; - uint16_t flush_stride; - uint8_t duty_offset; - uint8_t duty_width; - uint8_t day_alrm; - uint8_t mon_alrm; - uint8_t century; - uint16_t iapc_boot_arch; - uint8_t reserved2; - uint32_t flags; - GenericAddressStructure reset_reg; - uint8_t reset_value; - uint16_t arm_boot_arch; - uint8_t fadt_minor_version; - uint64_t x_firmware_ctrl; - uint64_t x_dsdt; - GenericAddressStructure x_pm1a_evt_blk; - GenericAddressStructure x_pm1b_evt_blk; - GenericAddressStructure x_pm1a_cnt_blk; - GenericAddressStructure x_pm1b_cnt_blk; - GenericAddressStructure x_pm2_cnt_blk; - GenericAddressStructure x_pm_tmr_blk; - GenericAddressStructure x_gpe0_blk; - GenericAddressStructure x_gpe1_blk; - GenericAddressStructure sleep_control_reg; - GenericAddressStructure sleep_status_reg; - uint64_t hypervisor_vendor_id; - } __attribute__((packed)); - - /** - * @brief Differentiated System Description Table (DSDT) - * @see ACPI_Spec_6_5_Aug29.pdf#5.2.11.1 - */ - struct Dsdt { - Description_header header; - uint8_t* definition_block; - } __attribute__((packed)); - - uint64_t rsdp_addr_ = 0; -}; - -#endif /* SIMPLEKERNEL_SRC_DRIVER_ACPI_INCLUDE_ACPI_H_ */ diff --git a/src/driver/driver.cpp b/src/driver/driver.cpp deleted file mode 100644 index af50c24bb..000000000 --- a/src/driver/driver.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "driver.h" - -#include - -auto Driver(uint32_t argc, const uint8_t *argv) -> uint32_t { - (void)argc; - (void)argv; - - // 进入死循环 - while (true) { - ; - } - - return 0; -} diff --git a/src/driver/include/driver.h b/src/driver/include/driver.h deleted file mode 100644 index 30fe7b165..000000000 --- a/src/driver/include/driver.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#ifndef SIMPLEKERNEL_SRC_DRIVER_INCLUDE_DRIVER_H_ -#define SIMPLEKERNEL_SRC_DRIVER_INCLUDE_DRIVER_H_ - -#include - -/** - * @brief 入口 - * @param argc 参数个数 - * @param argv 参数列表 - * @return uint32_t 正常返回 0 - */ -auto Driver(uint32_t argc, const uint8_t *argv) -> uint32_t; - -#endif /* SIMPLEKERNEL_SRC_DRIVER_INCLUDE_DRIVER_H_ */ diff --git a/src/driver/ns16550a/CMakeLists.txt b/src/driver/ns16550a/CMakeLists.txt deleted file mode 100644 index 1f4c7b242..000000000 --- a/src/driver/ns16550a/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright The SimpleKernel Contributors - -ADD_LIBRARY (ns16550a INTERFACE) - -TARGET_INCLUDE_DIRECTORIES (ns16550a - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) - -TARGET_SOURCES (ns16550a INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/ns16550a.cpp) diff --git a/src/driver/ns16550a/README.md b/src/driver/ns16550a/README.md deleted file mode 100644 index 033484654..000000000 --- a/src/driver/ns16550a/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# NS16550A UART 驱动 - -NS16550A 是一款兼容 16550 的通用异步接收器发送器 (UART),广泛用于 RISC-V 架构的串口通信。 - -## 功能特性 - -- 支持 8 位数据位配置 -- 支持可配置波特率 -- 支持 FIFO 缓冲 -- 支持接收中断 -- 兼容标准 16550 寄存器布局 - -## 类接口 - -### 构造函数 -```cpp -Ns16550a(uint64_t dev_addr) -``` -- `dev_addr`: 设备基地址 - -### 主要方法 -```cpp -void PutChar(uint8_t c) const -``` -发送单个字符到串口 - -## 初始化配置 - -驱动在构造时自动进行以下初始化: -- 禁用中断 -- 设置波特率(使用除数锁存器) -- 配置为 8 位数据位 -- 启用 FIFO -- 启用接收中断 - -## 使用示例 - -```cpp -// 创建 NS16550A 实例 -Ns16550a uart(0x10000000); - -// 发送字符 -uart.PutChar('H'); -uart.PutChar('e'); -uart.PutChar('l'); -uart.PutChar('l'); -uart.PutChar('o'); -``` - -## 设备树配置示例 - -```dts -serial@10000000 { - interrupts = <0x0a>; - interrupt-parent = <0x03>; - clock-frequency = "08円@"; - reg = <0x00 0x10000000 0x00 0x100>; - compatible = "ns16550a"; -}; -``` - -## 寄存器映射 - -驱动实现了标准的 NS16550A 寄存器布局: - -| 偏移 | 读模式 | 写模式 | 描述 | -|------|--------|--------|------| -| 0x00 | RHR | THR | 接收/发送保持寄存器 | -| 0x01 | - | IER | 中断使能寄存器 | -| 0x02 | ISR | FCR | 中断状态/FIFO控制寄存器 | -| 0x03 | - | LCR | 线控制寄存器 | -| 0x04 | - | MCR | 调制解调器控制寄存器 | -| 0x05 | LSR | - | 线状态寄存器 | -| 0x06 | MSR | - | 调制解调器状态寄存器 | - -### 除数锁存器(当 LCR.DLAB=1 时) -| 偏移 | 寄存器 | 描述 | -|------|--------|------| -| 0x00 | DLL | 除数锁存器低字节 | -| 0x01 | DLM | 除数锁存器高字节 | - -## 注意事项 - -- 在 RISC-V 架构中,该驱动目前主要用于直接硬件访问 -- 当前实现中 RISC-V 架构使用 OpenSBI 进行控制台输出 -- 驱动支持轮询模式的字符发送 -- 发送前会检查发送缓冲区状态,确保可以安全发送 - -## 文件结构 - -- `include/ns16550a.h` - 头文件,包含类定义和寄存器常量 -- `ns16550a.cpp` - 实现文件,包含初始化和字符输出功能 -- `CMakeLists.txt` - 构建配置 diff --git a/src/driver/ns16550a/include/ns16550a.h b/src/driver/ns16550a/include/ns16550a.h deleted file mode 100644 index f67522046..000000000 --- a/src/driver/ns16550a/include/ns16550a.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#ifndef SIMPLEKERNEL_SRC_DRIVER_NS16550A_INCLUDE_NS16550A_H_ -#define SIMPLEKERNEL_SRC_DRIVER_NS16550A_INCLUDE_NS16550A_H_ - -#include - -class Ns16550a { - public: - /** - * 构造函数 - * @param dev_addr 设备地址 - */ - explicit Ns16550a(uint64_t dev_addr); - - /// @name 默认构造/析构函数 - /// @{ - Ns16550a() = default; - Ns16550a(const Ns16550a&) = delete; - Ns16550a(Ns16550a&&) = default; - auto operator=(const Ns16550a&) -> Ns16550a& = delete; - auto operator=(Ns16550a&&) -> Ns16550a& = default; - ~Ns16550a() = default; - /// @} - - void PutChar(uint8_t c) const; - - /** - * 阻塞式读取一个字符 - * @return 读取到的字符 - */ - [[nodiscard]] auto GetChar() const -> uint8_t; - - /** - * 非阻塞式尝试读取一个字符 - * @return 读取到的字符,如果没有数据则返回 -1 - */ - [[nodiscard]] auto TryGetChar() const -> uint8_t; - - private: - /// read mode: Receive holding reg - static constexpr uint8_t kRegRHR = 0; - /// write mode: Transmit Holding Reg - static constexpr uint8_t kRegTHR = 0; - /// write mode: interrupt enable reg - static constexpr uint8_t kRegIER = 1; - /// write mode: FIFO control Reg - static constexpr uint8_t kRegFCR = 2; - /// read mode: Interrupt Status Reg - static constexpr uint8_t kRegISR = 2; - /// write mode:Line Control Reg - static constexpr uint8_t kRegLCR = 3; - /// write mode:Modem Control Reg - static constexpr uint8_t kRegMCR = 4; - /// read mode: Line Status Reg - static constexpr uint8_t kRegLSR = 5; - /// read mode: Modem Status Reg - static constexpr uint8_t kRegMSR = 6; - - /// LSB of divisor Latch when enabled - static constexpr uint8_t kUartDLL = 0; - /// MSB of divisor Latch when enabled - static constexpr uint8_t kUartDLM = 1; - - uint64_t base_addr_; -}; - -#endif /* SIMPLEKERNEL_SRC_DRIVER_NS16550A_INCLUDE_NS16550A_H_ */ diff --git a/src/driver/ns16550a/ns16550a.cpp b/src/driver/ns16550a/ns16550a.cpp deleted file mode 100644 index c913e8d3e..000000000 --- a/src/driver/ns16550a/ns16550a.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "ns16550a.h" - -#include - -#include "io.hpp" - -Ns16550a::Ns16550a(uint64_t dev_addr) : base_addr_(dev_addr) { - // disable interrupt - io::Out(base_addr_ + kRegIER, 0x00); - // set baud rate - io::Out(base_addr_ + kRegLCR, 0x80); - io::Out(base_addr_ + kUartDLL, 0x03); - io::Out(base_addr_ + kUartDLM, 0x00); - // set word length to 8-bits - io::Out(base_addr_ + kRegLCR, 0x03); - // enable FIFOs - io::Out(base_addr_ + kRegFCR, 0x07); - // enable receiver interrupts - io::Out(base_addr_ + kRegIER, 0x01); -} - -void Ns16550a::PutChar(uint8_t c) const { - while ((io::In(base_addr_ + kRegLSR) & (1 << 5)) == 0) { - cpu_io::Pause(); - } - io::Out(base_addr_ + kRegTHR, c); -} - -auto Ns16550a::GetChar() const -> uint8_t { - // 等待直到接收缓冲区有数据 (LSR bit 0 = 1) - while ((io::In(base_addr_ + kRegLSR) & (1 << 0)) == 0) { - cpu_io::Pause(); - } - return io::In(base_addr_ + kRegRHR); -} - -auto Ns16550a::TryGetChar() const -> uint8_t { - // 检查接收缓冲区是否有数据 (LSR bit 0 = 1) - if ((io::In(base_addr_ + kRegLSR) & (1 << 0)) != 0) { - return io::In(base_addr_ + kRegRHR); - } - return -1; -} diff --git a/src/driver/pl011/CMakeLists.txt b/src/driver/pl011/CMakeLists.txt deleted file mode 100644 index c1f9723d4..000000000 --- a/src/driver/pl011/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright The SimpleKernel Contributors - -ADD_LIBRARY (pl011 INTERFACE) - -TARGET_INCLUDE_DIRECTORIES (pl011 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) - -TARGET_SOURCES (pl011 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/pl011.cpp) diff --git a/src/driver/pl011/README.md b/src/driver/pl011/README.md deleted file mode 100644 index b2dd8dcf6..000000000 --- a/src/driver/pl011/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# PL011 UART 驱动 - -PL011 是 ARM 设计的通用异步接收器发送器 (UART),广泛用于 aarch64 架构的串口通信。 - -## 功能特性 - -- 支持 8 位数据位,1 个停止位,无奇偶校验 -- 支持可配置波特率 -- 支持接收中断 -- 支持 FIFO 缓冲 -- 完整的寄存器级别控制 - -## 类接口 - -### 构造函数 -```cpp -Pl011(uint64_t dev_addr, uint64_t clock = 0, uint64_t baud_rate = 0) -``` -- `dev_addr`: 设备基地址 -- `clock`: 串口时钟频率(可选) -- `baud_rate`: 波特率(可选) - -### 主要方法 -```cpp -void PutChar(uint8_t c) -``` -发送单个字符到串口 - -## 使用示例 - -```cpp -// 在 aarch64 架构中的使用 -auto [serial_base, serial_size, irq] = - Singleton::GetInstance().GetSerial(); - -Singleton::GetInstance() = Pl011(serial_base); -pl011 = &Singleton::GetInstance(); - -// 发送字符 -pl011->PutChar('H'); -``` - -## 设备树配置示例 - -```dts -pl011@9000000 { - clock-names = "uartclk0円apb_pclk"; - clocks = <0x8000 0x8000>; - interrupts = <0x00 0x01 0x04>; - reg = <0x00 0x9000000 0x00 0x1000>; - compatible = "arm,pl0110円arm,primecell"; -}; -``` - -## 寄存器映射 - -驱动实现了完整的 PL011 寄存器映射,包括: -- 数据寄存器 (DR) -- 标志寄存器 (FR) -- 线控制寄存器 (LCRH) -- 控制寄存器 (CR) -- 波特率寄存器 (IBRD/FBRD) -- 中断相关寄存器等 - -## 参考文档 - -- [ARM PL011 技术参考手册](https://developer.arm.com/documentation/ddi0183/g/) - -## 文件结构 - -- `include/pl011.h` - 头文件,包含类定义和寄存器常量 -- `pl011.cpp` - 实现文件,包含构造函数和字符输出功能 -- `CMakeLists.txt` - 构建配置 diff --git a/src/driver/pl011/include/pl011.h b/src/driver/pl011/include/pl011.h deleted file mode 100644 index 45d515382..000000000 --- a/src/driver/pl011/include/pl011.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#ifndef SIMPLEKERNEL_SRC_DRIVER_PL011_INCLUDE_PL011_H_ -#define SIMPLEKERNEL_SRC_DRIVER_PL011_INCLUDE_PL011_H_ - -#include - -/** - * @brief PL011 串口驱动 - * @see https://developer.arm.com/documentation/ddi0183/g/ - */ -class Pl011 { - public: - /** - * 构造函数 - * @param dev_addr 设备地址 - * @param uart_clk 串口时钟 - * @param baud_rate 波特率 - */ - explicit Pl011(uint64_t dev_addr, uint64_t clock = 0, uint64_t baud_rate = 0); - - /// @name 默认构造/析构函数 - /// @{ - Pl011() = default; - Pl011(const Pl011&) = delete; - Pl011(Pl011&&) = default; - auto operator=(const Pl011&) -> Pl011& = delete; - auto operator=(Pl011&&) -> Pl011& = default; - ~Pl011() = default; - /// @} - - void PutChar(uint8_t c) const; - - /** - * 阻塞式读取一个字符 - * @return 读取到的字符 - */ - [[nodiscard]] auto GetChar() const -> uint8_t; - - /** - * 非阻塞式尝试读取一个字符 - * @return 读取到的字符,如果没有数据则返回 -1 - */ - [[nodiscard]] auto TryGetChar() const -> uint8_t; - - private: - /// data register - static constexpr uint32_t kRegDR = 0x00; - /// receive status or error clear - static constexpr uint32_t kRegRSRECR = 0x04; - /// DMA watermark configure - static constexpr uint32_t kRegDmaWm = 0x08; - /// Timeout period - static constexpr uint32_t kRegTimeOut = 0x0C; - /// flag register - static constexpr uint32_t kRegFR = 0x18; - /// IrDA low-poer - static constexpr uint32_t kRegILPR = 0x20; - /// integer baud register - static constexpr uint32_t kRegIBRD = 0x24; - /// fractional baud register - static constexpr uint32_t kRegFBRD = 0x28; - /// line control register - static constexpr uint32_t kRegLCRH = 0x2C; - /// control register - static constexpr uint32_t kRegCR = 0x30; - /// interrupt FIFO level select - static constexpr uint32_t kRegIFLS = 0x34; - /// interrupt mask set/clear - static constexpr uint32_t kRegIMSC = 0x38; - /// raw interrupt register - static constexpr uint32_t kRegRIS = 0x3C; - /// masked interrupt register - static constexpr uint32_t kRegMIS = 0x40; - /// interrupt clear register - static constexpr uint32_t kRegICR = 0x44; - /// DMA control register - static constexpr uint32_t kRegDmaCR = 0x48; - - /// flag register bits - static constexpr uint32_t kFRRTXDIS = (1 << 13); - static constexpr uint32_t kFRTERI = (1 << 12); - static constexpr uint32_t kFRDDCD = (1 << 11); - static constexpr uint32_t kFRDDSR = (1 << 10); - static constexpr uint32_t kFRDCTS = (1 << 9); - static constexpr uint32_t kFRRI = (1 << 8); - static constexpr uint32_t kFRTXFE = (1 << 7); - static constexpr uint32_t kFRRXFF = (1 << 6); - static constexpr uint32_t kFRTxFIFO = (1 << 5); - static constexpr uint32_t kFRRXFE = (1 << 4); - static constexpr uint32_t kFRBUSY = (1 << 3); - static constexpr uint32_t kFRDCD = (1 << 2); - static constexpr uint32_t kFRDSR = (1 << 1); - static constexpr uint32_t kFRCTS = (1 << 0); - - /// transmit/receive line register bits - static constexpr uint32_t kLCRHSPS = (1 << 7); - static constexpr uint32_t kLCRHWlen8 = (3 << 5); - static constexpr uint32_t kLCRHWLEN_7 = (2 << 5); - static constexpr uint32_t kLCRHWLEN_6 = (1 << 5); - static constexpr uint32_t kLCRHWLEN_5 = (0 << 5); - static constexpr uint32_t kLCRHFEN = (1 << 4); - static constexpr uint32_t kLCRHSTP2 = (1 << 3); - static constexpr uint32_t kLCRHEPS = (1 << 2); - static constexpr uint32_t kLCRHPEN = (1 << 1); - static constexpr uint32_t kLCRHBRK = (1 << 0); - - /// control register bits - static constexpr uint32_t kCRCTSEN = (1 << 15); - static constexpr uint32_t kCRRTSEN = (1 << 14); - static constexpr uint32_t kCROUT2 = (1 << 13); - static constexpr uint32_t kCROUT1 = (1 << 12); - static constexpr uint32_t kCRRTS = (1 << 11); - static constexpr uint32_t kCRDTR = (1 << 10); - static constexpr uint32_t kCRRxEnable = (1 << 9); - static constexpr uint32_t kCRTxEnable = (1 << 8); - static constexpr uint32_t kCRLPE = (1 << 7); - static constexpr uint32_t kCROVSFACT = (1 << 3); - static constexpr uint32_t kCREnable = (1 << 0); - - static constexpr uint32_t kIMSCRTIM = (1 << 6); - static constexpr uint32_t kIMSCRxim = (1 << 4); - - uint64_t base_addr_ = 0; - uint64_t base_clock_ = 0; - uint64_t baud_rate_ = 0; -}; - -#endif /* SIMPLEKERNEL_SRC_DRIVER_PL011_INCLUDE_PL011_H_ */ diff --git a/src/driver/pl011/pl011.cpp b/src/driver/pl011/pl011.cpp deleted file mode 100644 index b45f188ff..000000000 --- a/src/driver/pl011/pl011.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "pl011.h" - -#include - -#include "io.hpp" - -Pl011::Pl011(uint64_t dev_addr, uint64_t clock, uint64_t baud_rate) - : base_addr_(dev_addr), base_clock_(clock), baud_rate_(baud_rate) { - // Clear all errors - io::Out(base_addr_ + kRegRSRECR, 0); - // Disable everything - io::Out(base_addr_ + kRegCR, 0); - - if (baud_rate_ != 0) { - uint32_t divisor = (base_clock_ * 4) / baud_rate_; - - io::Out(base_addr_ + kRegIBRD, divisor>> 6); - io::Out(base_addr_ + kRegFBRD, divisor & 0x3f); - } - - // Configure TX to 8 bits, 1 stop bit, no parity, fifo disabled. - io::Out(base_addr_ + kRegLCRH, kLCRHWlen8); - - // Enable receive interrupt - io::Out(base_addr_ + kRegIMSC, kIMSCRxim); - - // Enable UART and RX/TX - io::Out(base_addr_ + kRegCR, kCREnable | kCRTxEnable | kCRRxEnable); -} - -void Pl011::PutChar(uint8_t c) const { - // Wait until there is space in the FIFO or device is disabled - while (io::In(base_addr_ + kRegFR) & kFRTxFIFO) { - cpu_io::Pause(); - } - - io::Out(base_addr_ + kRegDR, c); -} - -auto Pl011::GetChar() const -> uint8_t { - // Wait until there is data in the FIFO or device is disabled - while (io::In(base_addr_ + kRegFR) & kFRRXFE) { - cpu_io::Pause(); - } - - return io::In(base_addr_ + kRegDR); -} - -auto Pl011::TryGetChar() const -> uint8_t { - // Wait until there is data in the FIFO or device is disabled - if (io::In(base_addr_ + kRegFR) & kFRRXFE) { - return -1; - } - return io::In(base_addr_ + kRegDR); -} diff --git a/src/filesystem/CMakeLists.txt b/src/filesystem/CMakeLists.txt new file mode 100644 index 000000000..fa5ec99cf --- /dev/null +++ b/src/filesystem/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright The SimpleKernel Contributors + +ADD_LIBRARY (filesystem INTERFACE) + +TARGET_INCLUDE_DIRECTORIES (filesystem INTERFACE include) + +TARGET_SOURCES (filesystem INTERFACE file_descriptor.cpp) + +ADD_SUBDIRECTORY (vfs) +ADD_SUBDIRECTORY (ramfs) + +TARGET_LINK_LIBRARIES (filesystem INTERFACE vfs ramfs) diff --git a/src/filesystem/file_descriptor.cpp b/src/filesystem/file_descriptor.cpp new file mode 100644 index 000000000..410404771 --- /dev/null +++ b/src/filesystem/file_descriptor.cpp @@ -0,0 +1,173 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "file_descriptor.hpp" + +#include "kernel_log.hpp" + +namespace filesystem { + +FileDescriptorTable::FileDescriptorTable() + : table_{}, open_count_(0), lock_{"fd_table"} {} + +FileDescriptorTable::~FileDescriptorTable() { + CloseAll().or_else([](auto&& err) { + klog::Warn("Failed to close all files in destructor: %s\n", err.message()); + return Expected{}; + }); +} + +FileDescriptorTable::FileDescriptorTable(FileDescriptorTable&& other) + : open_count_(other.open_count_), lock_{"fd_table"} { + LockGuard guard(other.lock_); + + for (int i = 0; i < kMaxFd; ++i) { + table_[i] = other.table_[i]; + other.table_[i] = nullptr; + } + + other.open_count_ = 0; +} + +auto FileDescriptorTable::operator=(FileDescriptorTable&& other) + -> FileDescriptorTable& { + if (this != &other) { + // 先关闭当前的所有文件 + CloseAll().or_else([](auto&& err) { + klog::Warn("Failed to close all files in move assignment: %s\n", + err.message()); + return Expected{}; + }); + + LockGuard guard1(lock_); + LockGuard guard2(other.lock_); + + for (int i = 0; i < kMaxFd; ++i) { + table_[i] = other.table_[i]; + other.table_[i] = nullptr; + } + + open_count_ = other.open_count_; + other.open_count_ = 0; + } + + return *this; +} + +auto FileDescriptorTable::Alloc(vfs::File* file) -> Expected { + if (file == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + LockGuard guard(lock_); + + // 从 3 开始查找(0/1/2 预留给标准流) + for (int fd = 3; fd < kMaxFd; ++fd) { + if (table_[fd] == nullptr) { + table_[fd] = file; + ++open_count_; + return fd; + } + } + + return std::unexpected(Error(ErrorCode::kFsFdTableFull)); +} + +auto FileDescriptorTable::Get(int fd) -> vfs::File* { + if (fd < 0 || fd>= kMaxFd) { + return nullptr; + } + + LockGuard guard(lock_); + return table_[fd]; +} + +auto FileDescriptorTable::Free(int fd) -> Expected { + if (fd < 0 || fd>= kMaxFd) { + return std::unexpected(Error(ErrorCode::kFsInvalidFd)); + } + + LockGuard guard(lock_); + + if (table_[fd] == nullptr) { + return std::unexpected(Error(ErrorCode::kFsInvalidFd)); + } + + table_[fd] = nullptr; + --open_count_; + + return {}; +} + +auto FileDescriptorTable::Dup(int old_fd, int new_fd) -> Expected { + if (old_fd < 0 || old_fd>= kMaxFd) { + return std::unexpected(Error(ErrorCode::kFsInvalidFd)); + } + + LockGuard guard(lock_); + + vfs::File* file = table_[old_fd]; + if (file == nullptr) { + return std::unexpected(Error(ErrorCode::kFsInvalidFd)); + } + + if (new_fd>= 0 && new_fd < kMaxFd) { + // 关闭目标 fd 如果已打开 + if (table_[new_fd] != nullptr) { + table_[new_fd] = nullptr; + --open_count_; + } + + table_[new_fd] = file; + ++open_count_; + return new_fd; + } + + // 分配新的 fd + if (new_fd == -1) { + for (int fd = 0; fd < kMaxFd; ++fd) { + if (table_[fd] == nullptr) { + table_[fd] = file; + ++open_count_; + return fd; + } + } + } + + return std::unexpected(Error(ErrorCode::kFsFdTableFull)); +} + +auto FileDescriptorTable::CloseAll() -> Expected { + LockGuard guard(lock_); + + for (int fd = 0; fd < kMaxFd; ++fd) { + if (table_[fd] != nullptr) { + // 注意:这里不调用 VFS Close,因为 File 对象可能在其他地方共享 + // 实际关闭操作由调用者负责 + table_[fd] = nullptr; + } + } + + open_count_ = 0; + return {}; +} + +auto FileDescriptorTable::SetupStandardFiles(vfs::File* stdin_file, + vfs::File* stdout_file, + vfs::File* stderr_file) + -> Expected { + LockGuard guard(lock_); + + table_[kStdinFd] = stdin_file; + table_[kStdoutFd] = stdout_file; + table_[kStderrFd] = stderr_file; + + open_count_ += 3; + + return {}; +} + +auto FileDescriptorTable::GetOpenCount() const -> int { return open_count_; } + +} // namespace filesystem diff --git a/src/filesystem/include/file_descriptor.hpp b/src/filesystem/include/file_descriptor.hpp new file mode 100644 index 000000000..21f1597fc --- /dev/null +++ b/src/filesystem/include/file_descriptor.hpp @@ -0,0 +1,110 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief 文件描述符表 + */ + +#ifndef SIMPLEKERNEL_SRC_FILESYSTEM_INCLUDE_FILE_DESCRIPTOR_HPP_ +#define SIMPLEKERNEL_SRC_FILESYSTEM_INCLUDE_FILE_DESCRIPTOR_HPP_ + +#include +#include +#include + +#include "expected.hpp" +#include "spinlock.hpp" +#include "vfs.hpp" + +namespace filesystem { + +/** + * @brief 进程级文件描述符表 + * @details 每个进程(TaskControlBlock)持有一个 FdTable, + * 将整数 fd 映射到 File 对象。 + * fd 0/1/2 预留给 stdin/stdout/stderr。 + */ +class FileDescriptorTable { + public: + /// 最大文件描述符数 + static constexpr int kMaxFd = 64; + + /// 标准文件描述符 + static constexpr int kStdinFd = 0; + static constexpr int kStdoutFd = 1; + static constexpr int kStderrFd = 2; + + /// @brief 构造函数 + FileDescriptorTable(); + + /// @brief 析构函数 + ~FileDescriptorTable(); + + // 禁止拷贝,允许移动 + FileDescriptorTable(const FileDescriptorTable&) = delete; + FileDescriptorTable(FileDescriptorTable&& other); + auto operator=(const FileDescriptorTable&) -> FileDescriptorTable& = delete; + auto operator=(FileDescriptorTable&& other) -> FileDescriptorTable&; + + /** + * @brief 分配一个最小可用 fd 并关联 File + * @param file 要关联的 File 对象 + * @return Expected 分配到的 fd + * @post 返回的 fd>= 0 且 fd < kMaxFd + */ + [[nodiscard]] auto Alloc(vfs::File* file) -> Expected; + + /** + * @brief 获取 fd 对应的 File 对象 + * @param fd 文件描述符 + * @return vfs::File* 指针,无效 fd 返回 nullptr + * @pre 0 <= fd < kMaxFd + */ + auto Get(int fd) -> vfs::File*; + + /** + * @brief 释放 fd + * @param fd 要释放的文件描述符 + * @return Expected + */ + + [[nodiscard]] auto Free(int fd) -> Expected; + /** + * @brief 复制文件描述符(用于 dup/dup2) + * @param old_fd 原文件描述符 + * @param new_fd 目标文件描述符(若为 -1 则分配最小可用) + * @return Expected 新的文件描述符 + */ + + [[nodiscard]] auto Dup(int old_fd, int new_fd = -1) -> Expected; + /** + * @brief 关闭所有文件描述符 + * @return Expected 成功或错误 + */ + + [[nodiscard]] auto CloseAll() -> Expected; + /** + * @brief 设置标准文件描述符 + * @param stdin_file stdin 文件对象 + * @param stdout_file stdout 文件对象 + * @param stderr_file stderr 文件对象 + * @return Expected 成功或错误 + */ + + [[nodiscard]] auto SetupStandardFiles(vfs::File* stdin_file, + vfs::File* stdout_file, + vfs::File* stderr_file) + -> Expected; + /** + * @brief 获取已打开文件描述符数量 + * @return int 已打开 fd 数量 + */ + [[nodiscard]] auto GetOpenCount() const -> int; + + private: + std::array table_; + int open_count_; + SpinLock lock_{"fd_table"}; +}; + +} // namespace filesystem + +#endif /* SIMPLEKERNEL_SRC_FILESYSTEM_INCLUDE_FILE_DESCRIPTOR_HPP_ */ diff --git a/src/filesystem/ramfs/CMakeLists.txt b/src/filesystem/ramfs/CMakeLists.txt new file mode 100644 index 000000000..b31afedf7 --- /dev/null +++ b/src/filesystem/ramfs/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright The SimpleKernel Contributors + +ADD_LIBRARY (ramfs INTERFACE) + +TARGET_INCLUDE_DIRECTORIES (ramfs INTERFACE include) + +TARGET_SOURCES (ramfs INTERFACE ramfs.cpp) diff --git a/src/filesystem/ramfs/include/ramfs.hpp b/src/filesystem/ramfs/include/ramfs.hpp new file mode 100644 index 000000000..7d2da076a --- /dev/null +++ b/src/filesystem/ramfs/include/ramfs.hpp @@ -0,0 +1,175 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief ramfs - 内存文件系统 + */ + +#ifndef SIMPLEKERNEL_SRC_FILESYSTEM_RAMFS_INCLUDE_RAMFS_HPP_ +#define SIMPLEKERNEL_SRC_FILESYSTEM_RAMFS_INCLUDE_RAMFS_HPP_ + +#include "filesystem.hpp" +#include "vfs.hpp" + +namespace ramfs { + +/** + * @brief ramfs 文件系统实现 + * @details 纯内存文件系统,所有数据存储在内存中,适合用作 rootfs + */ +class RamFs : public vfs::FileSystem { + public: + /** + * @brief 构造函数 + */ + RamFs(); + + /** + * @brief 析构函数 + */ + ~RamFs() override; + + // 禁止拷贝和移动 + RamFs(const RamFs&) = delete; + RamFs(RamFs&&) = delete; + auto operator=(const RamFs&) -> RamFs& = delete; + auto operator=(RamFs&&) -> RamFs& = delete; + + /** + * @brief 获取文件系统类型名 + * @return "ramfs" + */ + [[nodiscard]] auto GetName() const -> const char* override; + + /** + * @brief 挂载 ramfs + * @param device 必须为 nullptr(ramfs 不需要块设备) + * @return Expected 根目录 inode + * @post 返回的 inode->type == vfs::FileType::kDirectory + */ + auto Mount(vfs::BlockDevice* device) -> Expected override; + + /** + * @brief 卸载 ramfs + * @return Expected 成功或错误 + * @pre 没有打开的文件引用此文件系统 + */ + auto Unmount() -> Expected override; + + /** + * @brief 同步数据到磁盘(ramfs 无操作) + * @return Expected 成功 + */ + auto Sync() -> Expected override; + + /** + * @brief 分配新 inode + * @return Expected 新分配的 inode 或错误 + */ + auto AllocateInode() -> Expected override; + + /** + * @brief 释放 inode + * @param inode 要释放的 inode + * @return Expected 成功或错误 + */ + auto FreeInode(vfs::Inode* inode) -> Expected override; + + /** + * @brief 获取根 inode + * @return vfs::Inode* 根目录 inode + */ + [[nodiscard]] auto GetRootInode() const -> vfs::Inode*; + + /** + * @brief 获取文件操作实例 + * @return vfs::FileOps* 文件操作实例指针 + */ + auto GetFileOps() -> vfs::FileOps*; + + // Inode 操作实现类 + class RamFsInodeOps : public vfs::InodeOps { + public: + explicit RamFsInodeOps(RamFs* fs) : fs_(fs) {} + auto Lookup(vfs::Inode* dir, const char* name) + -> Expected override; + auto Create(vfs::Inode* dir, const char* name, vfs::FileType type) + -> Expected override; + auto Unlink(vfs::Inode* dir, const char* name) -> Expected override; + auto Mkdir(vfs::Inode* dir, const char* name) + -> Expected override; + auto Rmdir(vfs::Inode* dir, const char* name) -> Expected override; + + private: + RamFs* fs_; + }; + + // File 操作实现类 + class RamFsFileOps : public vfs::FileOps { + public: + explicit RamFsFileOps(RamFs* fs) : fs_(fs) {} + auto Read(vfs::File* file, void* buf, size_t count) + -> Expected override; + auto Write(vfs::File* file, const void* buf, size_t count) + -> Expected override; + auto Seek(vfs::File* file, int64_t offset, vfs::SeekWhence whence) + -> Expected override; + auto Close(vfs::File* file) -> Expected override; + auto ReadDir(vfs::File* file, vfs::DirEntry* dirent, size_t count) + -> Expected override; + + private: + RamFs* fs_; + }; + + friend class RamFsInodeOps; + friend class RamFsFileOps; + + private: + /// @brief ramfs 内部 inode 数据 + struct RamInode { + vfs::Inode inode; + /// 文件数据(普通文件)或子项列表(目录) + void* data; + /// 数据缓冲区容量 + size_t capacity; + /// 子项数量(仅目录) + size_t child_count; + /// 空闲链表指针 + RamInode* next_free; + }; + + /// @brief 目录项结构(存储在目录的 data 中) + struct RamDirEntry { + char name[256]; + vfs::Inode* inode; + }; + + static constexpr size_t kMaxInodes = 1024; + /// 初始文件容量 + static constexpr size_t kInitialCapacity = 256; + + RamInode inodes_[kMaxInodes]; + /// 空闲 inode 链表头 + RamInode* free_list_; + /// 根目录 inode + vfs::Inode* root_inode_; + /// 已使用的 inode 数量 + size_t used_inodes_; + /// 是否已挂载 + bool mounted_; + + // 操作实例 + RamFsInodeOps inode_ops_; + RamFsFileOps file_ops_; + + // 辅助函数 + auto FindInDirectory(RamInode* dir, const char* name) -> RamDirEntry*; + auto AddToDirectory(RamInode* dir, const char* name, vfs::Inode* inode) + -> Expected; + auto RemoveFromDirectory(RamInode* dir, const char* name) -> Expected; + auto IsDirectoryEmpty(RamInode* dir) -> bool; + auto ExpandFile(RamInode* inode, size_t new_size) -> Expected; +}; + +} // namespace ramfs + +#endif // SIMPLEKERNEL_SRC_FILESYSTEM_RAMFS_INCLUDE_RAMFS_HPP_ diff --git a/src/filesystem/ramfs/ramfs.cpp b/src/filesystem/ramfs/ramfs.cpp new file mode 100644 index 000000000..125ed9e1a --- /dev/null +++ b/src/filesystem/ramfs/ramfs.cpp @@ -0,0 +1,615 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "ramfs.hpp" + +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs.hpp" + +namespace ramfs { + +using namespace vfs; + +RamFs::RamFs() + : inodes_{}, + free_list_(nullptr), + root_inode_(nullptr), + used_inodes_(0), + mounted_(false), + inode_ops_(this), + file_ops_(this) {} + +RamFs::~RamFs() { + if (mounted_) { + Unmount(); + } +} + +auto RamFs::GetName() const -> const char* { return "ramfs"; } + +auto RamFs::Mount(BlockDevice* device) -> Expected { + if (mounted_) { + return std::unexpected(Error(ErrorCode::kFsAlreadyMounted)); + } + + if (device != nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + klog::Info("RamFs: mounting...\n"); + + // 初始化 inode 空闲链表 + free_list_ = nullptr; + for (int i = kMaxInodes - 1; i>= 0; --i) { + inodes_[i].next_free = free_list_; + free_list_ = &inodes_[i]; + } + + // 分配根目录 inode + auto root_result = AllocateInode(); + if (!root_result.has_value()) { + return std::unexpected(root_result.error()); + } + + root_inode_ = root_result.value(); + root_inode_->type = FileType::kDirectory; + root_inode_->permissions = 0755; + + // 初始化根目录数据 + RamInode* ram_root = reinterpret_cast( + reinterpret_cast(root_inode_) - offsetof(RamInode, inode)); + ram_root->data = nullptr; + ram_root->capacity = 0; + ram_root->child_count = 0; + + used_inodes_ = 1; + mounted_ = true; + + klog::Info("RamFs: mounted successfully\n"); + return root_inode_; +} + +auto RamFs::Unmount() -> Expected { + if (!mounted_) { + return std::unexpected(Error(ErrorCode::kFsNotMounted)); + } + + klog::Info("RamFs: unmounting...\n"); + + // 释放所有 inode 的数据缓冲区 + for (size_t i = 0; i < kMaxInodes; ++i) { + if (inodes_[i].inode.type != FileType::kUnknown && + inodes_[i].data != nullptr) { + delete[] static_cast(inodes_[i].data); + inodes_[i].data = nullptr; + } + } + + // 重置状态 + free_list_ = nullptr; + root_inode_ = nullptr; + used_inodes_ = 0; + mounted_ = false; + + klog::Info("RamFs: unmounted\n"); + return {}; +} + +auto RamFs::Sync() -> Expected { + // ramfs 是纯内存文件系统,无需同步 + return {}; +} + +auto RamFs::AllocateInode() -> Expected { + if (free_list_ == nullptr) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + RamInode* ram_inode = free_list_; + free_list_ = free_list_->next_free; + + // 初始化 inode + ram_inode->inode.ino = reinterpret_cast(&ram_inode->inode); + ram_inode->inode.type = FileType::kUnknown; + ram_inode->inode.size = 0; + ram_inode->inode.permissions = 0644; + ram_inode->inode.link_count = 1; + ram_inode->inode.fs_private = ram_inode; + ram_inode->inode.fs = this; + ram_inode->inode.ops = &inode_ops_; + + ram_inode->data = nullptr; + ram_inode->capacity = 0; + ram_inode->child_count = 0; + ram_inode->next_free = nullptr; + + ++used_inodes_; + return &ram_inode->inode; +} + +auto RamFs::FreeInode(Inode* inode) -> Expected { + if (inode == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + RamInode* ram_inode = reinterpret_cast( + reinterpret_cast(inode) - offsetof(RamInode, inode)); + + // 释放数据缓冲区 + if (ram_inode->data != nullptr) { + delete[] static_cast(ram_inode->data); + ram_inode->data = nullptr; + } + + // 重置 inode + ram_inode->inode.type = FileType::kUnknown; + ram_inode->inode.size = 0; + ram_inode->inode.fs_private = nullptr; + ram_inode->inode.ops = nullptr; + + ram_inode->capacity = 0; + ram_inode->child_count = 0; + + // 加入空闲链表 + ram_inode->next_free = free_list_; + free_list_ = ram_inode; + + --used_inodes_; + return {}; +} + +auto RamFs::GetRootInode() const -> Inode* { return root_inode_; } + +auto RamFs::GetFileOps() -> FileOps* { return &file_ops_; } + +// InodeOps 实现 + +auto RamFs::RamFsInodeOps::Lookup(Inode* dir, const char* name) + -> Expected { + if (dir == nullptr || name == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + RamInode* ram_dir = reinterpret_cast( + reinterpret_cast(dir) - offsetof(RamInode, inode)); + + RamDirEntry* entry = fs_->FindInDirectory(ram_dir, name); + + if (entry == nullptr) { + return std::unexpected(Error(ErrorCode::kFsFileNotFound)); + } + + return entry->inode; +} + +auto RamFs::RamFsInodeOps::Create(Inode* dir, const char* name, FileType type) + -> Expected { + if (dir == nullptr || name == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (type != FileType::kRegular && type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + RamInode* ram_dir = reinterpret_cast( + reinterpret_cast(dir) - offsetof(RamInode, inode)); + + // 分配新 inode + auto alloc_result = fs_->AllocateInode(); + if (!alloc_result.has_value()) { + return std::unexpected(alloc_result.error()); + } + + Inode* new_inode = alloc_result.value(); + new_inode->type = type; + + // 添加到目录 + auto add_result = fs_->AddToDirectory(ram_dir, name, new_inode); + if (!add_result.has_value()) { + fs_->FreeInode(new_inode); + return std::unexpected(add_result.error()); + } + + return new_inode; +} + +auto RamFs::RamFsInodeOps::Unlink(Inode* dir, const char* name) + -> Expected { + if (dir == nullptr || name == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + RamInode* ram_dir = reinterpret_cast( + reinterpret_cast(dir) - offsetof(RamInode, inode)); + + // 查找目标 + RamDirEntry* entry = fs_->FindInDirectory(ram_dir, name); + if (entry == nullptr) { + return std::unexpected(Error(ErrorCode::kFsFileNotFound)); + } + + // 不能删除目录 + if (entry->inode != nullptr && entry->inode->type == FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsIsADirectory)); + } + + // 从目录中移除 + auto remove_result = fs_->RemoveFromDirectory(ram_dir, name); + if (!remove_result.has_value()) { + return remove_result; + } + + // 如果链接计数为 0,释放 inode + if (entry->inode != nullptr && entry->inode->link_count == 0) { + fs_->FreeInode(entry->inode); + } + + return {}; +} + +auto RamFs::RamFsInodeOps::Mkdir(Inode* dir, const char* name) + -> Expected { + // 复用 Create,但指定类型为目录 + return Create(dir, name, FileType::kDirectory); +} + +auto RamFs::RamFsInodeOps::Rmdir(Inode* dir, const char* name) + -> Expected { + if (dir == nullptr || name == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + RamInode* ram_dir = reinterpret_cast( + reinterpret_cast(dir) - offsetof(RamInode, inode)); + + // 查找目标 + RamDirEntry* entry = fs_->FindInDirectory(ram_dir, name); + if (entry == nullptr) { + return std::unexpected(Error(ErrorCode::kFsFileNotFound)); + } + + // 必须是目录 + if (entry->inode == nullptr || entry->inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + // 检查目录是否为空 + RamInode* target = reinterpret_cast( + reinterpret_cast(entry->inode) - offsetof(RamInode, inode)); + if (!fs_->IsDirectoryEmpty(target)) { + return std::unexpected(Error(ErrorCode::kFsNotEmpty)); + } + + // 从目录中移除 + auto remove_result = fs_->RemoveFromDirectory(ram_dir, name); + if (!remove_result.has_value()) { + return remove_result; + } + + // 释放 inode + if (entry->inode->link_count == 0) { + fs_->FreeInode(entry->inode); + } + + return {}; +} + +// FileOps 实现 + +auto RamFs::RamFsFileOps::Read(File* file, void* buf, size_t count) + -> Expected { + if (file == nullptr || buf == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (file->inode == nullptr) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + + if (file->inode->type != FileType::kRegular) { + return std::unexpected(Error(ErrorCode::kFsIsADirectory)); + } + + RamInode* ram_inode = reinterpret_cast( + reinterpret_cast(file->inode) - offsetof(RamInode, inode)); + + // 计算可读字节数 + if (file->offset>= file->inode->size) { + return 0; // EOF + } + + size_t available = file->inode->size - file->offset; + size_t to_read = (count < available) ? count : available; + + if (to_read == 0) { + return 0; + } + + // 复制数据 + memcpy(buf, static_cast(ram_inode->data) + file->offset, to_read); + + file->offset += to_read; + return to_read; +} + +auto RamFs::RamFsFileOps::Write(File* file, const void* buf, size_t count) + -> Expected { + if (file == nullptr || buf == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (file->inode == nullptr) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + + if (file->inode->type != FileType::kRegular) { + return std::unexpected(Error(ErrorCode::kFsIsADirectory)); + } + + RamInode* ram_inode = reinterpret_cast( + reinterpret_cast(file->inode) - offsetof(RamInode, inode)); + + // 检查是否需要扩展文件 + size_t new_size = file->offset + count; + if (new_size> ram_inode->capacity) { + auto expand_result = fs_->ExpandFile(ram_inode, new_size); + if (!expand_result.has_value()) { + return std::unexpected(expand_result.error()); + } + } + + // 写入数据 + memcpy(static_cast(ram_inode->data) + file->offset, buf, count); + + file->offset += count; + + // 更新文件大小 + if (file->offset> file->inode->size) { + file->inode->size = file->offset; + } + + return count; +} + +auto RamFs::RamFsFileOps::Seek(File* file, int64_t offset, SeekWhence whence) + -> Expected { + if (file == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + uint64_t new_offset = 0; + + switch (whence) { + case SeekWhence::kSet: + if (offset < 0) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + new_offset = static_cast(offset); + break; + + case SeekWhence::kCur: + if (offset < 0 && static_cast(-offset)> file->offset) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + new_offset = + static_cast(static_cast(file->offset) + offset); + break; + + case SeekWhence::kEnd: + if (file->inode == nullptr) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + if (offset < 0 && static_cast(-offset)> file->inode->size) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + new_offset = static_cast( + static_cast(file->inode->size) + offset); + break; + + default: + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + file->offset = new_offset; + return new_offset; +} + +auto RamFs::RamFsFileOps::Close(File* file) -> Expected { + if (file == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // ramfs 没有特殊的关闭操作 + // File 对象由调用者释放 + return {}; +} + +auto RamFs::RamFsFileOps::ReadDir(File* file, DirEntry* dirent, size_t count) + -> Expected { + if (file == nullptr || dirent == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (file->inode == nullptr || file->inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + RamInode* ram_dir = reinterpret_cast( + reinterpret_cast(file->inode) - offsetof(RamInode, inode)); + + RamDirEntry* entries = static_cast(ram_dir->data); + + size_t read_count = 0; + size_t offset = file->offset; + + // 添加 . 和 .. 条目 + if (offset == 0 && read_count < count) { + dirent[read_count].ino = file->inode->ino; + dirent[read_count].type = static_cast(FileType::kDirectory); + strncpy(dirent[read_count].name, ".", sizeof(dirent[read_count].name)); + ++read_count; + ++offset; + } + + if (offset == 1 && read_count < count) { + Inode* parent_inode = + (file->dentry != nullptr && file->dentry->parent != nullptr) + ? file->dentry->parent->inode + : file->inode; + dirent[read_count].ino = parent_inode->ino; + dirent[read_count].type = static_cast(FileType::kDirectory); + strncpy(dirent[read_count].name, "..", sizeof(dirent[read_count].name)); + ++read_count; + ++offset; + } + + // 读取实际目录项(跳过 . 和 .. 的偏移) + size_t entry_idx = (offset> 2) ? offset - 2 : 0; + while (read_count < count && entry_idx < ram_dir->child_count) { + dirent[read_count].ino = entries[entry_idx].inode->ino; + dirent[read_count].type = + static_cast(entries[entry_idx].inode->type); + strncpy(dirent[read_count].name, entries[entry_idx].name, + sizeof(dirent[read_count].name)); + ++read_count; + ++entry_idx; + } + + file->offset = offset + read_count; + return read_count; +} + +// 辅助函数实现 + +auto RamFs::FindInDirectory(RamInode* dir, const char* name) -> RamDirEntry* { + if (dir == nullptr || dir->inode.type != FileType::kDirectory || + dir->data == nullptr) { + return nullptr; + } + + RamDirEntry* entries = static_cast(dir->data); + for (size_t i = 0; i < dir->child_count; ++i) { + if (strcmp(entries[i].name, name) == 0) { + return &entries[i]; + } + } + + return nullptr; +} + +auto RamFs::AddToDirectory(RamInode* dir, const char* name, Inode* inode) + -> Expected { + if (dir == nullptr || name == nullptr || inode == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (dir->inode.type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + // 检查是否已存在 + if (FindInDirectory(dir, name) != nullptr) { + return std::unexpected(Error(ErrorCode::kFsFileExists)); + } + + // 扩展目录容量 + size_t current_entries = dir->capacity / sizeof(RamDirEntry); + if (dir->child_count>= current_entries) { + size_t new_capacity = (current_entries == 0) ? 16 : current_entries * 2; + RamDirEntry* new_data = new RamDirEntry[new_capacity]; + if (new_data == nullptr) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + // 复制旧数据 + if (dir->data != nullptr) { + memcpy(new_data, dir->data, dir->child_count * sizeof(RamDirEntry)); + delete[] static_cast(dir->data); + } + + dir->data = new_data; + dir->capacity = new_capacity * sizeof(RamDirEntry); + } + + // 添加新条目 + RamDirEntry* entries = static_cast(dir->data); + RamDirEntry* new_entry = &entries[dir->child_count]; + strncpy(new_entry->name, name, sizeof(new_entry->name) - 1); + new_entry->name[sizeof(new_entry->name) - 1] = '0円'; + new_entry->inode = inode; + + ++dir->child_count; + ++inode->link_count; + + return {}; +} + +auto RamFs::RemoveFromDirectory(RamInode* dir, const char* name) + -> Expected { + if (dir == nullptr || name == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + RamDirEntry* entry = FindInDirectory(dir, name); + if (entry == nullptr) { + return std::unexpected(Error(ErrorCode::kFsFileNotFound)); + } + + // 减少链接计数 + if (entry->inode != nullptr) { + --entry->inode->link_count; + } + + // 移除条目(将最后一个条目移到此处) + RamDirEntry* entries = static_cast(dir->data); + size_t last_idx = dir->child_count - 1; + if (entry != &entries[last_idx]) { + *entry = entries[last_idx]; + } + + --dir->child_count; + return {}; +} + +auto RamFs::IsDirectoryEmpty(RamInode* dir) -> bool { + if (dir == nullptr || dir->inode.type != FileType::kDirectory) { + return true; + } + + // 只包含 . 和 .. 的目录也是空的 + return dir->child_count == 0; +} + +auto RamFs::ExpandFile(RamInode* inode, size_t new_size) -> Expected { + if (inode == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (new_size <= inode->capacity) { + return {}; + } + + // 计算新容量(按 256 字节对齐) + size_t new_capacity = ((new_size + 255) / 256) * 256; + + uint8_t* new_data = new uint8_t[new_capacity]; + if (new_data == nullptr) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + // 复制旧数据 + if (inode->data != nullptr) { + memcpy(new_data, inode->data, inode->inode.size); + delete[] static_cast(inode->data); + } + + inode->data = new_data; + inode->capacity = new_capacity; + + return {}; +} + +} // namespace ramfs diff --git a/src/filesystem/vfs/CMakeLists.txt b/src/filesystem/vfs/CMakeLists.txt new file mode 100644 index 000000000..f62740688 --- /dev/null +++ b/src/filesystem/vfs/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright The SimpleKernel Contributors + +ADD_LIBRARY (vfs INTERFACE) + +TARGET_INCLUDE_DIRECTORIES (vfs INTERFACE include) + +TARGET_SOURCES ( + vfs + INTERFACE vfs.cpp + mount.cpp + lookup.cpp + open.cpp + close.cpp + read.cpp + write.cpp + seek.cpp + mkdir.cpp + rmdir.cpp + unlink.cpp + readdir.cpp) diff --git a/src/filesystem/vfs/close.cpp b/src/filesystem/vfs/close.cpp new file mode 100644 index 000000000..e5712a5bb --- /dev/null +++ b/src/filesystem/vfs/close.cpp @@ -0,0 +1,28 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto Close(File* file) -> Expected { + if (file == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (file->ops != nullptr) { + auto result = file->ops->Close(file); + if (!result.has_value()) { + return result; + } + } + + delete file; + return {}; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/include/block_device.hpp b/src/filesystem/vfs/include/block_device.hpp new file mode 100644 index 000000000..08006c49f --- /dev/null +++ b/src/filesystem/vfs/include/block_device.hpp @@ -0,0 +1,80 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief 块设备抽象接口 + */ + +#ifndef SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_BLOCK_DEVICE_HPP_ +#define SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_BLOCK_DEVICE_HPP_ + +#include +#include + +#include "expected.hpp" + +namespace vfs { + +/** + * @brief 块设备抽象基类 + * @details 所有块设备驱动(virtio-blk、ramdisk 等)必须实现此接口。 + * 块设备以固定大小的扇区 (sector) 为最小 I/O 单位。 + */ +class BlockDevice { + public: + virtual ~BlockDevice() = default; + + /** + * @brief 读取连续扇区 + * @param sector_start 起始扇区号(LBA) + * @param sector_count 扇区数量 + * @param buffer 输出缓冲区,大小至少为 sector_count * GetSectorSize() + * @return Expected 成功时返回实际读取的字节数 + * @pre buffer != nullptr + * @pre sector_start + sector_count <= GetSectorCount() + * @post 返回值 == sector_count * GetSectorSize() 或错误 + */ + [[nodiscard]] virtual auto ReadSectors(uint64_t sector_start, + uint32_t sector_count, void* buffer) + -> Expected = 0; + + /** + * @brief 写入连续扇区 + * @param sector_start 起始扇区号(LBA) + * @param sector_count 扇区数量 + * @param buffer 输入缓冲区 + * @return Expected 成功时返回实际写入的字节数 + * @pre buffer != nullptr + * @pre sector_start + sector_count <= GetSectorCount() + */ + [[nodiscard]] virtual auto WriteSectors(uint64_t sector_start, + uint32_t sector_count, + const void* buffer) + -> Expected = 0; + + /** + * @brief 获取扇区大小(通常为 512 字节) + * @return 扇区大小(字节) + */ + [[nodiscard]] virtual auto GetSectorSize() const -> uint32_t = 0; + + /** + * @brief 获取设备总扇区数 + * @return 总扇区数 + */ + [[nodiscard]] virtual auto GetSectorCount() const -> uint64_t = 0; + + /** + * @brief 获取设备名称(如 "virtio-blk0") + * @return 设备名称 + */ + [[nodiscard]] virtual auto GetName() const -> const char* = 0; + + /** + * @brief 刷新设备缓存到物理介质 + * @return Expected 成功或错误 + */ + [[nodiscard]] virtual auto Flush() -> Expected { return {}; } +}; + +} // namespace vfs + +#endif /* SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_BLOCK_DEVICE_HPP_ */ diff --git a/src/filesystem/vfs/include/filesystem.hpp b/src/filesystem/vfs/include/filesystem.hpp new file mode 100644 index 000000000..21aa6ee3d --- /dev/null +++ b/src/filesystem/vfs/include/filesystem.hpp @@ -0,0 +1,76 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief 文件系统基类接口 + */ + +#ifndef SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_FILESYSTEM_HPP_ +#define SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_FILESYSTEM_HPP_ + +#include "block_device.hpp" +#include "vfs_types.hpp" + +namespace vfs { + +/** + * @brief 文件系统类型基类 + * @details 每种文件系统(ramfs/fat32/ext2 等)注册一个 FileSystem 实例。 + * VFS 通过此接口挂载/卸载文件系统。 + */ +class FileSystem { + public: + virtual ~FileSystem() = default; + + /** + * @brief 获取文件系统类型名(如 "ramfs", "fat32") + * @return 文件系统类型名 + */ + [[nodiscard]] virtual auto GetName() const -> const char* = 0; + + /** + * @brief 挂载文件系统 + * @param device 块设备指针(ramfs 等内存文件系统传 nullptr) + * @return Expected 根目录 inode + * @post 返回的 inode->type == FileType::kDirectory + */ + [[nodiscard]] virtual auto Mount(BlockDevice* device) -> Expected = 0; + + /** + * @brief 卸载文件系统 + * @return Expected 成功或错误 + * @pre 没有打开的文件引用此文件系统 + */ + [[nodiscard]] virtual auto Unmount() -> Expected = 0; + + /** + * @brief 将缓存数据刷写到磁盘 + * @return Expected 成功或错误 + */ + [[nodiscard]] virtual auto Sync() -> Expected = 0; + + /** + * @brief 分配新 inode + * @return Expected 新分配的 inode 或错误 + * @note 由具体文件系统实现 inode 分配策略 + */ + [[nodiscard]] virtual auto AllocateInode() -> Expected = 0; + + /** + * @brief 释放 inode + * @param inode 要释放的 inode + * @return Expected 成功或错误 + * @pre inode != nullptr + * @pre inode->link_count == 0 + */ + [[nodiscard]] virtual auto FreeInode(Inode* inode) -> Expected = 0; + + /** + * @brief 获取文件系统的文件操作接口 + * @return FileOps* 文件操作接口指针 + * @note 用于创建 File 对象时设置 ops + */ + [[nodiscard]] virtual auto GetFileOps() -> FileOps* = 0; +}; + +} // namespace vfs + +#endif /* SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_FILESYSTEM_HPP_ */ diff --git a/src/filesystem/vfs/include/mount.hpp b/src/filesystem/vfs/include/mount.hpp new file mode 100644 index 000000000..dd5ed8ba7 --- /dev/null +++ b/src/filesystem/vfs/include/mount.hpp @@ -0,0 +1,117 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief 挂载管理 + */ + +#ifndef SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_MOUNT_HPP_ +#define SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_MOUNT_HPP_ + +#include "filesystem.hpp" + +namespace vfs { + +/** + * @brief 挂载点 + * @details 将一个文件系统的根 inode 关联到目录树中的某个 dentry 上 + */ +struct MountPoint { + /// 挂载路径(如 "/mnt/disk") + const char* mount_path; + /// 挂载点在父文件系统中的 dentry + Dentry* mount_dentry; + /// 挂载的文件系统实例 + FileSystem* filesystem; + /// 关联的块设备(可为 nullptr) + BlockDevice* device; + /// 该文件系统的根 inode + Inode* root_inode; + /// 该文件系统的根 dentry + Dentry* root_dentry; + /// 是否处于活动状态 + bool active; + + /// @brief 构造函数 + MountPoint(); +}; +/** + * @brief 挂载表管理器 + */ +class MountTable { + public: + /// 最大挂载点数 + static constexpr size_t kMaxMounts = 16; + + /// @brief 构造函数 + /// @brief 构造函数 + MountTable(); + /// @brief 析构函数 + ~MountTable() = default; + + // 禁止拷贝和移动 + MountTable(const MountTable&) = delete; + MountTable(MountTable&&) = delete; + auto operator=(const MountTable&) -> MountTable& = delete; + auto operator=(MountTable&&) -> MountTable& = delete; + + /** + * @brief 挂载文件系统到指定路径 + * @param path 挂载点路径 + * @param fs 文件系统实例 + * @param device 块设备(可为 nullptr) + * @return Expected + * @pre path 必须是已存在的目录 + * @post 后续对 path 下路径的访问将被重定向到新文件系统 + */ + [[nodiscard]] auto Mount(const char* path, FileSystem* fs, + BlockDevice* device) -> Expected; + + /** + * @brief 卸载指定路径的文件系统 + * @param path 挂载点路径 + * @return Expected + */ + [[nodiscard]] auto Unmount(const char* path) -> Expected; + + /** + * @brief 根据路径查找对应的挂载点 + * @param path 文件路径 + * @return MountPoint* 最长前缀匹配的挂载点,未找到返回 nullptr + */ + auto Lookup(const char* path) -> MountPoint*; + + /** + * @brief 获取指定挂载点的根 dentry + * @param mp 挂载点 + * @return Dentry* 挂载点根 dentry + */ + auto GetRootDentry(MountPoint* mp) -> Dentry*; + + /** + * @brief 检查路径是否是挂载点 + * @param path 路径 + * @return true 是挂载点 + * @return false 不是挂载点 + */ + auto IsMountPoint(const char* path) -> bool; + + /** + * @brief 获取根挂载点 + * @return MountPoint* 根挂载点 + */ + auto GetRootMount() -> MountPoint*; + + private: + MountPoint mounts_[kMaxMounts]; + size_t mount_count_; + MountPoint* root_mount_; +}; + +/** + * @brief 获取全局挂载表实例 + * @return MountTable& 挂载表引用 + */ +[[nodiscard]] auto GetMountTable() -> MountTable&; + +} // namespace vfs + +#endif /* SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_MOUNT_HPP_ */ diff --git a/src/filesystem/vfs/include/vfs.hpp b/src/filesystem/vfs/include/vfs.hpp new file mode 100644 index 000000000..9f146bc3a --- /dev/null +++ b/src/filesystem/vfs/include/vfs.hpp @@ -0,0 +1,216 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief VFS 核心数据结构和接口 + */ + +#ifndef SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_VFS_HPP_ +#define SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_VFS_HPP_ + +#include "vfs_types.hpp" + +namespace vfs { + +/** + * @brief Inode — 文件元数据(独立于路径名) + * @details 每个文件/目录在 VFS 中有且仅有一个 Inode。 + * Inode 持有文件的元信息和操作方法指针。 + */ +struct Inode { + /// inode 编号(文件系统内唯一) + uint64_t ino; + /// 文件类型 + FileType type; + /// 文件大小(字节) + uint64_t size; + /// 权限位(简化版) + uint32_t permissions; + /// 硬链接计数 + uint32_t link_count; + /// 文件系统私有数据指针 + void* fs_private; + /// 所属文件系统 + FileSystem* fs; + + /// inode 操作接口 + InodeOps* ops = nullptr; + + /// @brief 构造函数 + Inode(); +}; + +/** + * @brief Dentry — 目录项缓存(路径名 ↔ Inode 的映射) + * @details Dentry 构成一棵树,反映目录层次结构。 + * 支持路径查找加速。 + */ +struct Dentry { + /// 文件/目录名 + char name[256]; + /// 关联的 inode + Inode* inode; + /// 父目录项 + Dentry* parent; + /// 子目录项链表头 + Dentry* children; + /// 兄弟目录项(同一父目录下) + Dentry* next_sibling; + /// 文件系统私有数据 + void* fs_private; + + /// @brief 构造函数 + Dentry(); +}; + +/** + * @brief File — 打开的文件实例(每次 open 产生一个) + * @details File 对象持有当前偏移量和操作方法指针。 + * 多个 File 可以指向同一个 Inode。 + */ +struct File { + /// 关联的 inode + Inode* inode; + /// 关联的 dentry + Dentry* dentry; + /// 当前读写偏移量 + uint64_t offset; + /// 打开标志 (OpenFlags) + uint32_t flags; + + /// 文件操作接口 + FileOps* ops = nullptr; + + /// @brief 构造函数 + File(); +}; + +/** + * @brief VFS 全局初始化 + * @return Expected 成功或错误 + * @post VFS 子系统已准备好接受挂载请求 + * @note 线程安全:应在系统启动时单线程调用 + * @note 幂等性:重复调用会返回成功(无操作) + */ +auto Init() -> Expected; + +/** + * @brief 路径解析,查找 dentry + * @param path 绝对路径(以 / 开头) + * @return Expected 找到的 dentry 或错误 + * @pre path != nullptr && path[0] == '/' + * @note 线程安全:是,内部使用 MountTable 锁 + * @note 会自动跨越挂载点解析路径 + */ +auto Lookup(const char* path) -> Expected; + +/** + * @brief 打开文件 + * @param path 文件路径 + * @param flags 打开标志 + * @return Expected 文件对象或错误 + * @pre path != nullptr + * @post 成功时返回有效的 File 对象 + * @note 调用者负责调用 Close() 释放 File 对象 + * @note 若 flags 包含 kOCreate,文件不存在时会自动创建 + */ +auto Open(const char* path, uint32_t flags) -> Expected; + +/** + * @brief 关闭文件 + * @param file 文件对象 + * @return Expected 成功或错误 + * @pre file != nullptr + * @note 会调用文件系统特定的 close 回调 + * @note 关闭后 File 指针失效,不应再使用 + */ +auto Close(File* file) -> Expected; + +/** + * @brief 从文件读取数据 + * @param file 文件对象 + * @param buf 输出缓冲区 + * @param count 最大读取字节数 + * @return Expected 实际读取的字节数或错误 + * @pre file != nullptr && buf != nullptr + * @note 实际读取字节数可能小于 count(到达文件末尾) + * @note 会自动更新 file->offset + */ +auto Read(File* file, void* buf, size_t count) -> Expected; + +/** + * @brief 向文件写入数据 + * @param file 文件对象 + * @param buf 输入缓冲区 + * @param count 要写入的字节数 + * @return Expected 实际写入的字节数或错误 + * @pre file != nullptr && buf != nullptr + * @note 文件系统可能需要在写入前扩展文件大小 + * @note 会自动更新 file->offset 和 file->inode->size + */ +auto Write(File* file, const void* buf, size_t count) -> Expected; + +/** + * @brief 调整文件偏移量 + * @param file 文件对象 + * @param offset 偏移量 + * @param whence 基准位置 + * @return Expected 新的偏移量或错误 + * @pre file != nullptr + * @note 如果 whence 为 kEnd 且 offset 为正,可能超过文件末尾 + * @note 返回的偏移量是绝对位置(从文件开头计算) + */ +auto Seek(File* file, int64_t offset, SeekWhence whence) -> Expected; + +/** + * @brief 创建目录 + * @param path 目录路径 + * @return Expected 成功或错误 + * @pre path != nullptr + * @note 父目录必须存在 + * @note 如果目录已存在会返回错误 + */ +auto MkDir(const char* path) -> Expected; + +/** + * @brief 删除目录 + * @param path 目录路径 + * @return Expected 成功或错误 + * @pre path != nullptr + * @note 目录必须为空(不含子项) + * @note 不能删除挂载点 + */ +auto RmDir(const char* path) -> Expected; + +/** + * @brief 删除文件 + * @param path 文件路径 + * @return Expected 成功或错误 + * @pre path != nullptr + * @note 不能删除目录(使用 RmDir) + * @note 如果多个硬链接,只会减少链接计数 + */ +auto Unlink(const char* path) -> Expected; + +/** + * @brief 读取目录内容 + * @param file 目录文件对象 + * @param dirent 输出目录项数组 + * @param count 最多读取的条目数 + * @return Expected 实际读取的条目数或错误 + * @pre file != nullptr && file->inode->type == FileType::kDirectory + * @note 会返回 . 和 .. 目录项 + * @note 多次调用可遍历整个目录,自动维护偏移量 + */ +auto ReadDir(File* file, DirEntry* dirent, size_t count) -> Expected; + +/** + * @brief 获取根目录 dentry + * @return Dentry* 根目录 dentry + * @pre VFS 已初始化且根文件系统已挂载 + * @note 返回 nullptr 表示根文件系统未挂载 + * @note 返回的指针由 VFS 内部管理,不应释放 + */ +auto GetRootDentry() -> Dentry*; + +} // namespace vfs + +#endif /* SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_VFS_HPP_ */ diff --git a/src/filesystem/vfs/include/vfs_types.hpp b/src/filesystem/vfs/include/vfs_types.hpp new file mode 100644 index 000000000..1829b7364 --- /dev/null +++ b/src/filesystem/vfs/include/vfs_types.hpp @@ -0,0 +1,196 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief VFS 基础类型定义 + * @note 此头文件只包含基础类型定义,不包含复杂依赖,用于解决循环依赖问题 + */ + +#ifndef SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_VFS_TYPES_HPP_ +#define SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_VFS_TYPES_HPP_ + +#include +#include + +#include "expected.hpp" + +namespace vfs { + +// Forward declarations +struct Inode; +struct Dentry; +struct File; +struct FileSystem; +class MountTable; + +/// 文件类型 +enum class FileType : uint8_t { + kUnknown = 0, + /// 普通文件 + kRegular = 1, + /// 目录 + kDirectory = 2, + /// 字符设备 + kCharDevice = 3, + /// 块设备 + kBlockDevice = 4, + /// 符号链接 + kSymlink = 5, + /// 命名管道 + kFifo = 6, +}; + +/// 文件打开标志(兼容 Linux O_* 定义) +enum OpenFlags : uint32_t { + kOReadOnly = 0x0000, + kOWriteOnly = 0x0001, + kOReadWrite = 0x0002, + kOCreate = 0x0040, + kOTruncate = 0x0200, + kOAppend = 0x0400, + /// 必须是目录 + kODirectory = 0x010000, +}; + +/// 文件 seek 基准 +enum class SeekWhence : int { + /// 从文件开头 + kSet = 0, + /// 从当前位置 + kCur = 1, + /// 从文件末尾 + kEnd = 2, +}; + +/// Inode 操作接口 +class InodeOps { + public: + virtual ~InodeOps() = default; + + /** + * @brief 在目录中查找指定名称的 inode + * @param dir 父目录 inode + * @param name 要查找的文件名 + * @return Expected 找到的 inode 或错误 + * @pre dir != nullptr && dir->type == FileType::kDirectory + * @pre name != nullptr && strlen(name)> 0 + */ + virtual auto Lookup(Inode* dir, const char* name) -> Expected = 0; + + /** + * @brief 在目录中创建新文件 + * @param dir 父目录 inode + * @param name 文件名 + * @param type 文件类型 + * @return Expected 新创建的 inode 或错误 + * @pre dir != nullptr && dir->type == FileType::kDirectory + * @pre name != nullptr && strlen(name)> 0 + * @post 返回的 inode->type == type + */ + virtual auto Create(Inode* dir, const char* name, FileType type) + -> Expected = 0; + + /** + * @brief 删除文件(解除链接) + * @param dir 父目录 inode + * @param name 要删除的文件名 + * @return Expected 成功或错误 + * @pre dir != nullptr && dir->type == FileType::kDirectory + * @pre name != nullptr + */ + virtual auto Unlink(Inode* dir, const char* name) -> Expected = 0; + + /** + * @brief 创建目录 + * @param dir 父目录 inode + * @param name 目录名 + * @return Expected 新创建的目录 inode 或错误 + * @pre dir != nullptr && dir->type == FileType::kDirectory + * @pre name != nullptr && strlen(name)> 0 + * @post 返回的 inode->type == FileType::kDirectory + */ + virtual auto Mkdir(Inode* dir, const char* name) -> Expected = 0; + + /** + * @brief 删除目录 + * @param dir 父目录 inode + * @param name 要删除的目录名 + * @return Expected 成功或错误 + * @pre dir != nullptr && dir->type == FileType::kDirectory + * @pre name != nullptr + */ + virtual auto Rmdir(Inode* dir, const char* name) -> Expected = 0; +}; +/// 目录项结构(用于 readdir) +struct DirEntry { + /// inode 编号 + uint64_t ino; + /// 文件类型 + uint8_t type; + /// 文件名 + char name[256]; +}; + +/// File 操作接口 +class FileOps { + public: + virtual ~FileOps() = default; + + /** + * @brief 从文件读取数据 + * @param file 文件对象 + * @param buf 输出缓冲区 + * @param count 最大读取字节数 + * @return Expected 实际读取的字节数或错误 + * @pre file != nullptr && buf != nullptr + * @post 返回值 <= count + */ + virtual auto Read(File* file, void* buf, size_t count) + -> Expected = 0; + + /** + * @brief 向文件写入数据 + * @param file 文件对象 + * @param buf 输入缓冲区 + * @param count 要写入的字节数 + * @return Expected 实际写入的字节数或错误 + * @pre file != nullptr && buf != nullptr + * @post 返回值 <= count + */ + virtual auto Write(File* file, const void* buf, size_t count) + -> Expected = 0; + + /** + * @brief 调整文件偏移量 + * @param file 文件对象 + * @param offset 偏移量 + * @param whence 基准位置 + * @return Expected 新的偏移量或错误 + * @pre file != nullptr + */ + virtual auto Seek(File* file, int64_t offset, SeekWhence whence) + -> Expected = 0; + + /** + * @brief 关闭文件 + * @param file 文件对象 + * @return Expected 成功或错误 + * @pre file != nullptr + * @post file 对象将被释放 + */ + virtual auto Close(File* file) -> Expected = 0; + + /** + * @brief 读取目录项 + * @param file 目录文件对象 + * @param dirent 输出目录项数组 + * @param count 最多读取的条目数 + * @return Expected 实际读取的条目数或错误 + * @pre file != nullptr && file->inode->type == FileType::kDirectory + * @pre dirent != nullptr + */ + virtual auto ReadDir(File* file, DirEntry* dirent, size_t count) + -> Expected = 0; +}; + +} // namespace vfs + +#endif /* SIMPLEKERNEL_SRC_FILESYSTEM_VFS_INCLUDE_VFS_TYPES_HPP_ */ diff --git a/src/filesystem/vfs/lookup.cpp b/src/filesystem/vfs/lookup.cpp new file mode 100644 index 000000000..fb5997c61 --- /dev/null +++ b/src/filesystem/vfs/lookup.cpp @@ -0,0 +1,134 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto Lookup(const char* path) -> Expected { + if (!GetVfsState().initialized) { + return std::unexpected(Error(ErrorCode::kFsNotMounted)); + } + + if (path == nullptr || path[0] != '/') { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // 空路径或根目录 + if (path[0] == '/' && (path[1] == '0円' || path[1] == '/')) { + if (GetVfsState().root_dentry == nullptr) { + return std::unexpected(Error(ErrorCode::kFsNotMounted)); + } + return GetVfsState().root_dentry; + } + + // 查找路径对应的挂载点 + MountPoint* mp = GetVfsState().mount_table->Lookup(path); + if (mp == nullptr || mp->root_dentry == nullptr) { + return std::unexpected(Error(ErrorCode::kFsNotMounted)); + } + + // 从挂载点根开始解析路径 + Dentry* current = mp->root_dentry; + const char* p = SkipLeadingSlashes(path); + + // 如果挂载路径不是根,需要跳过挂载路径部分 + if (mp->mount_path != nullptr && strcmp(mp->mount_path, "/") != 0) { + const char* mount_path = SkipLeadingSlashes(mp->mount_path); + while (*mount_path != '0円') { + char component[256]; + size_t len = CopyPathComponent(mount_path, component, sizeof(component)); + if (len == 0) { + break; + } + mount_path += len; + mount_path = SkipLeadingSlashes(mount_path); + + // 跳过挂载路径的组件 + len = CopyPathComponent(p, component, sizeof(component)); + if (len == 0) { + break; + } + p += len; + p = SkipLeadingSlashes(p); + } + } + + // 逐级解析路径组件 + char component[256]; + while (*p != '0円') { + // 检查当前 dentry 是否是目录 + if (current->inode == nullptr || + current->inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + // 提取路径组件 + size_t len = CopyPathComponent(p, component, sizeof(component)); + if (len == 0) { + break; + } + p += len; + p = SkipLeadingSlashes(p); + + // 处理 "." 和 ".." + if (strcmp(component, ".") == 0) { + continue; + } + if (strcmp(component, "..") == 0) { + if (current->parent != nullptr) { + current = current->parent; + } + continue; + } + + // 在子节点中查找 + Dentry* child = FindChild(current, component); + if (child == nullptr) { + // 尝试通过 inode ops 查找 + if (current->inode->ops != nullptr) { + auto result = current->inode->ops->Lookup(current->inode, component); + if (!result.has_value()) { + return std::unexpected(Error(ErrorCode::kFsFileNotFound)); + } + + // 创建新的 dentry + child = new Dentry(); + if (child == nullptr) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + strncpy(child->name, component, sizeof(child->name) - 1); + child->name[sizeof(child->name) - 1] = '0円'; + child->inode = result.value(); + AddChild(current, child); + } else { + return std::unexpected(Error(ErrorCode::kFsFileNotFound)); + } + } + + current = child; + + // 检查是否遇到挂载点 + if (current->inode != nullptr) { + // 检查是否有文件系统挂载在此 dentry 上 + for (size_t i = 0; i < MountTable::kMaxMounts; ++i) { + MountPoint* next_mp = GetVfsState().mount_table->Lookup(p); + if (next_mp != nullptr && next_mp != mp && + next_mp->mount_dentry == current) { + mp = next_mp; + current = next_mp->root_dentry; + break; + } + } + } + } + + return current; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/mkdir.cpp b/src/filesystem/vfs/mkdir.cpp new file mode 100644 index 000000000..ad7f30889 --- /dev/null +++ b/src/filesystem/vfs/mkdir.cpp @@ -0,0 +1,77 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto MkDir(const char* path) -> Expected { + if (path == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // 解析父目录路径和目录名 + char parent_path[512]; + char dir_name[256]; + const char* last_slash = strrchr(path, '/'); + if (last_slash == nullptr || last_slash == path) { + strncpy(parent_path, "/", sizeof(parent_path)); + strncpy(dir_name, path[0] == '/' ? path + 1 : path, sizeof(dir_name)); + } else { + size_t parent_len = last_slash - path; + if (parent_len>= sizeof(parent_path)) { + parent_len = sizeof(parent_path) - 1; + } + strncpy(parent_path, path, parent_len); + parent_path[parent_len] = '0円'; + strncpy(dir_name, last_slash + 1, sizeof(dir_name)); + } + dir_name[sizeof(dir_name) - 1] = '0円'; + + // 查找父目录 + auto parent_result = Lookup(parent_path); + if (!parent_result.has_value()) { + return std::unexpected(parent_result.error()); + } + + Dentry* parent_dentry = parent_result.value(); + if (parent_dentry->inode == nullptr || + parent_dentry->inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + // 检查目录是否已存在 + if (FindChild(parent_dentry, dir_name) != nullptr) { + return std::unexpected(Error(ErrorCode::kFsFileExists)); + } + + // 创建目录 + if (parent_dentry->inode->ops == nullptr) { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + + auto result = + parent_dentry->inode->ops->Mkdir(parent_dentry->inode, dir_name); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + + // 创建 dentry + Dentry* new_dentry = new Dentry(); + if (new_dentry == nullptr) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + strncpy(new_dentry->name, dir_name, sizeof(new_dentry->name) - 1); + new_dentry->inode = result.value(); + AddChild(parent_dentry, new_dentry); + + klog::Debug("VFS: created directory '%s'\n", path); + return {}; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/mount.cpp b/src/filesystem/vfs/mount.cpp new file mode 100644 index 000000000..e9205242d --- /dev/null +++ b/src/filesystem/vfs/mount.cpp @@ -0,0 +1,233 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "mount.hpp" + +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs.hpp" + +namespace vfs { + +// MountPoint 构造函数实现 +MountPoint::MountPoint() + : mount_path(nullptr), + mount_dentry(nullptr), + filesystem(nullptr), + device(nullptr), + root_inode(nullptr), + root_dentry(nullptr), + active(false) {} + +MountTable::MountTable() : mounts_{}, mount_count_(0), root_mount_(nullptr) {} + +auto MountTable::Mount(const char* path, FileSystem* fs, BlockDevice* device) + -> Expected { + if (path == nullptr || fs == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // 检查挂载点数量 + if (mount_count_>= kMaxMounts) { + return std::unexpected(Error(ErrorCode::kFsMountFailed)); + } + + // 规范化路径(确保以 / 开头) + if (path[0] != '/') { + return std::unexpected(Error(ErrorCode::kFsInvalidPath)); + } + + // 检查路径是否已被挂载 + for (size_t i = 0; i < mount_count_; ++i) { + if (mounts_[i].active && strcmp(mounts_[i].mount_path, path) == 0) { + return std::unexpected(Error(ErrorCode::kFsAlreadyMounted)); + } + } + + // 挂载文件系统 + auto mount_result = fs->Mount(device); + if (!mount_result.has_value()) { + klog::Err("MountTable: failed to mount filesystem '%s': %s\n", + fs->GetName(), mount_result.error().message()); + return std::unexpected(Error(ErrorCode::kFsMountFailed)); + } + + Inode* root_inode = mount_result.value(); + if (root_inode == nullptr || root_inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + + // 为根 inode 创建 dentry + Dentry* root_dentry = new Dentry(); + if (root_dentry == nullptr) { + fs->Unmount(); + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + root_dentry->inode = root_inode; + strncpy(root_dentry->name, "/", sizeof(root_dentry->name)); + + // 查找挂载点 dentry(如果是非根挂载) + Dentry* mount_dentry = nullptr; + if (strcmp(path, "/") != 0) { + // 需要找到父文件系统中对应的 dentry + // 这里简化处理,实际应该通过 VFS Lookup 找到 + // 暂时设置为 nullptr,表示根挂载 + } + + // 找到空闲挂载点槽位 + size_t slot = 0; + for (; slot < kMaxMounts; ++slot) { + if (!mounts_[slot].active) { + break; + } + } + + if (slot>= kMaxMounts) { + delete root_dentry; + fs->Unmount(); + return std::unexpected(Error(ErrorCode::kFsMountFailed)); + } + + // 填充挂载点信息 + mounts_[slot].mount_path = path; + mounts_[slot].mount_dentry = mount_dentry; + mounts_[slot].filesystem = fs; + mounts_[slot].device = device; + mounts_[slot].root_inode = root_inode; + mounts_[slot].root_dentry = root_dentry; + mounts_[slot].active = true; + + ++mount_count_; + + // 如果是根挂载,设置 root_mount_ + if (strcmp(path, "/") == 0) { + root_mount_ = &mounts_[slot]; + // 更新 VFS 根 dentry + extern void SetRootDentry(Dentry*); + SetRootDentry(root_dentry); + } + + klog::Info("MountTable: mounted '%s' on '%s'\n", fs->GetName(), path); + return {}; +} + +auto MountTable::Unmount(const char* path) -> Expected { + if (path == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // 查找挂载点 + MountPoint* mp = nullptr; + size_t mp_index = 0; + for (size_t i = 0; i < kMaxMounts; ++i) { + if (mounts_[i].active && strcmp(mounts_[i].mount_path, path) == 0) { + mp = &mounts_[i]; + mp_index = i; + break; + } + } + + if (mp == nullptr) { + return std::unexpected(Error(ErrorCode::kFsNotMounted)); + } + + // 卸载文件系统 + auto result = mp->filesystem->Unmount(); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + + // 清理挂载点 + delete mp->root_dentry; + + mp->active = false; + mp->mount_path = nullptr; + mp->mount_dentry = nullptr; + mp->filesystem = nullptr; + mp->device = nullptr; + mp->root_inode = nullptr; + mp->root_dentry = nullptr; + + --mount_count_; + + // 如果是根挂载,清除 root_mount_ + if (root_mount_ == &mounts_[mp_index]) { + root_mount_ = nullptr; + extern void SetRootDentry(Dentry*); + SetRootDentry(nullptr); + } + + klog::Info("MountTable: unmounted '%s'\n", path); + return {}; +} + +auto MountTable::Lookup(const char* path) -> MountPoint* { + if (path == nullptr || path[0] != '/') { + return nullptr; + } + + MountPoint* best_match = nullptr; + size_t best_match_len = 0; + + for (size_t i = 0; i < kMaxMounts; ++i) { + if (!mounts_[i].active) { + continue; + } + + const char* mp_path = mounts_[i].mount_path; + if (mp_path == nullptr) { + continue; + } + + size_t mp_len = strlen(mp_path); + + // 检查路径是否以挂载路径开头 + if (strncmp(path, mp_path, mp_len) == 0) { + // 确保是完整匹配或下一个字符是 /,或者是根目录挂载 + char next_char = path[mp_len]; + if (next_char == '0円' || next_char == '/' || + (mp_len == 1 && mp_path[0] == '/')) { + // 选择最长的匹配 + if (mp_len> best_match_len) { + best_match = &mounts_[i]; + best_match_len = mp_len; + } + } + } + } + + return best_match; +} + +auto MountTable::GetRootDentry(MountPoint* mp) -> Dentry* { + if (mp == nullptr || !mp->active) { + return nullptr; + } + return mp->root_dentry; +} + +auto MountTable::IsMountPoint(const char* path) -> bool { + if (path == nullptr) { + return false; + } + + for (size_t i = 0; i < kMaxMounts; ++i) { + if (mounts_[i].active && mounts_[i].mount_path != nullptr && + strcmp(mounts_[i].mount_path, path) == 0) { + return true; + } + } + + return false; +} + +auto MountTable::GetRootMount() -> MountPoint* { return root_mount_; } + +auto GetMountTable() -> MountTable& { + static MountTable instance; + return instance; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/open.cpp b/src/filesystem/vfs/open.cpp new file mode 100644 index 000000000..dc69bfbae --- /dev/null +++ b/src/filesystem/vfs/open.cpp @@ -0,0 +1,121 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto Open(const char* path, uint32_t flags) -> Expected { + if (!GetVfsState().initialized) { + return std::unexpected(Error(ErrorCode::kFsNotMounted)); + } + + if (path == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // 查找或创建 dentry + auto lookup_result = Lookup(path); + Dentry* dentry = nullptr; + + if (!lookup_result.has_value()) { + // 文件不存在,检查是否需要创建 + if ((flags & kOCreate) == 0) { + return std::unexpected(lookup_result.error()); + } + + // 创建新文件 + // 找到父目录路径 + char parent_path[512]; + char file_name[256]; + const char* last_slash = strrchr(path, '/'); + if (last_slash == nullptr || last_slash == path) { + strncpy(parent_path, "/", sizeof(parent_path)); + strncpy(file_name, path[0] == '/' ? path + 1 : path, sizeof(file_name)); + } else { + size_t parent_len = last_slash - path; + if (parent_len>= sizeof(parent_path)) { + parent_len = sizeof(parent_path) - 1; + } + strncpy(parent_path, path, parent_len); + parent_path[parent_len] = '0円'; + strncpy(file_name, last_slash + 1, sizeof(file_name)); + } + file_name[sizeof(file_name) - 1] = '0円'; + + // 查找父目录 + auto parent_result = Lookup(parent_path); + if (!parent_result.has_value()) { + return std::unexpected(parent_result.error()); + } + + Dentry* parent_dentry = parent_result.value(); + if (parent_dentry->inode == nullptr || + parent_dentry->inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + // 创建文件 + // 创建文件 + if (parent_dentry->inode->ops == nullptr) { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + + auto create_result = parent_dentry->inode->ops->Create( + parent_dentry->inode, file_name, FileType::kRegular); + + // 创建 dentry + dentry = new Dentry(); + if (dentry == nullptr) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + strncpy(dentry->name, file_name, sizeof(dentry->name) - 1); + dentry->name[sizeof(dentry->name) - 1] = '0円'; + dentry->inode = create_result.value(); + AddChild(parent_dentry, dentry); + } else { + dentry = lookup_result.value(); + } + + if (dentry == nullptr || dentry->inode == nullptr) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + + // 检查打开模式 + if ((flags & kODirectory) != 0 && + dentry->inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + // 创建 File 对象 + File* file = new File(); + if (file == nullptr) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + file->inode = dentry->inode; + file->dentry = dentry; + file->offset = 0; + file->flags = flags; + + // 从文件系统获取 FileOps + if (file->inode != nullptr && file->inode->fs != nullptr) { + file->ops = file->inode->fs->GetFileOps(); + } + + // 处理 O_TRUNC + if ((flags & kOTruncate) != 0 && dentry->inode->type == FileType::kRegular) { + // 截断文件,由具体文件系统处理 + // 这里不直接操作,而是通过后续的 write 来处理 + } + + klog::Debug("VFS: opened '%s', flags=0x%x\n", path, flags); + return file; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/read.cpp b/src/filesystem/vfs/read.cpp new file mode 100644 index 000000000..0b3e0853f --- /dev/null +++ b/src/filesystem/vfs/read.cpp @@ -0,0 +1,24 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto Read(File* file, void* buf, size_t count) -> Expected { + if (file == nullptr || buf == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (file->ops == nullptr) { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + + return file->ops->Read(file, buf, count); +} + +} // namespace vfs diff --git a/src/filesystem/vfs/readdir.cpp b/src/filesystem/vfs/readdir.cpp new file mode 100644 index 000000000..c43598487 --- /dev/null +++ b/src/filesystem/vfs/readdir.cpp @@ -0,0 +1,27 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto ReadDir(File* file, DirEntry* dirent, size_t count) -> Expected { + if (file == nullptr || dirent == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (file->inode == nullptr || file->inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + if (file->ops == nullptr) { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + + return file->ops->ReadDir(file, dirent, count); +} +} // namespace vfs diff --git a/src/filesystem/vfs/rmdir.cpp b/src/filesystem/vfs/rmdir.cpp new file mode 100644 index 000000000..2e22946e5 --- /dev/null +++ b/src/filesystem/vfs/rmdir.cpp @@ -0,0 +1,81 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto RmDir(const char* path) -> Expected { + if (path == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // 解析父目录路径和目录名 + char parent_path[512]; + char dir_name[256]; + const char* last_slash = strrchr(path, '/'); + if (last_slash == nullptr || last_slash == path) { + strncpy(parent_path, "/", sizeof(parent_path)); + strncpy(dir_name, path[0] == '/' ? path + 1 : path, sizeof(dir_name)); + } else { + size_t parent_len = last_slash - path; + if (parent_len>= sizeof(parent_path)) { + parent_len = sizeof(parent_path) - 1; + } + strncpy(parent_path, path, parent_len); + parent_path[parent_len] = '0円'; + strncpy(dir_name, last_slash + 1, sizeof(dir_name)); + } + dir_name[sizeof(dir_name) - 1] = '0円'; + + // 查找父目录 + auto parent_result = Lookup(parent_path); + if (!parent_result.has_value()) { + return std::unexpected(parent_result.error()); + } + + Dentry* parent_dentry = parent_result.value(); + if (parent_dentry->inode == nullptr) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + + // 查找目标目录 + Dentry* target_dentry = FindChild(parent_dentry, dir_name); + if (target_dentry == nullptr) { + return std::unexpected(Error(ErrorCode::kFsFileNotFound)); + } + + if (target_dentry->inode == nullptr || + target_dentry->inode->type != FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsNotADirectory)); + } + + // 检查目录是否为空 + if (target_dentry->children != nullptr) { + return std::unexpected(Error(ErrorCode::kFsNotEmpty)); + } + + // 删除目录 + if (parent_dentry->inode->ops == nullptr) { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + + auto result = + parent_dentry->inode->ops->Rmdir(parent_dentry->inode, dir_name); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + + // 从父目录中移除 dentry + RemoveChild(parent_dentry, target_dentry); + delete target_dentry; + + klog::Debug("VFS: removed directory '%s'\n", path); + return {}; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/seek.cpp b/src/filesystem/vfs/seek.cpp new file mode 100644 index 000000000..fed5de300 --- /dev/null +++ b/src/filesystem/vfs/seek.cpp @@ -0,0 +1,56 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto Seek(File* file, int64_t offset, SeekWhence whence) -> Expected { + if (file == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + if (file->ops != nullptr) { + return file->ops->Seek(file, offset, whence); + } + + // 默认实现 + uint64_t new_offset = file->offset; + + switch (whence) { + case SeekWhence::kSet: + if (offset < 0) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + new_offset = static_cast(offset); + break; + case SeekWhence::kCur: + if (offset < 0 && static_cast(-offset)> file->offset) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + new_offset = + static_cast(static_cast(file->offset) + offset); + break; + case SeekWhence::kEnd: + if (file->inode == nullptr) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + if (offset < 0 && static_cast(-offset)> file->inode->size) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + new_offset = static_cast( + static_cast(file->inode->size) + offset); + break; + default: + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + file->offset = new_offset; + return new_offset; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/unlink.cpp b/src/filesystem/vfs/unlink.cpp new file mode 100644 index 000000000..bd8c26ace --- /dev/null +++ b/src/filesystem/vfs/unlink.cpp @@ -0,0 +1,80 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto Unlink(const char* path) -> Expected { + if (path == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // 解析父目录路径和文件名 + char parent_path[512]; + char file_name[256]; + const char* last_slash = strrchr(path, '/'); + if (last_slash == nullptr || last_slash == path) { + strncpy(parent_path, "/", sizeof(parent_path)); + strncpy(file_name, path[0] == '/' ? path + 1 : path, sizeof(file_name)); + } else { + size_t parent_len = last_slash - path; + if (parent_len>= sizeof(parent_path)) { + parent_len = sizeof(parent_path) - 1; + } + strncpy(parent_path, path, parent_len); + parent_path[parent_len] = '0円'; + strncpy(file_name, last_slash + 1, sizeof(file_name)); + } + file_name[sizeof(file_name) - 1] = '0円'; + + // 查找父目录 + auto parent_result = Lookup(parent_path); + if (!parent_result.has_value()) { + return std::unexpected(parent_result.error()); + } + + Dentry* parent_dentry = parent_result.value(); + if (parent_dentry->inode == nullptr) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + + // 查找目标文件 + Dentry* target_dentry = FindChild(parent_dentry, file_name); + if (target_dentry == nullptr) { + return std::unexpected(Error(ErrorCode::kFsFileNotFound)); + } + + if (target_dentry->inode == nullptr) { + return std::unexpected(Error(ErrorCode::kFsCorrupted)); + } + + // 不能删除目录 + if (target_dentry->inode->type == FileType::kDirectory) { + return std::unexpected(Error(ErrorCode::kFsIsADirectory)); + } + + // 删除文件 + if (parent_dentry->inode->ops == nullptr) { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + + auto result = + parent_dentry->inode->ops->Unlink(parent_dentry->inode, file_name); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + + // 从父目录中移除 dentry + RemoveChild(parent_dentry, target_dentry); + delete target_dentry; + + klog::Debug("VFS: unlinked '%s'\n", path); + return {}; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/vfs.cpp b/src/filesystem/vfs/vfs.cpp new file mode 100644 index 000000000..7155818d2 --- /dev/null +++ b/src/filesystem/vfs/vfs.cpp @@ -0,0 +1,133 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "vfs.hpp" + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "mount.hpp" +#include "singleton.hpp" +#include "sk_cstdio" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto GetVfsState() -> VfsState& { + static VfsState state; + return state; +} + +auto SkipLeadingSlashes(const char* path) -> const char* { + while (*path == '/') { + ++path; + } + return path; +} + +auto CopyPathComponent(const char* src, char* dst, size_t dst_size) -> size_t { + size_t i = 0; + while (*src != '0円' && *src != '/' && i < dst_size - 1) { + dst[i++] = *src++; + } + dst[i] = '0円'; + return i; +} + +auto FindChild(Dentry* parent, const char* name) -> Dentry* { + if (parent == nullptr || name == nullptr) { + return nullptr; + } + + Dentry* child = parent->children; + while (child != nullptr) { + if (strcmp(child->name, name) == 0) { + return child; + } + child = child->next_sibling; + } + return nullptr; +} + +auto AddChild(Dentry* parent, Dentry* child) -> void { + if (parent == nullptr || child == nullptr) { + return; + } + + child->parent = parent; + child->next_sibling = parent->children; + parent->children = child; +} + +auto RemoveChild(Dentry* parent, Dentry* child) -> void { + if (parent == nullptr || child == nullptr) { + return; + } + + Dentry** current = &parent->children; + while (*current != nullptr) { + if (*current == child) { + *current = child->next_sibling; + child->parent = nullptr; + child->next_sibling = nullptr; + return; + } + current = &(*current)->next_sibling; + } +} + +// Inode 构造函数实现 +Inode::Inode() + : ino(0), + type(FileType::kUnknown), + size(0), + permissions(0644), + link_count(1), + fs_private(nullptr), + fs(nullptr), + ops(nullptr) {} + +// Dentry 构造函数实现 +Dentry::Dentry() + : inode(nullptr), + parent(nullptr), + children(nullptr), + next_sibling(nullptr), + fs_private(nullptr) { + name[0] = '0円'; +} + +// File 构造函数实现 +File::File() + : inode(nullptr), dentry(nullptr), offset(0), flags(0), ops(nullptr) {} + +auto Init() -> Expected { + if (GetVfsState().initialized) { + return {}; + } + + klog::Info("VFS: initializing...\n"); + + // 初始化挂载表 + GetVfsState().mount_table = new MountTable(); + if (GetVfsState().mount_table == nullptr) { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + GetVfsState().initialized = true; + klog::Info("VFS: initialization complete\n"); + return {}; +} + +auto GetRootDentry() -> Dentry* { return GetVfsState().root_dentry; } + +// 内部接口:设置根 dentry +void SetRootDentry(Dentry* dentry) { GetVfsState().root_dentry = dentry; } + +// 内部接口:获取挂载表 +auto GetMountTableInternal() -> MountTable* { + return GetVfsState().mount_table; +} + +} // namespace vfs diff --git a/src/filesystem/vfs/vfs_internal.hpp b/src/filesystem/vfs/vfs_internal.hpp new file mode 100644 index 000000000..4c7cd495b --- /dev/null +++ b/src/filesystem/vfs/vfs_internal.hpp @@ -0,0 +1,56 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#ifndef SIMPLEKERNEL_SRC_FILESYSTEM_VFS_VFS_INTERNAL_HPP_ +#define SIMPLEKERNEL_SRC_FILESYSTEM_VFS_VFS_INTERNAL_HPP_ + +#include "mount.hpp" +#include "vfs.hpp" + +namespace vfs { + +// VFS 全局状态结构体 +struct VfsState { + bool initialized = false; + MountTable* mount_table = nullptr; + Dentry* root_dentry = nullptr; +}; + +// 获取 VFS 状态 +auto GetVfsState() -> VfsState&; + +/** + * @brief 跳过路径中的前导斜杠 + * @param path 输入路径 + * @return 跳过前导斜杠后的路径指针 + */ +auto SkipLeadingSlashes(const char* path) -> const char*; + +/** + * @brief 复制路径组件到缓冲区 + * @param src 源路径(当前位置) + * @param dst 目标缓冲区 + * @param dst_size 缓冲区大小 + * @return 复制的长度 + */ +auto CopyPathComponent(const char* src, char* dst, size_t dst_size) -> size_t; + +/** + * @brief 在 dentry 的子节点中查找指定名称 + */ +auto FindChild(Dentry* parent, const char* name) -> Dentry*; + +/** + * @brief 添加子 dentry + */ +auto AddChild(Dentry* parent, Dentry* child) -> void; + +/** + * @brief 从父 dentry 中移除子 dentry + */ +auto RemoveChild(Dentry* parent, Dentry* child) -> void; + +} // namespace vfs + +#endif /* SIMPLEKERNEL_SRC_FILESYSTEM_VFS_VFS_INTERNAL_HPP_ */ diff --git a/src/filesystem/vfs/write.cpp b/src/filesystem/vfs/write.cpp new file mode 100644 index 000000000..e8060a979 --- /dev/null +++ b/src/filesystem/vfs/write.cpp @@ -0,0 +1,29 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "filesystem.hpp" +#include "kernel_log.hpp" +#include "sk_cstring" +#include "vfs_internal.hpp" + +namespace vfs { + +auto Write(File* file, const void* buf, size_t count) -> Expected { + if (file == nullptr || buf == nullptr) { + return std::unexpected(Error(ErrorCode::kInvalidArgument)); + } + + // 检查写入权限 + if ((file->flags & kOWriteOnly) == 0 && (file->flags & kOReadWrite) == 0) { + return std::unexpected(Error(ErrorCode::kFsPermissionDenied)); + } + + if (file->ops == nullptr) { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + + return file->ops->Write(file, buf, count); +} + +} // namespace vfs diff --git a/src/include/expected.hpp b/src/include/expected.hpp index b17556420..12151ff19 100644 --- a/src/include/expected.hpp +++ b/src/include/expected.hpp @@ -54,6 +54,49 @@ enum class ErrorCode : uint64_t { kTaskKernelStackAllocationFailed = 0x705, kTaskNoChildFound = 0x706, kTaskInvalidPid = 0x707, + // Device 相关错误 (0x800 - 0x8FF) + kDeviceNotFound = 0x800, + kDeviceAlreadyOpen = 0x801, + kDeviceNotOpen = 0x802, + kDeviceReadFailed = 0x803, + kDeviceWriteFailed = 0x804, + kDeviceIoctlFailed = 0x805, + kDeviceMmapFailed = 0x806, + kDeviceNotSupported = 0x807, + kDeviceBusy = 0x808, + kDevicePermissionDenied = 0x809, + kDeviceInvalidOffset = 0x80A, + kDeviceBlockUnaligned = 0x80B, + kDeviceBlockOutOfRange = 0x80C, + kDeviceFlushFailed = 0x80D, + // 文件系统相关错误 (0xA00 - 0xAFF) + kFsFileNotFound = 0xA00, + kFsPermissionDenied = 0xA01, + kFsNotADirectory = 0xA02, + kFsIsADirectory = 0xA03, + kFsFileExists = 0xA04, + kFsNoSpace = 0xA05, + kFsMountFailed = 0xA06, + kFsUnmountFailed = 0xA07, + kFsInvalidPath = 0xA08, + kFsFdTableFull = 0xA09, + kFsInvalidFd = 0xA0A, + kFsNotMounted = 0xA0B, + kFsReadOnly = 0xA0C, + kFsCorrupted = 0xA0D, + kFsAlreadyMounted = 0xA0E, + kFsNotEmpty = 0xA0F, + // BlockDevice 相关错误 (0xB00 - 0xBFF) + kBlkDeviceNotFound = 0xB00, + kBlkReadFailed = 0xB01, + kBlkWriteFailed = 0xB02, + kBlkSectorOutOfRange = 0xB03, + // IrqChip 相关错误 (0xC00 - 0xCFF) + // IrqChip 相关错误 (0x900 - 0x9FF) + kIrqChipInvalidIrq = 0x900, + kIrqChipIrqNotEnabled = 0x901, + kIrqChipAffinityFailed = 0x902, + kIrqChipIpiTimeout = 0x903, // 通用错误 (0xF00 - 0xFFF) kInvalidArgument = 0xF00, kOutOfMemory = 0xF01, @@ -136,6 +179,82 @@ constexpr auto GetErrorMessage(ErrorCode code) -> const char* { return "No child process found"; case ErrorCode::kTaskInvalidPid: return "Invalid PID"; + case ErrorCode::kDeviceNotFound: + return "Device not found"; + case ErrorCode::kDeviceAlreadyOpen: + return "Device already open"; + case ErrorCode::kDeviceNotOpen: + return "Device not open"; + case ErrorCode::kDeviceReadFailed: + return "Device read failed"; + case ErrorCode::kDeviceWriteFailed: + return "Device write failed"; + case ErrorCode::kDeviceIoctlFailed: + return "Device ioctl failed"; + case ErrorCode::kDeviceMmapFailed: + return "Device mmap failed"; + case ErrorCode::kDeviceNotSupported: + return "Operation not supported by device"; + case ErrorCode::kDeviceBusy: + return "Device busy"; + case ErrorCode::kDevicePermissionDenied: + return "Device permission denied"; + case ErrorCode::kDeviceInvalidOffset: + return "Invalid device offset"; + case ErrorCode::kDeviceBlockUnaligned: + return "Block access not aligned to block size"; + case ErrorCode::kDeviceBlockOutOfRange: + return "Block number out of device range"; + case ErrorCode::kDeviceFlushFailed: + return "Device flush failed"; + case ErrorCode::kFsFileNotFound: + return "File not found"; + case ErrorCode::kFsPermissionDenied: + return "Filesystem permission denied"; + case ErrorCode::kFsNotADirectory: + return "Not a directory"; + case ErrorCode::kFsIsADirectory: + return "Is a directory"; + case ErrorCode::kFsFileExists: + return "File already exists"; + case ErrorCode::kFsNoSpace: + return "No space left on device"; + case ErrorCode::kFsMountFailed: + return "Mount failed"; + case ErrorCode::kFsUnmountFailed: + return "Unmount failed"; + case ErrorCode::kFsInvalidPath: + return "Invalid path"; + case ErrorCode::kFsFdTableFull: + return "File descriptor table full"; + case ErrorCode::kFsInvalidFd: + return "Invalid file descriptor"; + case ErrorCode::kFsNotMounted: + return "Filesystem not mounted"; + case ErrorCode::kFsReadOnly: + return "Read-only filesystem"; + case ErrorCode::kFsCorrupted: + return "Filesystem corrupted"; + case ErrorCode::kFsAlreadyMounted: + return "Filesystem already mounted"; + case ErrorCode::kFsNotEmpty: + return "Directory not empty"; + case ErrorCode::kBlkDeviceNotFound: + return "Block device not found"; + case ErrorCode::kBlkReadFailed: + return "Block read failed"; + case ErrorCode::kBlkWriteFailed: + return "Block write failed"; + case ErrorCode::kBlkSectorOutOfRange: + return "Sector out of range"; + case ErrorCode::kIrqChipInvalidIrq: + return "IRQ number out of controller range"; + case ErrorCode::kIrqChipIrqNotEnabled: + return "IRQ not enabled"; + case ErrorCode::kIrqChipAffinityFailed: + return "Failed to set IRQ CPU affinity"; + case ErrorCode::kIrqChipIpiTimeout: + return "IPI delivery timeout"; case ErrorCode::kInvalidArgument: return "Invalid argument"; case ErrorCode::kOutOfMemory: diff --git a/src/include/interrupt_base.h b/src/include/interrupt_base.h index 832fd0a5e..5853a41c6 100644 --- a/src/include/interrupt_base.h +++ b/src/include/interrupt_base.h @@ -57,6 +57,19 @@ class InterruptBase { * @return Expected 成功时返回 void,失败时返回错误 */ virtual auto BroadcastIpi() -> Expected = 0; + + /** + * @brief 注册外部中断处理函数 + * @param irq 外部中断号(平台相关: PLIC source_id / GIC INTID / APIC IRQ) + * @param cpu_id 目标 CPU 核心 ID,中断将路由到该核心 + * @param priority 中断优先级 + * @param handler 中断处理函数 + * @return Expected 成功时返回 void,失败时返回错误 + */ + virtual auto RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, + uint32_t priority, + InterruptFunc handler) + -> Expected = 0; }; #endif /* SIMPLEKERNEL_SRC_KERNEL_INCLUDE_INTERRUPT_BASE_H_ */ diff --git a/src/include/io.hpp b/src/include/io.hpp index ac9a73cb7..0522d832f 100644 --- a/src/include/io.hpp +++ b/src/include/io.hpp @@ -5,15 +5,9 @@ #ifndef SIMPLEKERNEL_SRC_INCLUDE_IO_HPP_ #define SIMPLEKERNEL_SRC_INCLUDE_IO_HPP_ -#include - -#include #include #include -#include "per_cpu.hpp" -#include "sk_cstdio" - namespace io { /** * @brief 从指定地址读数据 @@ -23,7 +17,7 @@ namespace io { */ template static __always_inline auto In(const uint64_t addr) -> T { - return *reinterpret_cast(addr); + return *reinterpret_cast(addr); } /** @@ -34,7 +28,7 @@ static __always_inline auto In(const uint64_t addr) -> T { */ template static __always_inline void Out(const uint64_t addr, const T data) { - *reinterpret_cast(addr) = data; + *reinterpret_cast(addr) = data; } } // namespace io diff --git a/src/include/io_buffer.hpp b/src/include/io_buffer.hpp new file mode 100644 index 000000000..566add739 --- /dev/null +++ b/src/include/io_buffer.hpp @@ -0,0 +1,83 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#ifndef SIMPLEKERNEL_SRC_INCLUDE_IO_BUFFER_HPP_ +#define SIMPLEKERNEL_SRC_INCLUDE_IO_BUFFER_HPP_ + +#include +#include +#include + +/** + * @brief RAII wrapper for dynamically allocated, aligned IO buffers. + * + * @pre None + * @post The buffer memory is correctly allocated and aligned. It will be freed + * upon destruction. + */ +class IoBuffer { + public: + /// Default alignment for IO buffers (e.g., page size) + static constexpr size_t kDefaultAlignment = 4096; + + /// @name 构造/析构函数 + /// @{ + IoBuffer() = default; + + /** + * @brief 构造函数 + * @param size 缓冲区大小 + * @param alignment 对齐要求 + * @pre size> 0, alignment 必须是 2 的幂 + * @post 缓冲区内存已正确分配并对齐 + */ + IoBuffer(size_t size, size_t alignment = kDefaultAlignment); + + /** + * @brief 析构函数 + */ + ~IoBuffer(); + /// @} + + /// @name 拷贝与移动控制 + /// @{ + // Disable copy + IoBuffer(const IoBuffer&) = delete; + auto operator=(const IoBuffer&) -> IoBuffer& = delete; + + // Enable move + IoBuffer(IoBuffer&& other) noexcept; + auto operator=(IoBuffer&& other) noexcept -> IoBuffer&; + /// @} + + /** + * @brief 获取缓冲区数据与大小 (只读) + * @return std::pair 数据指针与大小的 pair + * @pre None + * @post 返回指向缓冲区数据的常量指针与大小 + */ + [[nodiscard]] auto GetBuffer() const -> std::pair; + + /** + * @brief 获取缓冲区数据与大小 + * @return std::pair 数据指针与大小的 pair + * @pre None + * @post 返回指向缓冲区数据的指针与大小 + */ + [[nodiscard]] auto GetBuffer() -> std::pair; + + /** + * @brief 检查缓冲区是否有效 + * @return bool 有效则返回 true + * @pre None + * @post 返回缓冲区是否已分配且有效的状态 + */ + [[nodiscard]] auto IsValid() const -> bool; + + private: + /// 缓冲区数据与大小 + std::pair buffer_{nullptr, 0}; +}; + +#endif /* SIMPLEKERNEL_SRC_INCLUDE_IO_BUFFER_HPP_ */ diff --git a/src/include/kernel.h b/src/include/kernel.h index 2c7f681e5..a37e8475e 100644 --- a/src/include/kernel.h +++ b/src/include/kernel.h @@ -29,4 +29,6 @@ auto main(int argc, const char** argv) -> int; void MemoryInit(); void MemoryInitSMP(); +void DeviceInit(); + #endif /* SIMPLEKERNEL_SRC_INCLUDE_KERNEL_H_ */ diff --git a/src/include/kernel_fdt.hpp b/src/include/kernel_fdt.hpp index 9287ccc94..d36a7abf1 100644 --- a/src/include/kernel_fdt.hpp +++ b/src/include/kernel_fdt.hpp @@ -18,6 +18,9 @@ #pragma GCC diagnostic pop #endif +#include + +#include #include #include #include @@ -27,17 +30,56 @@ #include "kernel_log.hpp" #include "singleton.hpp" #include "sk_cassert" +#include "sk_cstring" /** - * fdt 相关 + * @brief FDT(Flattened Device Tree)解析器 + * + * 提供对 FDT 的解析功能,包括节点查找、属性读取、设备枚举等。 + * + * @pre FDT 数据必须是有效的 DTB 格式 + * @post 通过各 Get* 方法可获取设备树中的硬件信息 + * + * @note ForEachNode 和 ForEachCompatibleNode 是模板方法,保留在头文件中 + * @note compatible 属性是 stringlist 格式(多个以 '0円' 分隔的字符串), + * 回调接收完整的 compatible 数据和长度 */ class KernelFdt { public: - fdt_header* fdt_header_; + /** + * @brief 构造函数 + * @param header fdt 数据地址 + * @pre header 指向有效的 DTB 数据 + * @post fdt_header_ 已初始化并通过校验 + */ + explicit KernelFdt(uint64_t header) + : fdt_header_(reinterpret_cast(header)) { + ValidateFdtHeader().or_else([](auto&& err) { + klog::Err("KernelFdt init failed: %s\n", err.message()); + while (true) { + cpu_io::Pause(); + } + return Expected{}; + }); + + klog::Debug("Load dtb at [0x%X], size [0x%X]\n", fdt_header_, + fdt32_to_cpu(fdt_header_->totalsize)); + } + + /// @name 构造/析构函数 + /// @{ + KernelFdt() = default; + KernelFdt(const KernelFdt&) = default; + KernelFdt(KernelFdt&&) = default; + auto operator=(const KernelFdt&) -> KernelFdt& = default; + auto operator=(KernelFdt&&) -> KernelFdt& = default; + ~KernelFdt() = default; + /// @} /** - * 获取 core 数量 + * @brief 获取 CPU 核心数量 * @return Expected 成功返回核心数,失败返回错误 + * @pre fdt_header_ 不为空 */ [[nodiscard]] auto GetCoreCount() const -> Expected { sk_assert_msg(fdt_header_ != nullptr, "fdt_header_ is null"); @@ -52,8 +94,9 @@ class KernelFdt { } /** - * 判断 psci 信息 + * @brief 判断 PSCI 信息 * @return Expected 成功返回空,失败返回错误 + * @pre fdt_header_ 不为空 */ [[nodiscard]] auto CheckPSCI() const -> Expected { sk_assert_msg(fdt_header_ != nullptr, "fdt_header_ is null"); @@ -71,8 +114,10 @@ class KernelFdt { } /** - * 获取内存信息 + * @brief 获取内存信息 * @return Expected> 内存信息<地址,长度> + * @pre fdt_header_ 不为空 + * @post 返回第一个 reg 条目的 base 和 size */ [[nodiscard]] auto GetMemory() const -> Expected> { @@ -86,9 +131,10 @@ class KernelFdt { } /** - * 获取串口信息 + * @brief 获取串口信息 * @return Expected> - * <地址,长度,中断号> + * <地址,长度,中断号> + * @pre fdt_header_ 不为空 */ [[nodiscard]] auto GetSerial() const -> Expected> { @@ -108,7 +154,8 @@ class KernelFdt { const char* stdout_path = reinterpret_cast(prop->data); std::array path_buffer; - strncpy(path_buffer.data(), stdout_path, path_buffer.max_size()); + sk_std::strncpy(path_buffer.data(), stdout_path, + path_buffer.max_size() - 1); char* colon = strchr(path_buffer.data(), ':'); if (colon != nullptr) { @@ -130,21 +177,11 @@ class KernelFdt { return std::unexpected(Error(ErrorCode::kFdtNodeNotFound)); } - // Get reg property - prop = fdt_get_property(fdt_header_, stdout_offset, "reg", &len); - if (prop == nullptr) { - return std::unexpected(Error(ErrorCode::kFdtPropertyNotFound)); - } - - uint64_t base = 0; - size_t size = 0; - const auto* reg = reinterpret_cast(prop->data); - for (size_t i = 0; i < len / sizeof(uint64_t); i += 2) { - base = fdt64_to_cpu(reg[i]); - size = fdt64_to_cpu(reg[i + 1]); + auto reg = GetRegProperty(stdout_offset); + if (!reg.has_value()) { + return std::unexpected(reg.error()); } - // Get interrupts property prop = fdt_get_property(fdt_header_, stdout_offset, "interrupts", &len); if (prop == nullptr) { return std::unexpected(Error(ErrorCode::kFdtPropertyNotFound)); @@ -156,12 +193,13 @@ class KernelFdt { irq = fdt32_to_cpu(*interrupts); } - return std::tuple{base, size, irq}; + return std::tuple{reg.value().first, reg.value().second, irq}; } /** - * 获取 cpu 时钟 + * @brief 获取 CPU 时钟频率 * @return Expected 时钟频率 + * @pre fdt_header_ 不为空 */ [[nodiscard]] auto GetTimebaseFrequency() const -> Expected { sk_assert_msg(fdt_header_ != nullptr, "fdt_header_ is null"); @@ -183,9 +221,10 @@ class KernelFdt { } /** - * 获取 gic 信息 + * @brief 获取 GIC 信息 * @return Expected> - * + * + * @pre fdt_header_ 不为空 */ [[nodiscard]] auto GetGIC() const -> Expected> { @@ -222,7 +261,7 @@ class KernelFdt { } /** - * 获取 GIC Distributor 信息 + * @brief 获取 GIC Distributor 信息 * @return Expected> */ [[nodiscard]] auto GetGicDist() const @@ -234,7 +273,7 @@ class KernelFdt { } /** - * 获取 GIC CPU Interface (Redistributor) 信息 + * @brief 获取 GIC CPU Interface (Redistributor) 信息 * @return Expected> */ [[nodiscard]] auto GetGicCpu() const @@ -246,8 +285,10 @@ class KernelFdt { } /** - * 获取 aarch64 中断号 + * @brief 获取 aarch64 中断号 + * @param compatible 要查找的 compatible 字符串 * @return Expected intid + * @pre fdt_header_ 不为空 */ [[nodiscard]] auto GetAarch64Intid(const char* compatible) const -> Expected { @@ -291,15 +332,16 @@ class KernelFdt { } /** - * 获取 plic 信息 - * @return 内存信息<地址,长度,中断源数量,上下文数量> + * @brief 获取 PLIC 信息 + * @return Expected> + * <地址,长度,中断源数量,上下文数量> + * @pre fdt_header_ 不为空 * @see https://github.com/qemu/qemu/blob/master/hw/arm/virt.c */ [[nodiscard]] auto GetPlic() const -> Expected> { sk_assert_msg(fdt_header_ != nullptr, "fdt_header_ is null"); - // 查找 PLIC 节点,依次尝试两个 compatible 字符串 auto offset = FindCompatibleNode("sifive,plic-1.0.0"); if (!offset.has_value()) { offset = FindCompatibleNode("riscv,plic0"); @@ -310,20 +352,15 @@ class KernelFdt { int len = 0; - // 通过 interrupts-extended 字段计算上下文数量 const auto* prop = fdt_get_property(fdt_header_, offset.value(), "interrupts-extended", &len); if (prop == nullptr) { return std::unexpected(Error(ErrorCode::kFdtPropertyNotFound)); } - // interrupts-extended 格式: 成对出现 - // 每两个 uint32_t 值表示一个上下文 (CPU + 模式) uint32_t num_entries = len / sizeof(uint32_t); - // 每两个条目表示一个上下文 uint32_t context_count = num_entries / 2; - // 获取 ndev 属性 prop = fdt_get_property(fdt_header_, offset.value(), "riscv,ndev", &len); if (prop == nullptr) { return std::unexpected(Error(ErrorCode::kFdtPropertyNotFound)); @@ -331,7 +368,6 @@ class KernelFdt { uint32_t ndev = fdt32_to_cpu(*reinterpret_cast(prop->data)); - // 获取 reg 属性 auto reg = GetRegProperty(offset.value()); if (!reg.has_value()) { return std::unexpected(reg.error()); @@ -342,34 +378,162 @@ class KernelFdt { } /** - * 构造函数 - * @param header fdt 地址 + * @brief 遍历 FDT 中所有设备节点 + * @tparam Callback 回调类型,签名: + * bool(const char* node_name, const char* compatible_data, + * size_t compatible_len, uint64_t mmio_base, size_t mmio_size, + * uint32_t irq) + * 返回 true 继续遍历,false 停止 + * @param callback 节点处理函数 + * @return Expected + * @pre fdt_header_ 不为空 + * @note compatible_data 是完整的 stringlist(多个字符串以 '0円' 分隔), + * compatible_len 是整个 stringlist 的字节长度。 + * 若需要显示第一个 compatible 字符串,直接使用 compatible_data 即可。 + * 若需要遍历所有 compatible 字符串,需按 '0円' 分隔迭代。 */ - explicit KernelFdt(uint64_t header) - : fdt_header_(reinterpret_cast(header)) { - ValidateFdtHeader().or_else([](Error err) -> Expected { - klog::Err("KernelFdt init failed: %s\n", err.message()); - while (true) { - cpu_io::Pause(); + template + [[nodiscard]] auto ForEachNode(Callback&& callback) const -> Expected { + sk_assert_msg(fdt_header_ != nullptr, "fdt_header_ is null"); + + int offset = -1; + int depth = 0; + + while (true) { + offset = fdt_next_node(fdt_header_, offset, &depth); + if (offset < 0) { + if (offset == -FDT_ERR_NOTFOUND) { + break; + } + return std::unexpected(Error(ErrorCode::kFdtParseFailed)); } - return {}; - }); - klog::Debug("Load dtb at [0x%X], size [0x%X]\n", fdt_header_, - fdt32_to_cpu(fdt_header_->totalsize)); + const char* node_name = fdt_get_name(fdt_header_, offset, nullptr); + if (node_name == nullptr) { + continue; + } + + int status_len = 0; + const auto* status_prop = + fdt_get_property(fdt_header_, offset, "status", &status_len); + if (status_prop != nullptr) { + const char* status = reinterpret_cast(status_prop->data); + if (strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) { + continue; + } + } + + const char* compatible_data = nullptr; + size_t compatible_len = 0; + int compat_len = 0; + const auto* compat_prop = + fdt_get_property(fdt_header_, offset, "compatible", &compat_len); + if (compat_prop != nullptr && compat_len> 0) { + compatible_data = reinterpret_cast(compat_prop->data); + compatible_len = static_cast(compat_len); + } + + uint64_t mmio_base = 0; + size_t mmio_size = 0; + int reg_len = 0; + const auto* reg_prop = + fdt_get_property(fdt_header_, offset, "reg", ®_len); + if (reg_prop != nullptr && + static_cast(reg_len)>= 2 * sizeof(uint64_t)) { + const auto* reg = reinterpret_cast(reg_prop->data); + mmio_base = fdt64_to_cpu(reg[0]); + mmio_size = fdt64_to_cpu(reg[1]); + } + + uint32_t irq = 0; + int irq_len = 0; + const auto* irq_prop = + fdt_get_property(fdt_header_, offset, "interrupts", &irq_len); + if (irq_prop != nullptr && + static_cast(irq_len)>= sizeof(uint32_t)) { + const auto* interrupts = + reinterpret_cast(irq_prop->data); + irq = fdt32_to_cpu(interrupts[0]); + } + + if (!callback(node_name, compatible_data, compatible_len, mmio_base, + mmio_size, irq)) { + break; + } + } + + return {}; } - /// @name 构造/析构函数 - /// @{ - KernelFdt() = default; - KernelFdt(const KernelFdt&) = default; - KernelFdt(KernelFdt&&) = default; - auto operator=(const KernelFdt&) -> KernelFdt& = default; - auto operator=(KernelFdt&&) -> KernelFdt& = default; - ~KernelFdt() = default; - /// @} + /** + * @brief 遍历所有匹配指定 compatible 的 FDT 节点 + * @tparam Callback 回调类型,签名: + * bool(int offset, const char* node_name, + * uint64_t mmio_base, size_t mmio_size, uint32_t irq) + * 返回 true 继续遍历,false 停止 + * @param compatible 要匹配的 compatible 字符串 + * @param callback 节点处理函数 + * @return Expected + * @pre fdt_header_ 不为空 + * @note 使用 fdt_node_offset_by_compatible 的迭代模式, + * 可正确处理多个节点共享相同 compatible 的情况 + */ + template + [[nodiscard]] auto ForEachCompatibleNode(const char* compatible, + Callback&& callback) const + -> Expected { + sk_assert_msg(fdt_header_ != nullptr, "fdt_header_ is null"); + + int offset = -1; + while (true) { + offset = fdt_node_offset_by_compatible(fdt_header_, offset, compatible); + if (offset < 0) { + if (offset == -FDT_ERR_NOTFOUND) { + break; + } + return std::unexpected(Error(ErrorCode::kFdtParseFailed)); + } + + const char* node_name = fdt_get_name(fdt_header_, offset, nullptr); + if (node_name == nullptr) { + continue; + } + + uint64_t mmio_base = 0; + size_t mmio_size = 0; + int reg_len = 0; + const auto* reg_prop = + fdt_get_property(fdt_header_, offset, "reg", ®_len); + if (reg_prop != nullptr && + static_cast(reg_len)>= 2 * sizeof(uint64_t)) { + const auto* reg = reinterpret_cast(reg_prop->data); + mmio_base = fdt64_to_cpu(reg[0]); + mmio_size = fdt64_to_cpu(reg[1]); + } + + uint32_t irq = 0; + int irq_len = 0; + const auto* irq_prop = + fdt_get_property(fdt_header_, offset, "interrupts", &irq_len); + if (irq_prop != nullptr && + static_cast(irq_len)>= sizeof(uint32_t)) { + const auto* interrupts = + reinterpret_cast(irq_prop->data); + irq = fdt32_to_cpu(interrupts[0]); + } + + if (!callback(offset, node_name, mmio_base, mmio_size, irq)) { + break; + } + } + + return {}; + } private: + /// FDT header 指针 + fdt_header* fdt_header_{nullptr}; + /// PSCI 标准函数 ID(SMC64 调用约定) /// @see https://developer.arm.com/documentation/den0022/fb/?lang=en /// @note 高位 0xC4 表示 SMC64 快速调用,0x84 表示 SMC32 快速调用 @@ -378,7 +542,8 @@ class KernelFdt { static constexpr uint64_t kPsciCpuSuspendFuncId = 0xC4000001; /** - * 验证 FDT header + * @brief 验证 FDT header + * @return Expected */ [[nodiscard]] auto ValidateFdtHeader() const -> Expected { sk_assert_msg(fdt_header_ != nullptr, "fdt_header_ is null"); @@ -389,7 +554,9 @@ class KernelFdt { } /** - * 查找节点 + * @brief 按路径查找节点 + * @param path 节点路径(如 "/memory") + * @return Expected 节点偏移量 */ [[nodiscard]] auto FindNode(const char* path) const -> Expected { auto offset = fdt_path_offset(fdt_header_, path); @@ -400,7 +567,9 @@ class KernelFdt { } /** - * 按 compatible 查找节点 + * @brief 按 compatible 查找第一个匹配的节点 + * @param compatible 要查找的 compatible 字符串 + * @return Expected 节点偏移量 */ [[nodiscard]] auto FindCompatibleNode(const char* compatible) const -> Expected { @@ -412,7 +581,7 @@ class KernelFdt { } /** - * 根据 compatible 查找已启用的节点(跳过 status="disabled" 的节点) + * @brief 根据 compatible 查找已启用的节点(跳过 status="disabled") * @param compatible 要查找的 compatible 字符串 * @return Expected 节点偏移量 */ @@ -425,28 +594,26 @@ class KernelFdt { return std::unexpected(Error(ErrorCode::kFdtNodeNotFound)); } - // 检查 status 属性 int len = 0; const auto* status_prop = fdt_get_property(fdt_header_, offset, "status", &len); - // 如果没有 status 属性,默认为 okay if (status_prop == nullptr) { return offset; } - // 检查 status 是否为 "okay" 或 "ok" const char* status = reinterpret_cast(status_prop->data); if (strcmp(status, "okay") == 0 || strcmp(status, "ok") == 0) { return offset; } - - // 继续查找下一个匹配的节点 } } /** - * 获取 reg 属性 + * @brief 获取 reg 属性(返回第一个 reg 条目) + * @param offset 节点偏移量 + * @return Expected> base 和 size + * @post 返回第一个 对,而非最后一个 */ [[nodiscard]] auto GetRegProperty(int offset) const -> Expected> { @@ -456,18 +623,18 @@ class KernelFdt { return std::unexpected(Error(ErrorCode::kFdtPropertyNotFound)); } - uint64_t base = 0; - size_t size = 0; const auto* reg = reinterpret_cast(prop->data); - for (size_t i = 0; i < len / sizeof(uint64_t); i += 2) { - base = fdt64_to_cpu(reg[i]); - size = fdt64_to_cpu(reg[i + 1]); + if (static_cast(len) < 2 * sizeof(uint64_t)) { + return std::unexpected(Error(ErrorCode::kFdtInvalidPropertySize)); } + + uint64_t base = fdt64_to_cpu(reg[0]); + size_t size = fdt64_to_cpu(reg[1]); return std::pair{base, size}; } /** - * 按 device_type 统计节点数量 + * @brief 按 device_type 统计节点数量 * @param device_type 设备类型 * @return Expected 节点数量 */ @@ -499,7 +666,7 @@ class KernelFdt { } /** - * 获取 PSCI method 属性 + * @brief 获取 PSCI method 属性 * @param offset 节点偏移 * @return Expected method 字符串 */ @@ -513,7 +680,7 @@ class KernelFdt { } /** - * 验证 PSCI 函数 ID + * @brief 验证 PSCI 函数 ID * @param offset 节点偏移 * @return Expected 验证结果 */ diff --git a/src/include/mutex.hpp b/src/include/mutex.hpp index a29f780a6..26662b4c2 100644 --- a/src/include/mutex.hpp +++ b/src/include/mutex.hpp @@ -10,11 +10,8 @@ #include #include -#include "kernel_log.hpp" #include "resource_id.hpp" -#include "singleton.hpp" #include "task_control_block.hpp" -#include "task_manager.hpp" /** * @brief 互斥锁(Mutex) @@ -44,42 +41,7 @@ class Mutex { * @return true 成功获取锁 * @return false 失败(如递归获取) */ - auto Lock() -> bool { - auto current_task = Singleton::GetInstance().GetCurrentTask(); - if (current_task == nullptr) { - klog::Err("Mutex::Lock: Cannot lock mutex '%s' outside task context\n", - name_); - return false; - } - - Pid current_pid = current_task->pid; - - // 检查是否递归获取锁 - if (IsLockedByCurrentTask()) { - klog::Warn("Mutex::Lock: Task %zu tried to recursively lock mutex '%s'\n", - current_pid, name_); - return false; - } - - // 尝试获取锁 - bool expected = false; - while (!locked_.compare_exchange_weak( - expected, true, std::memory_order_acquire, std::memory_order_relaxed)) { - // 锁被占用,阻塞当前任务 - klog::Debug("Mutex::Lock: Task %zu blocking on mutex '%s'\n", current_pid, - name_); - Singleton::GetInstance().Block(resource_id_); - - // 被唤醒后重新尝试 - expected = false; - } - - // 成功获取锁,记录所有者 - owner_.store(current_pid, std::memory_order_release); - klog::Debug("Mutex::Lock: Task %zu acquired mutex '%s'\n", current_pid, - name_); - return true; - } + auto Lock() -> bool; /** * @brief 释放锁 @@ -90,37 +52,7 @@ class Mutex { * @return true 成功释放锁 * @return false 失败(如当前线程未持有锁) */ - auto UnLock() -> bool { - auto current_task = Singleton::GetInstance().GetCurrentTask(); - if (current_task == nullptr) { - klog::Err( - "Mutex::UnLock: Cannot unlock mutex '%s' outside task context\n", - name_); - return false; - } - - Pid current_pid = current_task->pid; - - // 检查是否由持有锁的任务释放 - if (!IsLockedByCurrentTask()) { - klog::Warn( - "Mutex::UnLock: Task %zu tried to unlock mutex '%s' it doesn't own\n", - current_pid, name_); - return false; - } - - // 释放锁 - owner_.store(std::numeric_limits::max(), std::memory_order_release); - locked_.store(false, std::memory_order_release); - - klog::Debug("Mutex::UnLock: Task %zu released mutex '%s'\n", current_pid, - name_); - - // 唤醒等待此锁的任务 - Singleton::GetInstance().Wakeup(resource_id_); - - return true; - } + auto UnLock() -> bool; /** * @brief 尝试获取锁(非阻塞) @@ -130,57 +62,14 @@ class Mutex { * @return true 成功获取锁 * @return false 锁不可用或递归获取 */ - auto TryLock() -> bool { - auto current_task = Singleton::GetInstance().GetCurrentTask(); - if (current_task == nullptr) { - klog::Err( - "Mutex::TryLock: Cannot trylock mutex '%s' outside task context\n", - name_); - return false; - } - - Pid current_pid = current_task->pid; - - // 检查是否递归获取锁 - if (IsLockedByCurrentTask()) { - klog::Debug( - "Mutex::TryLock: Task %zu tried to recursively trylock mutex '%s'\n", - current_pid, name_); - return false; - } - - // 尝试获取锁(非阻塞) - bool expected = false; - if (locked_.compare_exchange_strong(expected, true, - std::memory_order_acquire, - std::memory_order_relaxed)) { - // 成功获取锁,记录所有者 - owner_.store(current_pid, std::memory_order_release); - klog::Debug("Mutex::TryLock: Task %zu acquired mutex '%s'\n", current_pid, - name_); - return true; - } - - // 锁被占用 - klog::Debug("Mutex::TryLock: Task %zu failed to acquire mutex '%s'\n", - current_pid, name_); - return false; - } + auto TryLock() -> bool; /** * @brief 检查锁是否被当前线程持有 * @return true 当前线程持有锁 * @return false 当前线程未持有锁 */ - auto IsLockedByCurrentTask() const -> bool { - auto current_task = Singleton::GetInstance().GetCurrentTask(); - if (current_task == nullptr) { - return false; - } - - return locked_.load(std::memory_order_acquire) && - owner_.load(std::memory_order_acquire) == current_task->pid; - } + auto IsLockedByCurrentTask() const -> bool; /** * @brief 构造函数 diff --git a/src/include/singleton.hpp b/src/include/singleton.hpp index 464e29f0e..e209f2d26 100644 --- a/src/include/singleton.hpp +++ b/src/include/singleton.hpp @@ -5,7 +5,7 @@ #ifndef SIMPLEKERNEL_SRC_INCLUDE_SINGLETON_HPP_ #define SIMPLEKERNEL_SRC_INCLUDE_SINGLETON_HPP_ -// 单例模板类 +/// 单例模板类 template class Singleton { public: diff --git a/src/include/spinlock.hpp b/src/include/spinlock.hpp index 5afa61a6e..4cb69e66e 100644 --- a/src/include/spinlock.hpp +++ b/src/include/spinlock.hpp @@ -33,7 +33,7 @@ class SpinLock { * @brief 获得锁 * @return Expected 成功返回空值,失败返回错误 */ - __always_inline auto Lock() -> Expected { + [[nodiscard]] __always_inline auto Lock() -> Expected { auto intr_enable = cpu_io::GetInterruptStatus(); cpu_io::DisableInterrupt(); @@ -62,7 +62,7 @@ class SpinLock { * @brief 释放锁 * @return Expected 成功返回空值,失败返回错误 */ - __always_inline auto UnLock() -> Expected { + [[nodiscard]] __always_inline auto UnLock() -> Expected { if (!IsLockedByCurrentCore()) { sk_printf("spinlock %s IsLockedByCurrentCore == false.\n", name_); return std::unexpected(Error{ErrorCode::kSpinLockNotOwned}); @@ -110,8 +110,9 @@ class SpinLock { * @return false 否 */ __always_inline auto IsLockedByCurrentCore() -> bool { - return locked_.test() && (core_id_.load(std::memory_order_acquire) == - cpu_io::GetCurrentCoreId()); + return locked_.test(std::memory_order_acquire) && + (core_id_.load(std::memory_order_acquire) == + cpu_io::GetCurrentCoreId()); } }; @@ -120,10 +121,10 @@ class SpinLock { * @tparam Mutex 锁类型,必须有返回 Expected 的 Lock() 和 UnLock() 方法 */ template -requires requires(Mutex& m) { - { m.Lock() } -> std::same_as>; - { m.UnLock() } -> std::same_as>; -} + requires requires(Mutex& m) { + { m.Lock() } -> std::same_as>; + { m.UnLock() } -> std::same_as>; + } class LockGuard { public: using mutex_type = Mutex; @@ -133,19 +134,27 @@ class LockGuard { * @param mutex 要保护的锁对象 */ explicit LockGuard(mutex_type& mutex) : mutex_(mutex) { - if (auto result = mutex_.Lock(); !result) { - sk_printf("LockGuard: Failed to acquire lock: %s\n", - result.error().message()); + mutex_.Lock().or_else([](auto&& err) { + sk_printf("LockGuard: Failed to acquire lock: %s\n", err.message()); while (true) { cpu_io::Pause(); } - } + return Expected{}; + }); } /** * @brief 析构函数,自动释放锁 */ - ~LockGuard() { mutex_.UnLock(); } + ~LockGuard() { + mutex_.UnLock().or_else([](auto&& err) { + sk_printf("LockGuard: Failed to release lock: %s\n", err.message()); + while (true) { + cpu_io::Pause(); + } + return Expected{}; + }); + } LockGuard(const LockGuard&) = delete; LockGuard(LockGuard&&) = delete; diff --git a/src/include/virtual_memory.hpp b/src/include/virtual_memory.hpp index f093d44c2..a6d2c78d3 100644 --- a/src/include/virtual_memory.hpp +++ b/src/include/virtual_memory.hpp @@ -1,23 +1,16 @@ /** - * Copyright The SimpleKernel Contributors + * @copyright Copyright The SimpleKernel Contributors */ -#ifndef SIMPLEKERNEL_SRC_MEMORY_INCLUDE_VIRTUAL_MEMORY_HPP_ -#define SIMPLEKERNEL_SRC_MEMORY_INCLUDE_VIRTUAL_MEMORY_HPP_ +#ifndef SIMPLEKERNEL_SRC_INCLUDE_VIRTUAL_MEMORY_HPP_ +#define SIMPLEKERNEL_SRC_INCLUDE_VIRTUAL_MEMORY_HPP_ #include -#include #include #include -#include -#include "basic_info.hpp" #include "expected.hpp" -#include "kernel_log.hpp" -#include "singleton.hpp" -#include "sk_cassert" -#include "sk_stdlib.h" /** * @brief 虚拟内存管理抽象基类 @@ -26,34 +19,12 @@ */ class VirtualMemory { public: - VirtualMemory() { - // 分配根页表目录 - kernel_page_dir_ = aligned_alloc(cpu_io::virtual_memory::kPageSize, - cpu_io::virtual_memory::kPageSize); - sk_assert_msg(kernel_page_dir_ != nullptr, - "Failed to allocate kernel page directory"); - - // 清零页表目录 - std::memset(kernel_page_dir_, 0, cpu_io::virtual_memory::kPageSize); - - // 获取内核基本信息 - const auto& basic_info = Singleton::GetInstance(); - - // 映射全部物理内存 - MapMMIO(basic_info.physical_memory_addr, basic_info.physical_memory_size) - .or_else([](auto&& err) -> Expected { - klog::Err("Failed to map kernel memory: %s", err.message()); - while (true) { - cpu_io::Pause(); - } - return {}; - }); - - klog::Info( - "Kernel memory mapped from 0x%lX to 0x%lX\n", - basic_info.physical_memory_addr, - basic_info.physical_memory_addr + basic_info.physical_memory_size); - } + /** + * @brief 构造函数 + * @post 内核页表目录已分配并清零 + * @post 全部物理内存已映射 + */ + VirtualMemory(); /// @name 构造/析构函数 /// @{ @@ -64,12 +35,12 @@ class VirtualMemory { ~VirtualMemory() = default; /// @} - void InitCurrentCore() const { - cpu_io::virtual_memory::SetPageDirectory( - reinterpret_cast(kernel_page_dir_)); - // 开启分页 - cpu_io::virtual_memory::EnablePage(); - } + /** + * @brief 初始化当前核心的页表 + * @pre 内核页表目录已初始化 + * @post 当前核心的页表目录已设置并启用分页 + */ + void InitCurrentCore() const; /** * @brief 映射设备内存 (MMIO) @@ -81,22 +52,7 @@ class VirtualMemory { auto MapMMIO( uint64_t phys_addr, size_t size, uint32_t flags = cpu_io::virtual_memory::GetKernelPagePermissions()) - -> Expected { - // 计算对齐后的起始和结束页 - auto start_page = cpu_io::virtual_memory::PageAlign(phys_addr); - auto end_page = cpu_io::virtual_memory::PageAlignUp(phys_addr + size); - - // 遍历并映射 - for (uint64_t addr = start_page; addr < end_page; - addr += cpu_io::virtual_memory::kPageSize) { - auto result = MapPage(kernel_page_dir_, reinterpret_cast(addr), - reinterpret_cast(addr), flags); - if (!result.has_value()) { - return std::unexpected(result.error()); - } - } - return reinterpret_cast(phys_addr); - } + -> Expected; /** * @brief 映射单个页面 @@ -107,42 +63,7 @@ class VirtualMemory { * @return Expected 成功时返回 void,失败时返回错误 */ auto MapPage(void* page_dir, void* virtual_addr, void* physical_addr, - uint32_t flags) -> Expected { - sk_assert_msg(page_dir != nullptr, "MapPage: page_dir is null"); - - // 查找页表项,如果不存在则分配 - auto pte_result = FindPageTableEntry(page_dir, virtual_addr, true); - if (!pte_result.has_value()) { - return std::unexpected(Error(ErrorCode::kVmMapFailed)); - } - - auto pte = pte_result.value(); - - // 检查是否已经映射且标志位相同 - if (cpu_io::virtual_memory::IsPageTableEntryValid(*pte)) { - // 如果物理地址和标志位都相同,则认为是重复映射(警告但不失败) - auto existing_pa = cpu_io::virtual_memory::PageTableEntryToPhysical(*pte); - if (existing_pa == reinterpret_cast(physical_addr) && - (*pte & ((1ULL << cpu_io::virtual_memory::kPteAttributeBits) - 1)) == - flags) { - klog::Debug( - "MapPage: duplicate va = %p, pa = 0x%lX, flags = 0x%X, skip\n", - virtual_addr, existing_pa, flags); - // 重复映射,但不是错误 - return {}; - } - klog::Warn("MapPage: remap va = %p from pa = 0x%lX to pa = %p\n", - virtual_addr, existing_pa, physical_addr); - } - - // 设置页表项 - *pte = cpu_io::virtual_memory::PhysicalToPageTableEntry( - reinterpret_cast(physical_addr), flags); - // 刷新 TLB - cpu_io::virtual_memory::FlushTLBAll(); - - return {}; - } + uint32_t flags) -> Expected; /** * @brief 取消映射单个页面 @@ -150,28 +71,7 @@ class VirtualMemory { * @param virtual_addr 虚拟地址 * @return Expected 成功时返回 void,失败时返回错误 */ - auto UnmapPage(void* page_dir, void* virtual_addr) -> Expected { - sk_assert_msg(page_dir != nullptr, "UnmapPage: page_dir is null"); - - auto pte_result = FindPageTableEntry(page_dir, virtual_addr, false); - if (!pte_result.has_value()) { - return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); - } - - auto pte = pte_result.value(); - - if (!cpu_io::virtual_memory::IsPageTableEntryValid(*pte)) { - return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); - } - - // 清除页表项 - *pte = 0; - - // 刷新 TLB - cpu_io::virtual_memory::FlushTLBAll(); - - return {}; - } + auto UnmapPage(void* page_dir, void* virtual_addr) -> Expected; /** * @brief 获取虚拟地址对应的物理地址映射 @@ -180,23 +80,7 @@ class VirtualMemory { * @return Expected 物理地址,失败时返回错误 */ [[nodiscard]] auto GetMapping(void* page_dir, void* virtual_addr) - -> Expected { - sk_assert_msg(page_dir != nullptr, "GetMapping: page_dir is null"); - - auto pte_result = FindPageTableEntry(page_dir, virtual_addr, false); - if (!pte_result.has_value()) { - return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); - } - - auto pte = pte_result.value(); - - if (!cpu_io::virtual_memory::IsPageTableEntryValid(*pte)) { - return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); - } - - return reinterpret_cast( - cpu_io::virtual_memory::PageTableEntryToPhysical(*pte)); - } + -> Expected; /** * @brief 回收页表,释放所有映射和子页表 @@ -204,21 +88,7 @@ class VirtualMemory { * @param free_pages 是否同时释放映射的物理页 * @note 此函数会递归释放所有层级的页表 */ - void DestroyPageDirectory(void* page_dir, bool free_pages = false) { - if (page_dir == nullptr) { - return; - } - - // 递归释放所有层级的页表 - RecursiveFreePageTable(reinterpret_cast(page_dir), - cpu_io::virtual_memory::kPageTableLevels - 1, - free_pages); - - // 释放根页表目录本身 - aligned_free(page_dir); - - klog::Debug("Destroyed page directory at address: %p\n", page_dir); - } + void DestroyPageDirectory(void* page_dir, bool free_pages = false); /** * @brief 复制页表 @@ -229,35 +99,7 @@ class VirtualMemory { * 如果为 false,只复制页表结构,不复制最后一级的映射 */ auto ClonePageDirectory(void* src_page_dir, bool copy_mappings = true) - -> Expected { - sk_assert_msg(src_page_dir != nullptr, - "ClonePageDirectory: source page directory is nullptr"); - - // 创建新的页表目录 - auto dst_page_dir = aligned_alloc(cpu_io::virtual_memory::kPageSize, - cpu_io::virtual_memory::kPageSize); - if (dst_page_dir == nullptr) { - return std::unexpected(Error(ErrorCode::kVmAllocationFailed)); - } - - // 清零新页表 - std::memset(dst_page_dir, 0, cpu_io::virtual_memory::kPageSize); - - // 递归复制页表 - auto result = RecursiveClonePageTable( - reinterpret_cast(src_page_dir), - reinterpret_cast(dst_page_dir), - cpu_io::virtual_memory::kPageTableLevels - 1, copy_mappings); - if (!result.has_value()) { - // 复制失败,清理已分配的页表 - DestroyPageDirectory(dst_page_dir, false); - return std::unexpected(result.error()); - } - - klog::Debug("Cloned page directory from %p to %p\n", src_page_dir, - dst_page_dir); - return dst_page_dir; - } + -> Expected; private: void* kernel_page_dir_ = nullptr; @@ -271,38 +113,7 @@ class VirtualMemory { * @param level 当前层级 * @param free_pages 是否释放物理页 */ - void RecursiveFreePageTable(uint64_t* table, size_t level, bool free_pages) { - if (table == nullptr) { - return; - } - - // 遍历页表中的所有条目 - for (size_t i = 0; i < kEntriesPerTable; ++i) { - uint64_t pte = table[i]; - if (!cpu_io::virtual_memory::IsPageTableEntryValid(pte)) { - continue; - } - - auto pa = cpu_io::virtual_memory::PageTableEntryToPhysical(pte); - - // 如果不是最后一级,递归释放子页表 - if (level> 0) { - RecursiveFreePageTable(reinterpret_cast(pa), level - 1, - free_pages); - } else if (free_pages) { - // 最后一级页表,释放物理页 - aligned_free(reinterpret_cast(pa)); - } - - // 清除页表项 - table[i] = 0; - } - - // 如果不是根页表,释放当前页表 - if (level < cpu_io::virtual_memory::kPageTableLevels - 1) { - aligned_free(table); - } - } + void RecursiveFreePageTable(uint64_t* table, size_t level, bool free_pages); /** * @brief 递归复制页表 @@ -314,58 +125,7 @@ class VirtualMemory { */ auto RecursiveClonePageTable(uint64_t* src_table, uint64_t* dst_table, size_t level, bool copy_mappings) - -> Expected { - sk_assert_msg(src_table != nullptr, - "RecursiveClonePageTable: src_table is null"); - sk_assert_msg(dst_table != nullptr, - "RecursiveClonePageTable: dst_table is null"); - - for (size_t i = 0; i < kEntriesPerTable; ++i) { - uint64_t src_pte = src_table[i]; - if (!cpu_io::virtual_memory::IsPageTableEntryValid(src_pte)) { - continue; - } - - if (level> 0) { - // 非最后一级,需要递归复制子页表 - auto src_pa = cpu_io::virtual_memory::PageTableEntryToPhysical(src_pte); - auto* src_next_table = reinterpret_cast(src_pa); - - // 分配新的子页表 - auto* dst_next_table = aligned_alloc(cpu_io::virtual_memory::kPageSize, - cpu_io::virtual_memory::kPageSize); - if (dst_next_table == nullptr) { - return std::unexpected(Error(ErrorCode::kVmAllocationFailed)); - } - - // 清零新页表 - std::memset(dst_next_table, 0, cpu_io::virtual_memory::kPageSize); - - // 递归复制子页表 - auto result = RecursiveClonePageTable( - src_next_table, reinterpret_cast(dst_next_table), - level - 1, copy_mappings); - if (!result.has_value()) { - aligned_free(dst_next_table); - return std::unexpected(result.error()); - } - - // 设置目标页表项指向新的子页表 - dst_table[i] = cpu_io::virtual_memory::PhysicalToPageTableEntry( - reinterpret_cast(dst_next_table), - cpu_io::virtual_memory::GetTableEntryPermissions()); - } else { - // 最后一级页表 - if (copy_mappings) { - // 直接复制页表项(共享物理页) - dst_table[i] = src_pte; - } - // 如果不复制映射,保持目标页表项为 0 - } - } - - return {}; - } + -> Expected; /** * @brief 在页表中查找虚拟地址对应的页表项 @@ -376,48 +136,7 @@ class VirtualMemory { */ [[nodiscard]] auto FindPageTableEntry(void* page_dir, void* virtual_addr, bool allocate = false) - -> Expected { - auto* current_table = reinterpret_cast(page_dir); - auto vaddr = reinterpret_cast(virtual_addr); - - // 遍历页表层级 - for (size_t level = cpu_io::virtual_memory::kPageTableLevels - 1; level> 0; - --level) { - // 获取当前级别的虚拟页号 - auto vpn = cpu_io::virtual_memory::GetVirtualPageNumber(vaddr, level); - auto* pte = ¤t_table[vpn]; - if (cpu_io::virtual_memory::IsPageTableEntryValid(*pte)) { - // 页表项有效,获取下一级页表 - current_table = reinterpret_cast( - cpu_io::virtual_memory::PageTableEntryToPhysical(*pte)); - } else { - // 页表项无效 - if (allocate) { - auto* new_table = aligned_alloc(cpu_io::virtual_memory::kPageSize, - cpu_io::virtual_memory::kPageSize); - if (new_table == nullptr) { - return std::unexpected(Error(ErrorCode::kVmAllocationFailed)); - } - // 清零新页表 - std::memset(new_table, 0, cpu_io::virtual_memory::kPageSize); - - // 设置中间页表项 - *pte = cpu_io::virtual_memory::PhysicalToPageTableEntry( - reinterpret_cast(new_table), - cpu_io::virtual_memory::GetTableEntryPermissions()); - - current_table = reinterpret_cast(new_table); - } else { - return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); - } - } - } - - // 返回最底层页表中的页表项 - auto vpn = cpu_io::virtual_memory::GetVirtualPageNumber(vaddr, 0); - - return ¤t_table[vpn]; - } + -> Expected; }; -#endif /* SIMPLEKERNEL_SRC_MEMORY_INCLUDE_VIRTUAL_MEMORY_HPP_ */ +#endif /* SIMPLEKERNEL_SRC_INCLUDE_VIRTUAL_MEMORY_HPP_ */ diff --git a/src/io_buffer.cpp b/src/io_buffer.cpp new file mode 100644 index 000000000..f6c1f0065 --- /dev/null +++ b/src/io_buffer.cpp @@ -0,0 +1,57 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "io_buffer.hpp" + +#include "kernel_log.hpp" +#include "sk_stdlib.h" + +IoBuffer::IoBuffer(size_t size, size_t alignment) { + if (size == 0 || alignment == 0 || (alignment & (alignment - 1)) != 0) { + klog::Err("IoBuffer: invalid size (%lu) or alignment (%lu)\n", size, + alignment); + buffer_ = {nullptr, 0}; + return; + } + + // Use aligned_alloc provided by bmalloc / sk_stdlib.h + uint8_t* data = static_cast(aligned_alloc(alignment, size)); + if (data == nullptr) { + klog::Err("IoBuffer: aligned_alloc failed for size %lu, alignment %lu\n", + size, alignment); + buffer_ = {nullptr, 0}; + } else { + buffer_ = {data, size}; + } +} + +IoBuffer::~IoBuffer() { + if (buffer_.first != nullptr) { + aligned_free(buffer_.first); + buffer_ = {nullptr, 0}; + } +} + +IoBuffer::IoBuffer(IoBuffer&& other) noexcept : buffer_(other.buffer_) { + other.buffer_ = {nullptr, 0}; +} + +auto IoBuffer::operator=(IoBuffer&& other) noexcept -> IoBuffer& { + if (this != &other) { + if (buffer_.first != nullptr) { + aligned_free(buffer_.first); + } + buffer_ = other.buffer_; + other.buffer_ = {nullptr, 0}; + } + return *this; +} + +auto IoBuffer::GetBuffer() const -> std::pair { + return {buffer_.first, buffer_.second}; +} + +auto IoBuffer::GetBuffer() -> std::pair { return buffer_; } + +auto IoBuffer::IsValid() const -> bool { return buffer_.first != nullptr; } diff --git a/src/libc/include/sk_string.h b/src/libc/include/sk_string.h index 5f5544fbc..9520384a0 100644 --- a/src/libc/include/sk_string.h +++ b/src/libc/include/sk_string.h @@ -12,46 +12,46 @@ extern "C" { #endif // 复制内存块 -void *memcpy(void *dest, const void *src, size_t n); +void* memcpy(void* dest, const void* src, size_t n); // 复制内存块,可以处理重叠区域。 -void *memmove(void *dest, const void *src, size_t n); +void* memmove(void* dest, const void* src, size_t n); // 设置内存块 -void *memset(void *dest, int val, size_t n); +void* memset(void* dest, int val, size_t n); // 比较内存块 -int memcmp(const void *str1, const void *str2, size_t n); +int memcmp(const void* str1, const void* str2, size_t n); // 在内存块中查找字符 -void *memchr(const void *str, int c, size_t n); +void* memchr(const void* str, int c, size_t n); // 复制字符串 -char *strcpy(char *dest, const char *src); +char* strcpy(char* dest, const char* src); // 复制指定长度的字符串 -char *strncpy(char *dest, const char *src, size_t n); +char* strncpy(char* dest, const char* src, size_t n); // 连接字符串 -char *strcat(char *dest, const char *src); +char* strcat(char* dest, const char* src); // 比较字符串 -int strcmp(const char *s1, const char *s2); +int strcmp(const char* s1, const char* s2); // 比较指定长度的字符串 -int strncmp(const char *s1, const char *s2, size_t n); +int strncmp(const char* s1, const char* s2, size_t n); // 获取字符串长度 -size_t strlen(const char *s); +size_t strlen(const char* s); // 获取指定字符串长度 -size_t strnlen(const char *s, size_t n); +size_t strnlen(const char* s, size_t n); // 查找字符在字符串中的首次出现 -char *strchr(const char *s, int c); +char* strchr(const char* s, int c); // 反向查找字符在字符串中的首次出现 -char *strrchr(const char *s, int c); +char* strrchr(const char* s, int c); #ifdef __cplusplus } diff --git a/src/libc/sk_stdlib.c b/src/libc/sk_stdlib.c index 861a6f203..4a4b9d831 100644 --- a/src/libc/sk_stdlib.c +++ b/src/libc/sk_stdlib.c @@ -19,17 +19,14 @@ extern "C" { uint64_t __stack_chk_guard = 0x595E9FBD94FDA766; /// 栈保护检查失败后进入死循环 -__attribute__((noreturn)) void __stack_chk_fail() { - while (true) - ; -} +__attribute__((noreturn)) void __stack_chk_fail() { while (true); } // Internal helper for string to number conversion // Parses magnitude into unsigned long long. // handles base detection, whitespace, signs. -static unsigned long long strtox_main(const char *nptr, char **endptr, int base, - int *sign_out, int *overflow) { - const char *s = nptr; +static unsigned long long strtox_main(const char* nptr, char** endptr, int base, + int* sign_out, int* overflow) { + const char* s = nptr; unsigned long long acc = 0; int c; unsigned long long cutoff; @@ -69,7 +66,7 @@ static unsigned long long strtox_main(const char *nptr, char **endptr, int base, } if (base < 2 || base> 36) { - if (endptr) *endptr = (char *)nptr; // Invalid base + if (endptr) *endptr = (char*)nptr; // Invalid base return 0; } @@ -102,12 +99,12 @@ static unsigned long long strtox_main(const char *nptr, char **endptr, int base, // Set endptr if (endptr) { - *endptr = (char *)(any ? s : nptr); + *endptr = (char*)(any ? s : nptr); } return acc; } -unsigned long long int strtoull(const char *nptr, char **endptr, int base) { +unsigned long long int strtoull(const char* nptr, char** endptr, int base) { int negative; int overflow; unsigned long long acc = @@ -117,7 +114,7 @@ unsigned long long int strtoull(const char *nptr, char **endptr, int base) { return negative ? -acc : acc; } -long long int strtoll(const char *nptr, char **endptr, int base) { +long long int strtoll(const char* nptr, char** endptr, int base) { int negative; int overflow; unsigned long long acc = @@ -136,7 +133,7 @@ long long int strtoll(const char *nptr, char **endptr, int base) { } } -long int strtol(const char *nptr, char **endptr, int base) { +long int strtol(const char* nptr, char** endptr, int base) { long long int val = strtoll(nptr, endptr, base); #if LONG_MAX != LLONG_MAX if (val> LONG_MAX) return LONG_MAX; @@ -145,7 +142,7 @@ long int strtol(const char *nptr, char **endptr, int base) { return (long int)val; } -unsigned long int strtoul(const char *nptr, char **endptr, int base) { +unsigned long int strtoul(const char* nptr, char** endptr, int base) { unsigned long long int val = strtoull(nptr, endptr, base); #if ULONG_MAX != ULLONG_MAX if (val> ULONG_MAX) return ULONG_MAX; @@ -153,16 +150,16 @@ unsigned long int strtoul(const char *nptr, char **endptr, int base) { return (unsigned long int)val; } -int atoi(const char *nptr) { return (int)strtol(nptr, NULL, 10); } +int atoi(const char* nptr) { return (int)strtol(nptr, NULL, 10); } -long int atol(const char *nptr) { return strtol(nptr, NULL, 10); } +long int atol(const char* nptr) { return strtol(nptr, NULL, 10); } -long long int atoll(const char *nptr) { return strtoll(nptr, NULL, 10); } +long long int atoll(const char* nptr) { return strtoll(nptr, NULL, 10); } #if (defined(__x86_64__) && defined(__SSE__)) || \ (defined(__aarch64__) && defined(__ARM_FP)) || defined(__riscv) -double strtod(const char *nptr, char **endptr) { - const char *s = nptr; +double strtod(const char* nptr, char** endptr) { + const char* s = nptr; double acc = 0.0; int sign = 1; @@ -196,7 +193,7 @@ double strtod(const char *nptr, char **endptr) { if (any && (*s == 'e' || *s == 'E')) { int esign = 1; int exp = 0; - const char *eptr = s + 1; + const char* eptr = s + 1; if (*eptr == '-') { esign = -1; @@ -225,15 +222,15 @@ double strtod(const char *nptr, char **endptr) { } } - if (endptr) *endptr = (char *)(any ? s : nptr); + if (endptr) *endptr = (char*)(any ? s : nptr); return sign * acc; } -float strtof(const char *nptr, char **endptr) { +float strtof(const char* nptr, char** endptr) { return (float)strtod(nptr, endptr); } -double atof(const char *nptr) { return strtod(nptr, NULL); } +double atof(const char* nptr) { return strtod(nptr, NULL); } #endif #ifdef __cplusplus diff --git a/src/libc/sk_string.c b/src/libc/sk_string.c index 2465a4260..cc7fd6537 100644 --- a/src/libc/sk_string.c +++ b/src/libc/sk_string.c @@ -9,9 +9,9 @@ extern "C" { #endif // 复制内存块 -void *memcpy(void *dest, const void *src, size_t n) { - char *d = (char *)dest; - const char *s = (const char *)src; +void* memcpy(void* dest, const void* src, size_t n) { + char* d = (char*)dest; + const char* s = (const char*)src; while (n--) { *d++ = *s++; } @@ -19,16 +19,16 @@ void *memcpy(void *dest, const void *src, size_t n) { } // 复制内存块,可以处理重叠区域。 -void *memmove(void *dest, const void *src, size_t n) { - char *d = (char *)dest; - const char *s = (const char *)src; +void* memmove(void* dest, const void* src, size_t n) { + char* d = (char*)dest; + const char* s = (const char*)src; if (d < s) { while (n--) { *d++ = *s++; } } else { - const char *lasts = s + (n - 1); - char *lastd = d + (n - 1); + const char* lasts = s + (n - 1); + char* lastd = d + (n - 1); while (n--) { *lastd-- = *lasts--; } @@ -37,8 +37,8 @@ void *memmove(void *dest, const void *src, size_t n) { } // 设置内存块 -void *memset(void *dest, int val, size_t n) { - unsigned char *ptr = (unsigned char *)dest; +void* memset(void* dest, int val, size_t n) { + unsigned char* ptr = (unsigned char*)dest; while (n--> 0) { *ptr++ = val; } @@ -46,9 +46,9 @@ void *memset(void *dest, int val, size_t n) { } // 比较内存块 -int memcmp(const void *str1, const void *str2, size_t n) { - const unsigned char *s1 = (const unsigned char *)str1; - const unsigned char *s2 = (const unsigned char *)str2; +int memcmp(const void* str1, const void* str2, size_t n) { + const unsigned char* s1 = (const unsigned char*)str1; + const unsigned char* s2 = (const unsigned char*)str2; while (n--> 0) { if (*s1++ != *s2++) { @@ -59,13 +59,13 @@ int memcmp(const void *str1, const void *str2, size_t n) { } // 在内存块中查找字符 -void *memchr(const void *str, int c, size_t n) { - const unsigned char *src = (const unsigned char *)str; +void* memchr(const void* str, int c, size_t n) { + const unsigned char* src = (const unsigned char*)str; unsigned char uc = (unsigned char)c; while (n--> 0) { if (*src == uc) { - return (void *)src; + return (void*)src; } src++; } @@ -73,8 +73,8 @@ void *memchr(const void *str, int c, size_t n) { } // 复制字符串 -char *strcpy(char *dest, const char *src) { - char *address = dest; +char* strcpy(char* dest, const char* src) { + char* address = dest; while ((*dest++ = *src++) != '0円') { ; } @@ -82,17 +82,17 @@ char *strcpy(char *dest, const char *src) { } // 复制指定长度的字符串 -char *strncpy(char *dest, const char *src, size_t n) { +char* strncpy(char* dest, const char* src, size_t n) { size_t size = strnlen(src, n); if (size != n) { memset(dest + size, '0円', n - size); } - return (char *)memcpy(dest, src, size); + return (char*)memcpy(dest, src, size); } // 连接字符串 -char *strcat(char *dest, const char *src) { - char *add_d = dest; +char* strcat(char* dest, const char* src) { + char* add_d = dest; if (dest != 0 && src != 0) { while (*add_d) { add_d++; @@ -106,22 +106,22 @@ char *strcat(char *dest, const char *src) { } // 比较字符串 -int strcmp(const char *s1, const char *s2) { +int strcmp(const char* s1, const char* s2) { while (*s1 && (*s1 == *s2)) { s1++; s2++; } - return *(const unsigned char *)s1 - *(const unsigned char *)s2; + return *(const unsigned char*)s1 - *(const unsigned char*)s2; } // 比较指定长度的字符串 -int strncmp(const char *s1, const char *s2, size_t n) { +int strncmp(const char* s1, const char* s2, size_t n) { if (n == 0) { return 0; } do { if (*s1 != *s2++) { - return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1)); + return (*(const unsigned char*)s1 - *(const unsigned char*)(s2 - 1)); } if (*s1++ == '0円') { break; @@ -131,7 +131,7 @@ int strncmp(const char *s1, const char *s2, size_t n) { } // 获取字符串长度 -size_t strlen(const char *s) { +size_t strlen(const char* s) { size_t len = 0; while (s[len]) { len++; @@ -140,8 +140,8 @@ size_t strlen(const char *s) { } // 获取指定字符串长度 -size_t strnlen(const char *s, size_t n) { - const char *p = s; +size_t strnlen(const char* s, size_t n) { + const char* p = s; while (n--> 0 && *p) { p++; } @@ -149,23 +149,23 @@ size_t strnlen(const char *s, size_t n) { } // 查找字符在字符串中的首次出现 -char *strchr(const char *s, int c) { +char* strchr(const char* s, int c) { char ch = (char)c; do { if (*s == ch) { - return (char *)s; + return (char*)s; } } while (*s++); return NULL; } -char *strrchr(const char *s, int c) { - char *rtnval = 0; +char* strrchr(const char* s, int c) { + char* rtnval = 0; char ch = (char)c; do { if (*s == ch) { - rtnval = (char *)s; + rtnval = (char*)s; } } while (*s++); return (rtnval); diff --git a/src/main.cpp b/src/main.cpp index a64609689..45e0f8ad2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -152,6 +152,8 @@ auto main(int argc, const char** argv) -> int { ArchInit(argc, argv); // 内存相关初始化 MemoryInit(); + // 设备管理器初始化 + DeviceInit(); // 中断相关初始化 InterruptInit(argc, argv); // 初始化任务管理器 (设置主线程) @@ -163,6 +165,7 @@ auto main(int argc, const char** argv) -> int { DumpStack(); klog::info << "Hello SimpleKernel\n"; + klog::Info("Initializing test tasks...\n"); // 为主核心创建测试任务 @@ -171,7 +174,6 @@ auto main(int argc, const char** argv) -> int { klog::Info("Main: Starting scheduler...\n"); // 启动调度器,不再返回 - // 从这里开始,系统将在各个任务之间切换 Singleton::GetInstance().Schedule(); // 不应该执行到这里 diff --git a/src/task/CMakeLists.txt b/src/task/CMakeLists.txt index b5c88eb83..08bf06006 100644 --- a/src/task/CMakeLists.txt +++ b/src/task/CMakeLists.txt @@ -15,4 +15,5 @@ TARGET_SOURCES ( wakeup.cpp clone.cpp wait.cpp - task_manager.cpp) + task_manager.cpp + mutex.cpp) diff --git a/src/task/include/task_control_block.hpp b/src/task/include/task_control_block.hpp index 3670fbf0e..fb6bb6c69 100644 --- a/src/task/include/task_control_block.hpp +++ b/src/task/include/task_control_block.hpp @@ -11,6 +11,7 @@ #include #include +#include "file_descriptor.hpp" #include "resource_id.hpp" /// 进程 ID 类型 @@ -189,7 +190,9 @@ struct TaskControlBlock { uint64_t interrupt_number = 0; /// @todo 优先级继承相关 - /// @todo 文件系统相关 + + /// 文件描述符表 + filesystem::FileDescriptorTable* fd_table = nullptr; /// @name 线程组相关方法 /// @{ diff --git a/src/task/include/task_manager.hpp b/src/task/include/task_manager.hpp index 2ac36b9d2..ad722a745 100644 --- a/src/task/include/task_manager.hpp +++ b/src/task/include/task_manager.hpp @@ -206,7 +206,7 @@ class TaskManager { sk_std::unordered_map interrupt_work_queues_; /// PID 分配器 - std::atomic pid_allocator{1}; + std::atomic pid_allocator_{1}; /** * @brief 分配新的 PID diff --git a/src/task/mutex.cpp b/src/task/mutex.cpp new file mode 100644 index 000000000..57bc1989e --- /dev/null +++ b/src/task/mutex.cpp @@ -0,0 +1,123 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "mutex.hpp" + +#include "kernel_log.hpp" +#include "singleton.hpp" +#include "task_manager.hpp" + +auto Mutex::Lock() -> bool { + auto current_task = Singleton::GetInstance().GetCurrentTask(); + if (current_task == nullptr) { + klog::Err("Mutex::Lock: Cannot lock mutex '%s' outside task context\n", + name_); + return false; + } + + Pid current_pid = current_task->pid; + + // 检查是否递归获取锁 + if (IsLockedByCurrentTask()) { + klog::Warn("Mutex::Lock: Task %zu tried to recursively lock mutex '%s'\n", + current_pid, name_); + return false; + } + + // 尝试获取锁 + bool expected = false; + while (!locked_.compare_exchange_weak( + expected, true, std::memory_order_acquire, std::memory_order_relaxed)) { + // 锁被占用,阻塞当前任务 + klog::Debug("Mutex::Lock: Task %zu blocking on mutex '%s'\n", current_pid, + name_); + Singleton::GetInstance().Block(resource_id_); + + // 被唤醒后重新尝试 + expected = false; + } + + // 成功获取锁,记录所有者 + owner_.store(current_pid, std::memory_order_release); + klog::Debug("Mutex::Lock: Task %zu acquired mutex '%s'\n", current_pid, + name_); + return true; +} + +auto Mutex::UnLock() -> bool { + auto current_task = Singleton::GetInstance().GetCurrentTask(); + if (current_task == nullptr) { + klog::Err("Mutex::UnLock: Cannot unlock mutex '%s' outside task context\n", + name_); + return false; + } + + Pid current_pid = current_task->pid; + + // 检查是否由持有锁的任务释放 + if (!IsLockedByCurrentTask()) { + klog::Warn( + "Mutex::UnLock: Task %zu tried to unlock mutex '%s' it doesn't own\n", + current_pid, name_); + return false; + } + + // 释放锁 + owner_.store(std::numeric_limits::max(), std::memory_order_release); + locked_.store(false, std::memory_order_release); + + klog::Debug("Mutex::UnLock: Task %zu released mutex '%s'\n", current_pid, + name_); + + // 唤醒等待此锁的任务 + Singleton::GetInstance().Wakeup(resource_id_); + + return true; +} + +auto Mutex::TryLock() -> bool { + auto current_task = Singleton::GetInstance().GetCurrentTask(); + if (current_task == nullptr) { + klog::Err( + "Mutex::TryLock: Cannot trylock mutex '%s' outside task context\n", + name_); + return false; + } + + Pid current_pid = current_task->pid; + + // 检查是否递归获取锁 + if (IsLockedByCurrentTask()) { + klog::Debug( + "Mutex::TryLock: Task %zu tried to recursively trylock mutex '%s'\n", + current_pid, name_); + return false; + } + + // 尝试获取锁(非阻塞) + bool expected = false; + if (locked_.compare_exchange_strong(expected, true, std::memory_order_acquire, + std::memory_order_relaxed)) { + // 成功获取锁,记录所有者 + owner_.store(current_pid, std::memory_order_release); + klog::Debug("Mutex::TryLock: Task %zu acquired mutex '%s'\n", current_pid, + name_); + return true; + } + + // 锁被占用 + klog::Debug("Mutex::TryLock: Task %zu failed to acquire mutex '%s'\n", + current_pid, name_); + return false; +} + +auto Mutex::IsLockedByCurrentTask() const -> bool { + auto current_task = Singleton::GetInstance().GetCurrentTask(); + if (current_task == nullptr) { + return false; + } + + return locked_.load(std::memory_order_acquire) && + owner_.load(std::memory_order_acquire) == current_task->pid; +} diff --git a/src/task/schedule.cpp b/src/task/schedule.cpp index 74783c9cd..1d9b6055f 100644 --- a/src/task/schedule.cpp +++ b/src/task/schedule.cpp @@ -26,7 +26,13 @@ void TaskManager::Schedule() { auto& cpu_sched = GetCurrentCpuSched(); - cpu_sched.lock.Lock(); + cpu_sched.lock.Lock().or_else([](auto&& err) { + sk_printf("Schedule: Failed to acquire lock: %s\n", err.message()); + while (true) { + cpu_io::Pause(); + } + return Expected{}; + }); auto* current = GetCurrentTask(); sk_assert_msg(current != nullptr, "Schedule: No current task to schedule"); @@ -66,7 +72,13 @@ void TaskManager::Schedule() { } else { // 否则统计空闲时间并返回 cpu_sched.idle_time++; - cpu_sched.lock.UnLock(); + cpu_sched.lock.UnLock().or_else([](auto&& err) { + sk_printf("Schedule: Failed to release lock: %s\n", err.message()); + while (true) { + cpu_io::Pause(); + } + return Expected{}; + }); return; } } @@ -92,7 +104,13 @@ void TaskManager::Schedule() { // 更新 per-CPU running_task per_cpu::GetCurrentCore().running_task = next; - cpu_sched.lock.UnLock(); + cpu_sched.lock.UnLock().or_else([](auto&& err) { + sk_printf("Schedule: Failed to release lock: %s\n", err.message()); + while (true) { + cpu_io::Pause(); + } + return Expected{}; + }); // 上下文切换 if (current != next) { diff --git a/src/task/task_control_block.cpp b/src/task/task_control_block.cpp index b81a1f0a8..e8dc79f42 100644 --- a/src/task/task_control_block.cpp +++ b/src/task/task_control_block.cpp @@ -54,7 +54,7 @@ uint64_t LoadElf(const uint8_t* elf_data, uint64_t* page_table) { klog::Err("Failed to allocate page for ELF\n"); return 0; } - std::memset(p_page, 0, cpu_io::virtual_memory::kPageSize); + sk_std::memset(p_page, 0, cpu_io::virtual_memory::kPageSize); // Mapping logic uintptr_t v_start = page; @@ -70,8 +70,8 @@ uint64_t LoadElf(const uint8_t* elf_data, uint64_t* page_table) { if (copy_end> copy_start) { uintptr_t dst_off = copy_start - v_start; uintptr_t src_off = (copy_start - vaddr) + offset; - std::memcpy((uint8_t*)p_page + dst_off, elf_data + src_off, - copy_end - copy_start); + sk_std::memcpy((uint8_t*)p_page + dst_off, elf_data + src_off, + copy_end - copy_start); } if (!vm.MapPage(page_table, (void*)page, p_page, flags)) { diff --git a/src/task/task_manager.cpp b/src/task/task_manager.cpp index 8927fdac9..c73763001 100644 --- a/src/task/task_manager.cpp +++ b/src/task/task_manager.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -103,17 +104,15 @@ void TaskManager::AddTask(TaskControlBlock* task) { auto& cpu_sched = cpu_schedulers_[target_core]; - // 加锁保护运行队列 - cpu_sched.lock.Lock(); - - if (task->policy < SchedPolicy::kPolicyCount) { - if (cpu_sched.schedulers[task->policy]) { - cpu_sched.schedulers[task->policy]->Enqueue(task); + { + LockGuard lock_guard(cpu_sched.lock); + if (task->policy < SchedPolicy::kPolicyCount) { + if (cpu_sched.schedulers[task->policy]) { + cpu_sched.schedulers[task->policy]->Enqueue(task); + } } } - cpu_sched.lock.UnLock(); - // 如果是当前核心,且添加了比当前任务优先级更高的任务,触发抢占 if (target_core == cpu_io::GetCurrentCoreId()) { auto& cpu_data = per_cpu::GetCurrentCore(); @@ -128,7 +127,7 @@ void TaskManager::AddTask(TaskControlBlock* task) { } } -size_t TaskManager::AllocatePid() { return pid_allocator.fetch_add(1); } +size_t TaskManager::AllocatePid() { return pid_allocator_.fetch_add(1); } TaskControlBlock* TaskManager::FindTask(Pid pid) { LockGuard lock_guard{task_table_lock_}; diff --git a/src/virtual_memory.cpp b/src/virtual_memory.cpp new file mode 100644 index 000000000..5e2aea486 --- /dev/null +++ b/src/virtual_memory.cpp @@ -0,0 +1,335 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "virtual_memory.hpp" + +#include + +#include +#include +#include +#include + +#include "basic_info.hpp" +#include "expected.hpp" +#include "kernel_log.hpp" +#include "singleton.hpp" +#include "sk_cassert" +#include "sk_stdlib.h" + +VirtualMemory::VirtualMemory() { + // 分配根页表目录 + kernel_page_dir_ = aligned_alloc(cpu_io::virtual_memory::kPageSize, + cpu_io::virtual_memory::kPageSize); + sk_assert_msg(kernel_page_dir_ != nullptr, + "Failed to allocate kernel page directory"); + + // 清零页表目录 + std::memset(kernel_page_dir_, 0, cpu_io::virtual_memory::kPageSize); + + // 获取内核基本信息 + const auto& basic_info = Singleton::GetInstance(); + + // 映射全部物理内存 + MapMMIO(basic_info.physical_memory_addr, basic_info.physical_memory_size) + .or_else([](auto&& err) -> Expected { + klog::Err("Failed to map kernel memory: %s", err.message()); + while (true) { + cpu_io::Pause(); + } + return {}; + }); + + klog::Info("Kernel memory mapped from 0x%lX to 0x%lX\n", + basic_info.physical_memory_addr, + basic_info.physical_memory_addr + basic_info.physical_memory_size); +} + +void VirtualMemory::InitCurrentCore() const { + cpu_io::virtual_memory::SetPageDirectory( + reinterpret_cast(kernel_page_dir_)); + // 开启分页 + cpu_io::virtual_memory::EnablePage(); +} + +auto VirtualMemory::MapMMIO(uint64_t phys_addr, size_t size, uint32_t flags) + -> Expected { + // 计算对齐后的起始和结束页 + auto start_page = cpu_io::virtual_memory::PageAlign(phys_addr); + auto end_page = cpu_io::virtual_memory::PageAlignUp(phys_addr + size); + + // 遍历并映射 + for (uint64_t addr = start_page; addr < end_page; + addr += cpu_io::virtual_memory::kPageSize) { + auto result = MapPage(kernel_page_dir_, reinterpret_cast(addr), + reinterpret_cast(addr), flags); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + return reinterpret_cast(phys_addr); +} + +auto VirtualMemory::MapPage(void* page_dir, void* virtual_addr, + void* physical_addr, uint32_t flags) + -> Expected { + sk_assert_msg(page_dir != nullptr, "MapPage: page_dir is null"); + + // 查找页表项,如果不存在则分配 + auto pte_result = FindPageTableEntry(page_dir, virtual_addr, true); + if (!pte_result.has_value()) { + return std::unexpected(Error(ErrorCode::kVmMapFailed)); + } + + auto pte = pte_result.value(); + + // 检查是否已经映射且标志位相同 + if (cpu_io::virtual_memory::IsPageTableEntryValid(*pte)) { + // 如果物理地址和标志位都相同,则认为是重复映射(警告但不失败) + auto existing_pa = cpu_io::virtual_memory::PageTableEntryToPhysical(*pte); + if (existing_pa == reinterpret_cast(physical_addr) && + (*pte & ((1ULL << cpu_io::virtual_memory::kPteAttributeBits) - 1)) == + flags) { + klog::Debug( + "MapPage: duplicate va = %p, pa = 0x%lX, flags = 0x%X, skip\n", + virtual_addr, existing_pa, flags); + // 重复映射,但不是错误 + return {}; + } + klog::Warn("MapPage: remap va = %p from pa = 0x%lX to pa = %p\n", + virtual_addr, existing_pa, physical_addr); + } + + // 设置页表项 + *pte = cpu_io::virtual_memory::PhysicalToPageTableEntry( + reinterpret_cast(physical_addr), flags); + // 刷新 TLB + cpu_io::virtual_memory::FlushTLBAll(); + + return {}; +} + +auto VirtualMemory::UnmapPage(void* page_dir, void* virtual_addr) + -> Expected { + sk_assert_msg(page_dir != nullptr, "UnmapPage: page_dir is null"); + + auto pte_result = FindPageTableEntry(page_dir, virtual_addr, false); + if (!pte_result.has_value()) { + return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); + } + + auto pte = pte_result.value(); + + if (!cpu_io::virtual_memory::IsPageTableEntryValid(*pte)) { + return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); + } + + // 清除页表项 + *pte = 0; + + // 刷新 TLB + cpu_io::virtual_memory::FlushTLBAll(); + + return {}; +} + +auto VirtualMemory::GetMapping(void* page_dir, void* virtual_addr) + -> Expected { + sk_assert_msg(page_dir != nullptr, "GetMapping: page_dir is null"); + + auto pte_result = FindPageTableEntry(page_dir, virtual_addr, false); + if (!pte_result.has_value()) { + return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); + } + + auto pte = pte_result.value(); + + if (!cpu_io::virtual_memory::IsPageTableEntryValid(*pte)) { + return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); + } + + return reinterpret_cast( + cpu_io::virtual_memory::PageTableEntryToPhysical(*pte)); +} + +void VirtualMemory::DestroyPageDirectory(void* page_dir, bool free_pages) { + if (page_dir == nullptr) { + return; + } + + // 递归释放所有层级的页表 + RecursiveFreePageTable(reinterpret_cast(page_dir), + cpu_io::virtual_memory::kPageTableLevels - 1, + free_pages); + + // 释放根页表目录本身 + aligned_free(page_dir); + + klog::Debug("Destroyed page directory at address: %p\n", page_dir); +} + +auto VirtualMemory::ClonePageDirectory(void* src_page_dir, bool copy_mappings) + -> Expected { + sk_assert_msg(src_page_dir != nullptr, + "ClonePageDirectory: source page directory is nullptr"); + + // 创建新的页表目录 + auto dst_page_dir = aligned_alloc(cpu_io::virtual_memory::kPageSize, + cpu_io::virtual_memory::kPageSize); + if (dst_page_dir == nullptr) { + return std::unexpected(Error(ErrorCode::kVmAllocationFailed)); + } + + // 清零新页表 + std::memset(dst_page_dir, 0, cpu_io::virtual_memory::kPageSize); + + // 递归复制页表 + auto result = RecursiveClonePageTable( + reinterpret_cast(src_page_dir), + reinterpret_cast(dst_page_dir), + cpu_io::virtual_memory::kPageTableLevels - 1, copy_mappings); + if (!result.has_value()) { + // 复制失败,清理已分配的页表 + DestroyPageDirectory(dst_page_dir, false); + return std::unexpected(result.error()); + } + + klog::Debug("Cloned page directory from %p to %p\n", src_page_dir, + dst_page_dir); + return dst_page_dir; +} + +void VirtualMemory::RecursiveFreePageTable(uint64_t* table, size_t level, + bool free_pages) { + if (table == nullptr) { + return; + } + + // 遍历页表中的所有条目 + for (size_t i = 0; i < kEntriesPerTable; ++i) { + uint64_t pte = table[i]; + if (!cpu_io::virtual_memory::IsPageTableEntryValid(pte)) { + continue; + } + + auto pa = cpu_io::virtual_memory::PageTableEntryToPhysical(pte); + + // 如果不是最后一级,递归释放子页表 + if (level> 0) { + RecursiveFreePageTable(reinterpret_cast(pa), level - 1, + free_pages); + } else if (free_pages) { + // 最后一级页表,释放物理页 + aligned_free(reinterpret_cast(pa)); + } + + // 清除页表项 + table[i] = 0; + } + + // 如果不是根页表,释放当前页表 + if (level < cpu_io::virtual_memory::kPageTableLevels - 1) { + aligned_free(table); + } +} + +auto VirtualMemory::RecursiveClonePageTable(uint64_t* src_table, + uint64_t* dst_table, size_t level, + bool copy_mappings) + -> Expected { + sk_assert_msg(src_table != nullptr, + "RecursiveClonePageTable: src_table is null"); + sk_assert_msg(dst_table != nullptr, + "RecursiveClonePageTable: dst_table is null"); + + for (size_t i = 0; i < kEntriesPerTable; ++i) { + uint64_t src_pte = src_table[i]; + if (!cpu_io::virtual_memory::IsPageTableEntryValid(src_pte)) { + continue; + } + + if (level> 0) { + // 非最后一级,需要递归复制子页表 + auto src_pa = cpu_io::virtual_memory::PageTableEntryToPhysical(src_pte); + auto* src_next_table = reinterpret_cast(src_pa); + + // 分配新的子页表 + auto* dst_next_table = aligned_alloc(cpu_io::virtual_memory::kPageSize, + cpu_io::virtual_memory::kPageSize); + if (dst_next_table == nullptr) { + return std::unexpected(Error(ErrorCode::kVmAllocationFailed)); + } + + // 清零新页表 + std::memset(dst_next_table, 0, cpu_io::virtual_memory::kPageSize); + + // 递归复制子页表 + auto result = RecursiveClonePageTable( + src_next_table, reinterpret_cast(dst_next_table), + level - 1, copy_mappings); + if (!result.has_value()) { + aligned_free(dst_next_table); + return std::unexpected(result.error()); + } + + // 设置目标页表项指向新的子页表 + dst_table[i] = cpu_io::virtual_memory::PhysicalToPageTableEntry( + reinterpret_cast(dst_next_table), + cpu_io::virtual_memory::GetTableEntryPermissions()); + } else { + // 最后一级页表 + if (copy_mappings) { + // 直接复制页表项(共享物理页) + dst_table[i] = src_pte; + } + // 如果不复制映射,保持目标页表项为 0 + } + } + + return {}; +} + +auto VirtualMemory::FindPageTableEntry(void* page_dir, void* virtual_addr, + bool allocate) -> Expected { + auto* current_table = reinterpret_cast(page_dir); + auto vaddr = reinterpret_cast(virtual_addr); + + // 遍历页表层级 + for (size_t level = cpu_io::virtual_memory::kPageTableLevels - 1; level> 0; + --level) { + // 获取当前级别的虚拟页号 + auto vpn = cpu_io::virtual_memory::GetVirtualPageNumber(vaddr, level); + auto* pte = ¤t_table[vpn]; + if (cpu_io::virtual_memory::IsPageTableEntryValid(*pte)) { + // 页表项有效,获取下一级页表 + current_table = reinterpret_cast( + cpu_io::virtual_memory::PageTableEntryToPhysical(*pte)); + } else { + // 页表项无效 + if (allocate) { + auto* new_table = aligned_alloc(cpu_io::virtual_memory::kPageSize, + cpu_io::virtual_memory::kPageSize); + if (new_table == nullptr) { + return std::unexpected(Error(ErrorCode::kVmAllocationFailed)); + } + // 清零新页表 + std::memset(new_table, 0, cpu_io::virtual_memory::kPageSize); + + // 设置中间页表项 + *pte = cpu_io::virtual_memory::PhysicalToPageTableEntry( + reinterpret_cast(new_table), + cpu_io::virtual_memory::GetTableEntryPermissions()); + + current_table = reinterpret_cast(new_table); + } else { + return std::unexpected(Error(ErrorCode::kVmPageNotMapped)); + } + } + } + + // 返回最底层页表中的页表项 + auto vpn = cpu_io::virtual_memory::GetVirtualPageNumber(vaddr, 0); + + return ¤t_table[vpn]; +} diff --git a/tests/integration_test/aarch64_minimal/main.cpp b/tests/integration_test/aarch64_minimal/main.cpp index ef28c8d95..ba5ba2d8a 100644 --- a/tests/integration_test/aarch64_minimal/main.cpp +++ b/tests/integration_test/aarch64_minimal/main.cpp @@ -7,7 +7,7 @@ #include extern "C" { -volatile uint8_t *uart = (uint8_t *)0x9000000; +volatile uint8_t* uart = (uint8_t*)0x9000000; void putc(char character) { *uart = character; } void SetupFpu() { diff --git a/tests/integration_test/opensbi_test/main.cpp b/tests/integration_test/opensbi_test/main.cpp index 8f50e254c..b45f12273 100644 --- a/tests/integration_test/opensbi_test/main.cpp +++ b/tests/integration_test/opensbi_test/main.cpp @@ -13,7 +13,7 @@ extern "C" void putchar_(char character) { sbi_debug_console_write_byte(character); } -uint32_t main(uint32_t, uint8_t *) { +uint32_t main(uint32_t, uint8_t*) { putchar_('H'); putchar_('e'); putchar_('l'); @@ -30,7 +30,7 @@ uint32_t main(uint32_t, uint8_t *) { return 0; } -extern "C" void _start(uint32_t argc, uint8_t *argv) { +extern "C" void _start(uint32_t argc, uint8_t* argv) { main(argc, argv); // 进入死循环 diff --git a/tests/system_test/memory_test.cpp b/tests/system_test/memory_test.cpp index 1df075a6f..dac7b6d2e 100644 --- a/tests/system_test/memory_test.cpp +++ b/tests/system_test/memory_test.cpp @@ -17,9 +17,9 @@ #include "system_test.h" extern "C" { -void *malloc(size_t size); -void free(void *ptr); -void *aligned_alloc(size_t alignment, size_t size); +void* malloc(size_t size); +void free(void* ptr); +void* aligned_alloc(size_t alignment, size_t size); } auto memory_test() -> bool { @@ -27,11 +27,11 @@ auto memory_test() -> bool { // Test 1: malloc & free size_t size = 1024; - void *ptr = malloc(size); + void* ptr = malloc(size); EXPECT_TRUE(ptr != nullptr, "memory_test: malloc failed"); // Write and read verification - auto *byte_ptr = static_cast(ptr); + auto* byte_ptr = static_cast(ptr); for (size_t i = 0; i < size; ++i) { byte_ptr[i] = static_cast(i & 0xFF); } @@ -47,7 +47,7 @@ auto memory_test() -> bool { // Test 2: aligned_alloc size_t alignment = 256; size_t aligned_size = 512; - void *aligned_ptr = aligned_alloc(alignment, aligned_size); + void* aligned_ptr = aligned_alloc(alignment, aligned_size); EXPECT_TRUE(aligned_ptr != nullptr, "memory_test: aligned_alloc failed"); EXPECT_EQ(reinterpret_cast(aligned_ptr) & (alignment - 1), 0, @@ -58,7 +58,7 @@ auto memory_test() -> bool { // Test 3: Multiple small allocations const int count = 10; - void *ptrs[count]; + void* ptrs[count]; for (int i = 0; i < count; ++i) { ptrs[i] = malloc(128); @@ -68,7 +68,7 @@ auto memory_test() -> bool { } for (int i = 0; i < count; ++i) { - auto *p = static_cast(ptrs[i]); + auto* p = static_cast(ptrs[i]); for (int j = 0; j < 128; ++j) { EXPECT_EQ(p[j], i, "memory_test: multi alloc verify failed"); } diff --git a/tests/system_test/sk_unordered_map_test.cpp b/tests/system_test/sk_unordered_map_test.cpp index 6ec8b774a..212ec69b8 100644 --- a/tests/system_test/sk_unordered_map_test.cpp +++ b/tests/system_test/sk_unordered_map_test.cpp @@ -32,7 +32,7 @@ bool test_insert_and_find() { EXPECT_EQ(map.contains(1), true, "Key 1 should exist"); auto it = map.find(1); - EXPECT_NE((size_t) & *it, (size_t) & *map.end(), + EXPECT_NE((size_t)&*it, (size_t)&*map.end(), "find(1) should not return end()"); map.insert({2, 20}); diff --git a/tests/system_test/spinlock_test.cpp b/tests/system_test/spinlock_test.cpp index 018392494..76915a4ad 100644 --- a/tests/system_test/spinlock_test.cpp +++ b/tests/system_test/spinlock_test.cpp @@ -288,7 +288,7 @@ auto spinlock_smp_string_test() -> bool { } // 验证填充完整性 - const char *padding = "|LongStringPaddingForContention"; + const char* padding = "|LongStringPaddingForContention"; int padding_len = 31; // "|LongStringPaddingForContention" 的长度 int token_content_len = end_idx - current_idx - 1; bool padding_ok = true; diff --git a/tests/system_test/system_test.h b/tests/system_test/system_test.h index f5a6de896..2edd2c526 100644 --- a/tests/system_test/system_test.h +++ b/tests/system_test/system_test.h @@ -10,7 +10,7 @@ #include "sk_cstdio" template -bool expect_eq_helper(const T1 &val1, const T2 &val2, const char *msg) { +bool expect_eq_helper(const T1& val1, const T2& val2, const char* msg) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" if (val1 != val2) { @@ -28,7 +28,7 @@ bool expect_eq_helper(const T1 &val1, const T2 &val2, const char *msg) { } template -bool expect_ne_helper(const T1 &val1, const T2 &val2, const char *msg) { +bool expect_ne_helper(const T1& val1, const T2& val2, const char* msg) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" if (val1 == val2) { @@ -46,7 +46,7 @@ bool expect_ne_helper(const T1 &val1, const T2 &val2, const char *msg) { } template -bool expect_gt_helper(const T1 &val1, const T2 &val2, const char *msg) { +bool expect_gt_helper(const T1& val1, const T2& val2, const char* msg) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" if (!(val1> val2)) { @@ -64,7 +64,7 @@ bool expect_gt_helper(const T1 &val1, const T2 &val2, const char *msg) { } template -bool expect_lt_helper(const T1 &val1, const T2 &val2, const char *msg) { +bool expect_lt_helper(const T1& val1, const T2& val2, const char* msg) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" if (!(val1 < val2)) { @@ -82,7 +82,7 @@ bool expect_lt_helper(const T1 &val1, const T2 &val2, const char *msg) { } template -bool expect_ge_helper(const T1 &val1, const T2 &val2, const char *msg) { +bool expect_ge_helper(const T1& val1, const T2& val2, const char* msg) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" if (!(val1>= val2)) { diff --git a/tests/unit_test/CMakeLists.txt b/tests/unit_test/CMakeLists.txt index 7b7f08fdb..3220433be 100644 --- a/tests/unit_test/CMakeLists.txt +++ b/tests/unit_test/CMakeLists.txt @@ -26,6 +26,7 @@ ADD_EXECUTABLE ( task/fixtures/task_test_harness.cpp mocks/arch.cpp mocks/test_environment_state.cpp + ${CMAKE_SOURCE_DIR}/src/virtual_memory.cpp ${CMAKE_SOURCE_DIR}/src/syscall.cpp ${CMAKE_SOURCE_DIR}/src/task/block.cpp ${CMAKE_SOURCE_DIR}/src/task/clone.cpp @@ -36,7 +37,8 @@ ADD_EXECUTABLE ( ${CMAKE_SOURCE_DIR}/src/task/task_manager.cpp ${CMAKE_SOURCE_DIR}/src/task/tick_update.cpp ${CMAKE_SOURCE_DIR}/src/task/wait.cpp - ${CMAKE_SOURCE_DIR}/src/task/wakeup.cpp) + vfs_test.cpp + ramfs_test.cpp) TARGET_COMPILE_DEFINITIONS ( ${PROJECT_NAME} @@ -57,13 +59,17 @@ TARGET_INCLUDE_DIRECTORIES ( ${CMAKE_SOURCE_DIR}/src/arch ${CMAKE_SOURCE_DIR}/src/libc/include ${CMAKE_SOURCE_DIR}/src/libcxx/include - ${CMAKE_SOURCE_DIR}/src/task/include) + ${CMAKE_SOURCE_DIR}/src/task/include + ${CMAKE_SOURCE_DIR}/src/filesystem/include + ${CMAKE_SOURCE_DIR}/src/filesystem/vfs/include + ${CMAKE_SOURCE_DIR}/src/filesystem/ramfs/include) TARGET_COMPILE_OPTIONS (${PROJECT_NAME} PRIVATE ${DEFAULT_TEST_COMPILE_OPTIONS}) TARGET_LINK_OPTIONS (${PROJECT_NAME} PRIVATE ${DEFAULT_TEST_LINK_OPTIONS}) -TARGET_LINK_LIBRARIES (${PROJECT_NAME} PRIVATE ${DEFAULT_TEST_LINK_LIB} dtc-lib - bmalloc MPMCQueue task) +TARGET_LINK_LIBRARIES ( + ${PROJECT_NAME} PRIVATE ${DEFAULT_TEST_LINK_LIB} dtc-lib bmalloc MPMCQueue + task filesystem) GTEST_DISCOVER_TESTS (${PROJECT_NAME}) diff --git a/tests/unit_test/kernel_fdt_test.cpp b/tests/unit_test/kernel_fdt_test.cpp index e191bb929..8a2762c59 100644 --- a/tests/unit_test/kernel_fdt_test.cpp +++ b/tests/unit_test/kernel_fdt_test.cpp @@ -135,3 +135,120 @@ TEST_F(KernelFdtTest, MoveAssignmentTest) { EXPECT_EQ(memory_base, expected_base); EXPECT_EQ(memory_size, expected_size); } + +TEST_F(KernelFdtTest, ForEachCompatibleNodeTest) { + KernelFdt kerlen_fdt((uint64_t)riscv64_virt_dtb_data); + + size_t count = 0; + auto result = kerlen_fdt.ForEachCompatibleNode( + "virtio,mmio", + [&count](int offset, const char* node_name, uint64_t mmio_base, + size_t mmio_size, uint32_t irq) -> bool { + (void)offset; + (void)node_name; + (void)mmio_base; + (void)mmio_size; + (void)irq; + ++count; + return true; + }); + ASSERT_TRUE(result.has_value()) << result.error().message(); + EXPECT_EQ(count, 8); // riscv64 virt has 8 virtio,mmio nodes +} + +TEST_F(KernelFdtTest, ForEachNodeCompatibleDataTest) { + KernelFdt kerlen_fdt((uint64_t)riscv64_virt_dtb_data); + + bool found_plic = false; + auto result = kerlen_fdt.ForEachNode( + [&found_plic](const char* node_name, const char* compatible_data, + size_t compatible_len, uint64_t mmio_base, size_t mmio_size, + uint32_t irq) -> bool { + (void)mmio_base; + (void)mmio_size; + (void)irq; + if (compatible_data == nullptr || compatible_len == 0) { + return true; + } + // Look for PLIC node which has multi-string compatible: + // "sifive,plic-1.0.00円riscv,plic0" + if (strcmp(node_name, "plic@c000000") == 0) { + found_plic = true; + // Should have both strings in the stringlist + EXPECT_GT(compatible_len, strlen("sifive,plic-1.0.0") + 1); + // First string should be "sifive,plic-1.0.0" + EXPECT_STREQ(compatible_data, "sifive,plic-1.0.0"); + // Second string starts after first null terminator + const char* second = + compatible_data + strlen("sifive,plic-1.0.0") + 1; + EXPECT_STREQ(second, "riscv,plic0"); + } + return true; + }); + ASSERT_TRUE(result.has_value()) << result.error().message(); + EXPECT_TRUE(found_plic) << "PLIC node not found in ForEachNode traversal"; +} + +TEST_F(KernelFdtTest, ForEachCompatibleNodeNoMatchTest) { + KernelFdt kerlen_fdt((uint64_t)riscv64_virt_dtb_data); + + size_t count = 0; + auto result = kerlen_fdt.ForEachCompatibleNode( + "nonexistent,device", + [&count](int offset, const char* node_name, uint64_t mmio_base, + size_t mmio_size, uint32_t irq) -> bool { + (void)offset; + (void)node_name; + (void)mmio_base; + (void)mmio_size; + (void)irq; + ++count; + return true; + }); + ASSERT_TRUE(result.has_value()) << result.error().message(); + EXPECT_EQ(count, 0); // No matching nodes +} + +TEST_F(KernelFdtTest, ForEachCompatibleNodeEarlyStopTest) { + KernelFdt kerlen_fdt((uint64_t)riscv64_virt_dtb_data); + + size_t count = 0; + auto result = kerlen_fdt.ForEachCompatibleNode( + "virtio,mmio", + [&count](int offset, const char* node_name, uint64_t mmio_base, + size_t mmio_size, uint32_t irq) -> bool { + (void)offset; + (void)node_name; + (void)mmio_base; + (void)mmio_size; + (void)irq; + ++count; + return count < 3; // Stop after 3 nodes + }); + ASSERT_TRUE(result.has_value()) << result.error().message(); + EXPECT_EQ(count, 3); +} + +TEST_F(KernelFdtTest, MultiCompatibleMatchTest) { + KernelFdt kerlen_fdt((uint64_t)riscv64_virt_dtb_data); + + // The PLIC node has compatible = "sifive,plic-1.0.00円riscv,plic0" + // ForEachCompatibleNode uses fdt_node_offset_by_compatible which + // matches against any string in the compatible stringlist + size_t count = 0; + auto result = kerlen_fdt.ForEachCompatibleNode( + "riscv,plic0", + [&count](int offset, const char* node_name, uint64_t mmio_base, + size_t mmio_size, uint32_t irq) -> bool { + (void)offset; + (void)node_name; + (void)mmio_base; + (void)mmio_size; + (void)irq; + ++count; + return true; + }); + ASSERT_TRUE(result.has_value()) << result.error().message(); + EXPECT_EQ(count, + 1); // Should find the PLIC node via second compatible string +} diff --git a/tests/unit_test/mocks/arch.cpp b/tests/unit_test/mocks/arch.cpp index e58bf4e1b..d2e8febb6 100644 --- a/tests/unit_test/mocks/arch.cpp +++ b/tests/unit_test/mocks/arch.cpp @@ -71,3 +71,15 @@ void InitTaskContext(cpu_io::CalleeSavedContext* task_context, task_context->EntryArgument() = reinterpret_cast(trap_context_ptr); task_context->StackPointer() = stack_top; } + +#include +#include + +#undef sk_printf +extern "C" int sk_printf(const char* format, ...) { + va_list args; + va_start(args, format); + int ret = vprintf(format, args); + va_end(args); + return ret; +} diff --git a/tests/unit_test/ramfs_test.cpp b/tests/unit_test/ramfs_test.cpp new file mode 100644 index 000000000..a69867b80 --- /dev/null +++ b/tests/unit_test/ramfs_test.cpp @@ -0,0 +1,288 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief ramfs 单元测试 + */ + +#include "ramfs.hpp" + +#include + +#include + +#include "test_environment_state.hpp" + +using namespace vfs; +using namespace ramfs; + +class RamFsTest : public ::testing::Test { + protected: + RamFs ramfs_; + test_env::TestEnvironmentState env_state_; + + void SetUp() override { + env_state_.InitializeCores(1); + env_state_.SetCurrentThreadEnvironment(); + env_state_.BindThreadToCore(std::this_thread::get_id(), 0); + auto result = ramfs_.Mount(nullptr); + EXPECT_TRUE(result.has_value()); + } + + void TearDown() override { + ramfs_.Unmount(); + env_state_.ClearCurrentThreadEnvironment(); + } +}; + +// 测试基本挂载/卸载 +TEST_F(RamFsTest, MountUnmount) { + // SetUp 中已经挂载,这里测试重复挂载 + auto result = ramfs_.Mount(nullptr); + EXPECT_FALSE(result.has_value()); // 应该失败,因为已经挂载 +} + +TEST_F(RamFsTest, GetName) { EXPECT_STREQ(ramfs_.GetName(), "ramfs"); } + +TEST_F(RamFsTest, GetRootInode) { + Inode* root = ramfs_.GetRootInode(); + EXPECT_NE(root, nullptr); + EXPECT_EQ(root->type, FileType::kDirectory); +} + +// 测试 inode 分配 +TEST_F(RamFsTest, AllocateInode) { + // 分配多个 inode + std::vector inodes; + for (int i = 0; i < 10; ++i) { + auto result = ramfs_.AllocateInode(); + EXPECT_TRUE(result.has_value()); + EXPECT_NE(result.value(), nullptr); + inodes.push_back(result.value()); + } + + // 释放所有 inode + for (auto* inode : inodes) { + auto free_result = ramfs_.FreeInode(inode); + EXPECT_TRUE(free_result.has_value()); + } +} + +// 测试文件创建和查找 +TEST_F(RamFsTest, CreateAndLookupFile) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + ASSERT_NE(root->ops, nullptr); + + // 创建文件 + auto create_result = + root->ops->Create(root, "testfile.txt", FileType::kRegular); + EXPECT_TRUE(create_result.has_value()); + + Inode* file_inode = create_result.value(); + EXPECT_NE(file_inode, nullptr); + EXPECT_EQ(file_inode->type, FileType::kRegular); + + // 查找文件 + auto lookup_result = root->ops->Lookup(root, "testfile.txt"); + EXPECT_TRUE(lookup_result.has_value()); + EXPECT_EQ(lookup_result.value(), file_inode); + + // 查找不存在的文件 + lookup_result = root->ops->Lookup(root, "nonexistent.txt"); + EXPECT_FALSE(lookup_result.has_value()); +} + +// 测试目录创建 +TEST_F(RamFsTest, CreateDirectory) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + ASSERT_NE(root->ops, nullptr); + + // 创建目录 + auto mkdir_result = root->ops->Mkdir(root, "testdir"); + EXPECT_TRUE(mkdir_result.has_value()); + + Inode* dir_inode = mkdir_result.value(); + EXPECT_NE(dir_inode, nullptr); + EXPECT_EQ(dir_inode->type, FileType::kDirectory); +} + +// 测试文件删除 +TEST_F(RamFsTest, UnlinkFile) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + + // 先创建文件 + auto create_result = + root->ops->Create(root, "todelete.txt", FileType::kRegular); + ASSERT_TRUE(create_result.has_value()); + + // 删除文件 + auto unlink_result = root->ops->Unlink(root, "todelete.txt"); + EXPECT_TRUE(unlink_result.has_value()); + + // 确认文件已删除 + auto lookup_result = root->ops->Lookup(root, "todelete.txt"); + EXPECT_FALSE(lookup_result.has_value()); +} + +// 测试目录删除 +TEST_F(RamFsTest, Rmdir) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + + // 创建目录 + auto mkdir_result = root->ops->Mkdir(root, "dir_to_remove"); + ASSERT_TRUE(mkdir_result.has_value()); + + // 删除目录 + auto rmdir_result = root->ops->Rmdir(root, "dir_to_remove"); + EXPECT_TRUE(rmdir_result.has_value()); + + // 确认目录已删除 + auto lookup_result = root->ops->Lookup(root, "dir_to_remove"); + EXPECT_FALSE(lookup_result.has_value()); +} + +// 测试文件读写 +TEST_F(RamFsTest, FileReadWrite) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + + // 创建文件 + auto create_result = + root->ops->Create(root, "rwtest.txt", FileType::kRegular); + ASSERT_TRUE(create_result.has_value()); + + Inode* file_inode = create_result.value(); + + // 创建 File 对象 + File file; + file.inode = file_inode; + file.offset = 0; + file.ops = ramfs_.GetFileOps(); + + // 写入数据 + const char* write_data = "Hello, RamFS!"; + size_t write_len = strlen(write_data); + + auto write_result = file.ops->Write(&file, write_data, write_len); + EXPECT_TRUE(write_result.has_value()); + EXPECT_EQ(write_result.value(), write_len); + + // 重置偏移量并读取 + file.offset = 0; + char read_buffer[64] = {0}; + + auto read_result = file.ops->Read(&file, read_buffer, sizeof(read_buffer)); + EXPECT_TRUE(read_result.has_value()); + EXPECT_EQ(read_result.value(), write_len); + EXPECT_STREQ(read_buffer, write_data); +} + +// 测试文件 seek +TEST_F(RamFsTest, FileSeek) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + + // 创建文件并写入数据 + auto create_result = + root->ops->Create(root, "seektest.txt", FileType::kRegular); + ASSERT_TRUE(create_result.has_value()); + + File file; + file.inode = create_result.value(); + file.offset = 0; + file.ops = ramfs_.GetFileOps(); + + const char* data = "ABCDEFGHIJ"; + file.ops->Write(&file, data, strlen(data)); + + // SEEK_SET + auto seek_result = file.ops->Seek(&file, 5, SeekWhence::kSet); + EXPECT_TRUE(seek_result.has_value()); + EXPECT_EQ(seek_result.value(), 5u); + + // SEEK_CUR + seek_result = file.ops->Seek(&file, 2, SeekWhence::kCur); + EXPECT_TRUE(seek_result.has_value()); + EXPECT_EQ(seek_result.value(), 7u); + + // SEEK_END + seek_result = file.ops->Seek(&file, -3, SeekWhence::kEnd); + EXPECT_TRUE(seek_result.has_value()); + EXPECT_EQ(seek_result.value(), strlen(data) - 3); +} + +// 测试读取目录 +TEST_F(RamFsTest, ReadDirectory) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + + // 创建一些文件和目录 + root->ops->Create(root, "file1.txt", FileType::kRegular); + root->ops->Create(root, "file2.txt", FileType::kRegular); + root->ops->Mkdir(root, "dir1"); + + // 创建 File 对象用于 readdir + File dir_file; + dir_file.inode = root; + dir_file.offset = 0; + dir_file.ops = ramfs_.GetFileOps(); + + DirEntry entries[16]; + auto readdir_result = dir_file.ops->ReadDir(&dir_file, entries, 16); + EXPECT_TRUE(readdir_result.has_value()); + + // 应该至少有 . 和 .. 加上我们创建的文件和目录 + size_t count = readdir_result.value(); + EXPECT_GE(count, 2u); // 至少包含 . 和 .. +} + +// 测试重复创建文件 +TEST_F(RamFsTest, CreateDuplicateFile) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + + // 创建文件 + auto create_result = + root->ops->Create(root, "duplicate.txt", FileType::kRegular); + EXPECT_TRUE(create_result.has_value()); + + // 重复创建应该失败 + create_result = root->ops->Create(root, "duplicate.txt", FileType::kRegular); + EXPECT_FALSE(create_result.has_value()); +} + +// 测试删除非空目录 +TEST_F(RamFsTest, RmdirNonEmpty) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + + // 创建目录 + auto mkdir_result = root->ops->Mkdir(root, "nonempty_dir"); + ASSERT_TRUE(mkdir_result.has_value()); + + Inode* dir = mkdir_result.value(); + + // 在目录中创建文件 + dir->ops->Create(dir, "file_inside.txt", FileType::kRegular); + + // 尝试删除非空目录应该失败 + auto rmdir_result = root->ops->Rmdir(root, "nonempty_dir"); + EXPECT_FALSE(rmdir_result.has_value()); +} + +// 测试删除不存在的文件 +TEST_F(RamFsTest, UnlinkNonExistent) { + Inode* root = ramfs_.GetRootInode(); + ASSERT_NE(root, nullptr); + + auto unlink_result = root->ops->Unlink(root, "nonexistent.txt"); + EXPECT_FALSE(unlink_result.has_value()); +} + +// 测试 Sync(ramfs 应该立即返回成功) +TEST_F(RamFsTest, Sync) { + auto sync_result = ramfs_.Sync(); + EXPECT_TRUE(sync_result.has_value()); +} diff --git a/tests/unit_test/sk_libc_test.cpp b/tests/unit_test/sk_libc_test.cpp index 8c10f0f9f..6ba1e3e4f 100644 --- a/tests/unit_test/sk_libc_test.cpp +++ b/tests/unit_test/sk_libc_test.cpp @@ -46,7 +46,7 @@ TEST(SkLibcTest, Atof) { } TEST(SkLibcTest, Strtol) { - char *end; + char* end; EXPECT_EQ(sk_strtol("123", &end, 10), 123L); EXPECT_EQ(*end, '0円'); @@ -70,14 +70,14 @@ TEST(SkLibcTest, Strtol) { } TEST(SkLibcTest, Strtoul) { - char *end; + char* end; EXPECT_EQ(sk_strtoul("123", &end, 10), 123UL); EXPECT_EQ(sk_strtoul("0xFF", &end, 16), 255UL); EXPECT_EQ(sk_strtoul("11", &end, 2), 3UL); } TEST(SkLibcTest, Strtoll) { - char *end; + char* end; // min for 64bit signed // -9223372036854775808 EXPECT_EQ(sk_strtoll("-9223372036854775808", &end, 10), @@ -85,7 +85,7 @@ TEST(SkLibcTest, Strtoll) { } TEST(SkLibcTest, Strtoull) { - char *end; + char* end; // max for 64bit unsigned // 18446744073709551615 EXPECT_EQ(sk_strtoull("18446744073709551615", &end, 10), @@ -93,7 +93,7 @@ TEST(SkLibcTest, Strtoull) { } TEST(SkLibcTest, Strtod) { - char *end; + char* end; EXPECT_DOUBLE_EQ(sk_strtod("3.14159", &end), 3.14159); EXPECT_EQ(*end, '0円'); EXPECT_DOUBLE_EQ(sk_strtod(" -123.456abc", &end), -123.456); @@ -101,7 +101,7 @@ TEST(SkLibcTest, Strtod) { } TEST(SkLibcTest, Strtof) { - char *end; + char* end; EXPECT_FLOAT_EQ(sk_strtof("3.14", &end), 3.14f); } @@ -140,7 +140,7 @@ TEST(SkLibcTest, AtofEdgeCases) { } TEST(SkLibcTest, StrtolEdgeCases) { - char *end; + char* end; // 空字符串 EXPECT_EQ(sk_strtol("", &end, 10), 0L); @@ -158,7 +158,7 @@ TEST(SkLibcTest, StrtolEdgeCases) { } TEST(SkLibcTest, StrtoulEdgeCases) { - char *end; + char* end; // 最大无符号值 EXPECT_EQ(sk_strtoul("4294967295", &end, 10), 4294967295UL); @@ -171,7 +171,7 @@ TEST(SkLibcTest, StrtoulEdgeCases) { } TEST(SkLibcTest, StrtollEdgeCases) { - char *end; + char* end; // 零 EXPECT_EQ(sk_strtoll("0", &end, 10), 0LL); @@ -182,7 +182,7 @@ TEST(SkLibcTest, StrtollEdgeCases) { } TEST(SkLibcTest, StrtoullEdgeCases) { - char *end; + char* end; // 零 EXPECT_EQ(sk_strtoull("0", &end, 10), 0ULL); @@ -192,7 +192,7 @@ TEST(SkLibcTest, StrtoullEdgeCases) { } TEST(SkLibcTest, StrtodEdgeCases) { - char *end; + char* end; // 无穷大(如果支持) // EXPECT_TRUE(std::isinf(sk_strtod("inf", &end))); @@ -205,7 +205,7 @@ TEST(SkLibcTest, StrtodEdgeCases) { } TEST(SkLibcTest, StrtofEdgeCases) { - char *end; + char* end; // 零 EXPECT_FLOAT_EQ(sk_strtof("0.0", &end), 0.0f); @@ -215,7 +215,7 @@ TEST(SkLibcTest, StrtofEdgeCases) { } TEST(SkLibcTest, BaseDetection) { - char *end; + char* end; // Base 0 应该自动检测 EXPECT_EQ(sk_strtol("0x10", &end, 0), 16L); @@ -238,7 +238,7 @@ TEST(SkLibcTest, SignHandling) { } TEST(SkLibcTest, PartialConversion) { - char *end; + char* end; // 部分转换 EXPECT_EQ(sk_strtol("123abc", &end, 10), 123L); diff --git a/tests/unit_test/spinlock_test.cpp b/tests/unit_test/spinlock_test.cpp index b9c85f938..2750d4ac1 100644 --- a/tests/unit_test/spinlock_test.cpp +++ b/tests/unit_test/spinlock_test.cpp @@ -67,11 +67,11 @@ TEST_F(SpinLockTest, InterruptControl) { // 初始状态中断是开启的 EXPECT_TRUE(cpu_io::GetInterruptStatus()); - lock.Lock(); + (void)lock.Lock(); // 加锁后中断应该被禁用 EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock.UnLock(); + (void)lock.UnLock(); // 解锁后中断应该被恢复 EXPECT_TRUE(cpu_io::GetInterruptStatus()); } @@ -84,10 +84,10 @@ TEST_F(SpinLockTest, InterruptRestore) { cpu_io::DisableInterrupt(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock.Lock(); + (void)lock.Lock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock.UnLock(); + (void)lock.UnLock(); // 解锁后中断应该保持关闭(恢复原状) EXPECT_FALSE(cpu_io::GetInterruptStatus()); @@ -109,11 +109,11 @@ TEST_F(SpinLockTest, ConcurrentAccess) { env_state_.BindThreadToCore(std::this_thread::get_id(), i % env_state_.GetCoreCount()); for (int j = 0; j < increments_per_thread; ++j) { - lock.Lock(); + (void)lock.Lock(); int temp = shared_counter.load(); std::this_thread::sleep_for(std::chrono::microseconds(1)); shared_counter.store(temp + 1); - lock.UnLock(); + (void)lock.UnLock(); } }); } @@ -163,17 +163,17 @@ TEST_F(SpinLockTest, NestedInterruptControl) { EXPECT_TRUE(cpu_io::GetInterruptStatus()); - lock1.Lock(); + (void)lock1.Lock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); // 嵌套加锁 - lock2.Lock(); + (void)lock2.Lock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock2.UnLock(); + (void)lock2.UnLock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); // 仍然禁用 - lock1.UnLock(); + (void)lock1.UnLock(); EXPECT_TRUE(cpu_io::GetInterruptStatus()); // 恢复 } @@ -199,10 +199,10 @@ TEST_F(SpinLockTest, MultipleLockIndependence) { SpinLockTestable lock2("independent_test2"); // 锁1和锁2应该是独立的 - lock1.Lock(); + (void)lock1.Lock(); EXPECT_TRUE(lock2.Lock()); - lock1.UnLock(); + (void)lock1.UnLock(); EXPECT_TRUE(lock2.UnLock()); } @@ -214,8 +214,8 @@ TEST_F(SpinLockTest, PerformanceTest) { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { - lock.Lock(); - lock.UnLock(); + (void)lock.Lock(); + (void)lock.UnLock(); } auto end = std::chrono::high_resolution_clock::now(); @@ -237,8 +237,8 @@ TEST_F(SpinLockTest, EdgeCases) { // 测试快速连续的 lock/UnLock for (int i = 0; i < 1000; ++i) { - lock.Lock(); - lock.UnLock(); + (void)lock.Lock(); + (void)lock.UnLock(); } EXPECT_TRUE(cpu_io::GetInterruptStatus()); // 中断状态应该正确恢复 @@ -250,13 +250,13 @@ TEST_F(SpinLockTest, InterruptDisabledDuringLock) { EXPECT_TRUE(cpu_io::GetInterruptStatus()); - lock.Lock(); + (void)lock.Lock(); // 锁持有期间中断应该被禁用 EXPECT_FALSE(cpu_io::GetInterruptStatus()); EXPECT_TRUE(lock.IsLockedByCurrentCore()); - lock.UnLock(); + (void)lock.UnLock(); // 解锁后中断应该恢复 EXPECT_TRUE(cpu_io::GetInterruptStatus()); @@ -278,13 +278,13 @@ TEST_F(SpinLockTest, FairnessTest) { i % env_state_.GetCoreCount()); std::this_thread::sleep_for(std::chrono::milliseconds(i * 10)); - lock.Lock(); + (void)lock.Lock(); { std::lock_guard guard(order_mutex); execution_order.push_back(i); } std::this_thread::sleep_for(std::chrono::milliseconds(10)); - lock.UnLock(); + (void)lock.UnLock(); }); } @@ -312,9 +312,9 @@ TEST_F(SpinLockTest, HighLoadPerformance) { env_state_.BindThreadToCore(std::this_thread::get_id(), i % env_state_.GetCoreCount()); for (int j = 0; j < operations_per_thread; ++j) { - lock.Lock(); + (void)lock.Lock(); total_operations.fetch_add(1); - lock.UnLock(); + (void)lock.UnLock(); } }); } @@ -335,23 +335,23 @@ TEST_F(SpinLockTest, NestedInterruptSaveRestore) { // 初始状态:中断开启 EXPECT_TRUE(cpu_io::GetInterruptStatus()); - lock1.Lock(); + (void)lock1.Lock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock2.Lock(); + (void)lock2.Lock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock3.Lock(); + (void)lock3.Lock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); // 按相反顺序解锁 - lock3.UnLock(); + (void)lock3.UnLock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock2.UnLock(); + (void)lock2.UnLock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock1.UnLock(); + (void)lock1.UnLock(); EXPECT_TRUE(cpu_io::GetInterruptStatus()); // 恢复原始状态 } @@ -363,9 +363,9 @@ TEST_F(SpinLockTest, NoContentionSingleThread) { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < iterations; ++i) { - lock.Lock(); + (void)lock.Lock(); shared_counter++; - lock.UnLock(); + (void)lock.UnLock(); } auto end = std::chrono::high_resolution_clock::now(); @@ -390,7 +390,7 @@ TEST_F(SpinLockTest, LongHoldTime) { env_state_.SetCurrentThreadEnvironment(); env_state_.BindThreadToCore(std::this_thread::get_id(), 1 % env_state_.GetCoreCount()); - lock.Lock(); + (void)lock.Lock(); lock_held = true; // 等待 waiter 线程开始尝试获取锁 @@ -400,7 +400,7 @@ TEST_F(SpinLockTest, LongHoldTime) { // 持有锁一段时间 std::this_thread::sleep_for(std::chrono::milliseconds(50)); - lock.UnLock(); + (void)lock.UnLock(); }); std::thread waiter([this, &lock, &lock_held, &spin_count, &waiter_started]() { @@ -418,7 +418,7 @@ TEST_F(SpinLockTest, LongHoldTime) { auto start_time = std::chrono::steady_clock::now(); // 尝试获取锁(会自旋等待) - lock.Lock(); + (void)lock.Lock(); auto end_time = std::chrono::steady_clock::now(); auto wait_duration = std::chrono::duration_cast( @@ -429,7 +429,7 @@ TEST_F(SpinLockTest, LongHoldTime) { spin_count.fetch_add(1); } - lock.UnLock(); + (void)lock.UnLock(); }); holder.join(); @@ -451,10 +451,10 @@ TEST_F(SpinLockTest, MultipleThreads) { env_state_.BindThreadToCore(std::this_thread::get_id(), i % env_state_.GetCoreCount()); for (int j = 0; j < 100; ++j) { - lock.Lock(); + (void)lock.Lock(); thread_results[i]++; std::this_thread::sleep_for(std::chrono::microseconds(10)); - lock.UnLock(); + (void)lock.UnLock(); } }); } @@ -477,18 +477,18 @@ TEST_F(SpinLockTest, StateConsistency) { EXPECT_TRUE(cpu_io::GetInterruptStatus()); // 加锁 - lock.Lock(); + (void)lock.Lock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); // 解锁 - lock.UnLock(); + (void)lock.UnLock(); EXPECT_TRUE(cpu_io::GetInterruptStatus()); // 多次循环验证状态一致性 for (int i = 0; i < 100; ++i) { - lock.Lock(); + (void)lock.Lock(); EXPECT_FALSE(cpu_io::GetInterruptStatus()); - lock.UnLock(); + (void)lock.UnLock(); EXPECT_TRUE(cpu_io::GetInterruptStatus()); } } diff --git a/tests/unit_test/task/task_scheduling_example_test.cpp b/tests/unit_test/task/task_scheduling_example_test.cpp index fe3c5c5be..cc216f71d 100644 --- a/tests/unit_test/task/task_scheduling_example_test.cpp +++ b/tests/unit_test/task/task_scheduling_example_test.cpp @@ -74,7 +74,7 @@ TEST_F(TaskSchedulingExample, SwitchToRecordsHistory) { env.RegisterTaskContext(&task2.task_context, &task2); // 获取当前核心环境 - auto& core_env = env.GetCurrentCoreEnv(); + (void)env.GetCurrentCoreEnv(); // 设置初始 tick(通过 PerCpu 的 sched_data) // 注意:这里假设测试已经设置了 sched_data @@ -183,8 +183,6 @@ TEST_F(TaskSchedulingExample, InterruptStatusDuringSwitch) { * @brief 示例:验证页表切换 */ TEST_F(TaskSchedulingExample, PageTableSwitchBetweenTasks) { - auto& env = GetEnvironmentState(); - const uint64_t kernel_pd = 0x1000; const uint64_t user_task1_pd = 0x2000; const uint64_t user_task2_pd = 0x3000; diff --git a/tests/unit_test/vfs_test.cpp b/tests/unit_test/vfs_test.cpp new file mode 100644 index 000000000..85f3a1859 --- /dev/null +++ b/tests/unit_test/vfs_test.cpp @@ -0,0 +1,327 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + * @brief VFS 单元测试 + */ + +#include "vfs.hpp" + +#include + +#include "file_descriptor.hpp" +#include "filesystem.hpp" +#include "mount.hpp" +#include "test_environment_state.hpp" + +using namespace filesystem; +using namespace vfs; + +// Mock FileOps for testing +class MockFileOps : public FileOps { + public: + auto Read(File*, void*, size_t) -> Expected override { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + auto Write(File*, const void*, size_t) -> Expected override { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + auto Seek(File*, int64_t, SeekWhence) -> Expected override { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } + auto Close(File*) -> Expected override { return {}; } + auto ReadDir(File*, DirEntry*, size_t) -> Expected override { + return std::unexpected(Error(ErrorCode::kDeviceNotSupported)); + } +}; + +// Mock file system for testing +class MockFs : public FileSystem { + public: + mutable bool mount_called = false; + mutable bool unmount_called = false; + mutable bool sync_called = false; + mutable BlockDevice* last_device = nullptr; + MockFileOps mock_file_ops_; + + Inode root_inode; + + MockFs() { + root_inode.type = FileType::kDirectory; + root_inode.ino = 1; + root_inode.ops = nullptr; // MockFs doesn't provide InodeOps + } + + [[nodiscard]] auto GetName() const -> const char* override { + return "mockfs"; + } + + auto Mount(BlockDevice* device) -> Expected override { + mount_called = true; + last_device = device; + return &root_inode; + } + + auto Unmount() -> Expected override { + unmount_called = true; + return {}; + } + + auto Sync() -> Expected override { + sync_called = true; + return {}; + } + + auto AllocateInode() -> Expected override { + return std::unexpected(Error(ErrorCode::kOutOfMemory)); + } + + auto FreeInode(Inode* /*inode*/) -> Expected override { return {}; } + + auto GetFileOps() -> FileOps* override { return &mock_file_ops_; } +}; + +// VFS 基础测试 +class VfsTest : public ::testing::Test { + protected: + void SetUp() override { + env_state_.InitializeCores(1); + env_state_.SetCurrentThreadEnvironment(); + env_state_.BindThreadToCore(std::this_thread::get_id(), 0); + auto result = vfs::Init(); + EXPECT_TRUE(result.has_value()); + } + + void TearDown() override { env_state_.ClearCurrentThreadEnvironment(); } + + test_env::TestEnvironmentState env_state_; +}; + +class BaseEnvTest : public ::testing::Test { + protected: + void SetUp() override { + env_state_.InitializeCores(1); + env_state_.SetCurrentThreadEnvironment(); + env_state_.BindThreadToCore(std::this_thread::get_id(), 0); + } + void TearDown() override { env_state_.ClearCurrentThreadEnvironment(); } + test_env::TestEnvironmentState env_state_; +}; + +// 测试挂载表 +class MountTableTest : public BaseEnvTest {}; +TEST_F(MountTableTest, MountAndUnmount) { + MountTable mount_table; + MockFs mock_fs; + + // 测试挂载 + auto mount_result = mount_table.Mount("/", &mock_fs, nullptr); + EXPECT_TRUE(mount_result.has_value()); + EXPECT_TRUE(mock_fs.mount_called); + + // 测试重复挂载 + mount_result = mount_table.Mount("/", &mock_fs, nullptr); + EXPECT_FALSE(mount_result.has_value()); + + // 测试卸载 + auto unmount_result = mount_table.Unmount("/"); + (void)unmount_result; + EXPECT_TRUE(unmount_result.has_value()); + EXPECT_TRUE(mock_fs.unmount_called); + + // 测试卸载未挂载的路径 + unmount_result = mount_table.Unmount("/mnt"); + EXPECT_FALSE(unmount_result.has_value()); +} + +TEST_F(MountTableTest, LookupMountPoint) { + MountTable mount_table; + MockFs mock_fs; + + auto mount_result = mount_table.Mount("/", &mock_fs, nullptr); + EXPECT_TRUE(mount_result.has_value()); + + // 查找根挂载点 + auto* mp = mount_table.Lookup("/file.txt"); + EXPECT_NE(mp, nullptr); + + // 查找不存在挂载的路径 + mp = mount_table.Lookup("/mnt/nonexistent/file"); + EXPECT_NE(mp, nullptr); // 应该返回根挂载点 + (void)mount_table.Unmount("/"); +} + +// 测试文件描述符表 +class FdTableTest : public BaseEnvTest { + protected: + FileDescriptorTable* fd_table_; + + void SetUp() override { + BaseEnvTest::SetUp(); + fd_table_ = new FileDescriptorTable(); + } + + void TearDown() override { + delete fd_table_; + BaseEnvTest::TearDown(); + } +}; + +TEST_F(FdTableTest, AllocAndFree) { + // 创建模拟文件 + File mock_file; + + // 分配 fd + auto alloc_result = fd_table_->Alloc(&mock_file); + EXPECT_TRUE(alloc_result.has_value()); + int fd = alloc_result.value(); + EXPECT_GE(fd, 3); // 0/1/2 预留给标准流 + + // 获取文件 + File* file = fd_table_->Get(fd); + EXPECT_EQ(file, &mock_file); + + // 释放 fd + auto free_result = fd_table_->Free(fd); + EXPECT_TRUE(free_result.has_value()); + + // 再次获取应该返回 nullptr + file = fd_table_->Get(fd); + EXPECT_EQ(file, nullptr); +} + +TEST_F(FdTableTest, InvalidFd) { + // 无效 fd + File* file = fd_table_->Get(-1); + EXPECT_EQ(file, nullptr); + + file = fd_table_->Get(999); + EXPECT_EQ(file, nullptr); + + // 释放无效 fd + auto free_result = fd_table_->Free(-1); + EXPECT_FALSE(free_result.has_value()); +} + +TEST_F(FdTableTest, DupFd) { + File mock_file; + + // 分配 fd + auto alloc_result = fd_table_->Alloc(&mock_file); + EXPECT_TRUE(alloc_result.has_value()); + int fd1 = alloc_result.value(); + + // 复制 fd + auto dup_result = fd_table_->Dup(fd1); + EXPECT_TRUE(dup_result.has_value()); + int fd2 = dup_result.value(); + + // fd1 和 fd2 应该指向同一个文件 + EXPECT_EQ(fd_table_->Get(fd1), fd_table_->Get(fd2)); + + // 清理 + (void)fd_table_->Free(fd1); + (void)fd_table_->Free(fd2); +} + +TEST_F(FdTableTest, SetupStandardFiles) { + File stdin_file; + File stdout_file; + File stderr_file; + + auto setup_result = + fd_table_->SetupStandardFiles(&stdin_file, &stdout_file, &stderr_file); + EXPECT_TRUE(setup_result.has_value()); + + // 检查标准文件描述符 + EXPECT_EQ(fd_table_->Get(0), &stdin_file); + EXPECT_EQ(fd_table_->Get(1), &stdout_file); + EXPECT_EQ(fd_table_->Get(2), &stderr_file); +} + +// VFS 路径解析测试 +TEST_F(VfsTest, LookupRoot) { + // 挂载 mock 文件系统作为根 + MockFs mock_fs; + auto& mount_table = GetMountTable(); + + auto mount_result = mount_table.Mount("/", &mock_fs, nullptr); + EXPECT_TRUE(mount_result.has_value()); + + // 查找根目录 + auto lookup_result = vfs::Lookup("/"); + EXPECT_TRUE(lookup_result.has_value()); + EXPECT_NE(lookup_result.value(), nullptr); +} + +TEST_F(VfsTest, LookupInvalidPaths) { + // 空路径 + auto result = vfs::Lookup(nullptr); + EXPECT_FALSE(result.has_value()); + + // 相对路径 + result = vfs::Lookup("relative/path"); + EXPECT_FALSE(result.has_value()); +} + +// VFS 初始化测试 +class VfsInitTest : public BaseEnvTest {}; +TEST_F(VfsInitTest, DoubleInit) { + // 第一次初始化 + auto result = vfs::Init(); + EXPECT_TRUE(result.has_value()); + + // 重复初始化应该成功(幂等) + result = vfs::Init(); + EXPECT_TRUE(result.has_value()); +} + +// File 结构测试 +TEST(FileStructTest, FileOperations) { + File file; + EXPECT_EQ(file.offset, 0); + EXPECT_EQ(file.flags, 0); + EXPECT_EQ(file.ops, nullptr); + EXPECT_EQ(file.inode, nullptr); + EXPECT_EQ(file.dentry, nullptr); +} + +// Inode 结构测试 +TEST(InodeStructTest, InodeDefaults) { + Inode inode; + EXPECT_EQ(inode.ino, 0); + EXPECT_EQ(inode.type, FileType::kUnknown); + EXPECT_EQ(inode.size, 0); + EXPECT_EQ(inode.permissions, 0644); + EXPECT_EQ(inode.link_count, 1); + EXPECT_EQ(inode.fs_private, nullptr); + EXPECT_EQ(inode.fs, nullptr); + EXPECT_EQ(inode.ops, nullptr); +} + +// Dentry 结构测试 +TEST(DentryStructTest, DentryDefaults) { + Dentry dentry; + EXPECT_EQ(dentry.name[0], '0円'); + EXPECT_EQ(dentry.inode, nullptr); + EXPECT_EQ(dentry.parent, nullptr); + EXPECT_EQ(dentry.children, nullptr); + EXPECT_EQ(dentry.next_sibling, nullptr); + EXPECT_EQ(dentry.fs_private, nullptr); +} + +// OpenFlags 测试 +TEST(OpenFlagsTest, FlagValues) { + EXPECT_EQ(kOReadOnly, 0x0000u); + EXPECT_EQ(kOWriteOnly, 0x0001u); + EXPECT_EQ(kOReadWrite, 0x0002u); + EXPECT_EQ(kOCreate, 0x0040u); + EXPECT_EQ(kOTruncate, 0x0200u); + EXPECT_EQ(kOAppend, 0x0400u); + EXPECT_EQ(kODirectory, 0x010000u); +} + +// SeekWhence 测试 +TEST(SeekWhenceTest, EnumValues) { + EXPECT_EQ(static_cast(SeekWhence::kSet), 0); + EXPECT_EQ(static_cast(SeekWhence::kCur), 1); + EXPECT_EQ(static_cast(SeekWhence::kEnd), 2); +} diff --git a/tests/unit_test/virtual_memory_test.cpp b/tests/unit_test/virtual_memory_test.cpp index c537dc3da..700d345b1 100644 --- a/tests/unit_test/virtual_memory_test.cpp +++ b/tests/unit_test/virtual_memory_test.cpp @@ -13,6 +13,8 @@ #include #include +#include "basic_info.hpp" +#include "singleton.hpp" #include "test_environment_state.hpp" namespace { diff --git a/tools/.pre-commit-config.yaml.in b/tools/.pre-commit-config.yaml.in index a55ce30a6..aff684096 100644 --- a/tools/.pre-commit-config.yaml.in +++ b/tools/.pre-commit-config.yaml.in @@ -1,7 +1,7 @@ fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-case-conflict - id: check-illegal-windows-names @@ -17,7 +17,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/cpp-linter/cpp-linter-hooks - rev: v0.5.1 + rev: v1.1.12 hooks: - id: clang-format args: @@ -48,7 +48,7 @@ repos: # - --exclude-header-filter=^(?@CMAKE_BINARY_DIR@/src/arch/riscv64|@CMAKE_BINARY_DIR@/src/arch/x86_64).* - repo: https://github.com/koalaman/shellcheck-precommit - rev: v0.10.0 + rev: v0.11.0 hooks: - id: shellcheck diff --git a/tools/aarch64_boot_scr.txt b/tools/aarch64_boot_scr.txt index 98f37f07c..db27661f5 100644 --- a/tools/aarch64_boot_scr.txt +++ b/tools/aarch64_boot_scr.txt @@ -1,3 +1,14 @@ +# 通过 DHCP/BOOTP 获取网络地址,用于后续 TFTP 下载 bootp + +# 将 FIT 镜像加载地址设为 0x44000000,避免与内核解压目标地址 (0x40100000) 重叠。 +# U-Boot 默认的 kernel_addr_r (0x40400000) 距内核 load address 仅 3 MiB, +# 当 FIT 镜像超过该间距时,bootm 解压内核会覆盖 FIT 源数据,导致: +# ERROR: new format image overwritten - must RESET the board to recover +setenv kernel_addr_r 0x44000000 + +# 通过 TFTP 从宿主机下载 FIT 镜像 (包含 kernel.elf + DTB) tftp $kernel_addr_r bin/boot.fit + +# 从 FIT 镜像启动内核,使用 QEMU 提供的 DTB ($fdt_addr) bootm $kernel_addr_r - $fdt_addr

AltStyle によって変換されたページ (->オリジナル) /