diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7a6e048f2..84284a33f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -29,20 +29,19 @@ RUN apt-get update && apt-get upgrade -y && \ python3-setuptools \ uuid-dev \ libgnutls28-dev \ + libssl-dev \ swig \ libgtest-dev \ doxygen \ graphviz \ lcov \ - qemu-system-x86 \ qemu-system-arm \ qemu-system-misc \ gdb-multiarch \ gcc-14 g++-14 \ gcc-14-riscv64-linux-gnu g++-14-riscv64-linux-gnu \ gcc-14-aarch64-linux-gnu g++-14-aarch64-linux-gnu \ - gcc-14-arm-linux-gnueabihf g++-14-arm-linux-gnueabihf \ - gcc-14-x86-64-linux-gnu g++-14-x86-64-linux-gnu && \ + gcc-14-arm-linux-gnueabihf g++-14-arm-linux-gnueabihf && \ update-alternatives \ --install /usr/bin/gcc gcc /usr/bin/gcc-14 100 \ --slave /usr/bin/g++ g++ /usr/bin/g++-14 \ @@ -56,16 +55,14 @@ RUN apt-get update && apt-get upgrade -y && \ --install /usr/bin/aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc \ /usr/bin/aarch64-linux-gnu-gcc-14 100 \ --slave /usr/bin/aarch64-linux-gnu-g++ aarch64-linux-gnu-g++ \ - /usr/bin/aarch64-linux-gnu-g++-14 && \ + /usr/bin/aarch64-linux-gnu-g++-14 \ + --slave /usr/bin/aarch64-linux-gnu-cpp aarch64-linux-gnu-cpp \ + /usr/bin/aarch64-linux-gnu-cpp-14 && \ update-alternatives \ --install /usr/bin/arm-linux-gnueabihf-gcc arm-linux-gnueabihf-gcc \ /usr/bin/arm-linux-gnueabihf-gcc-14 100 \ --slave /usr/bin/arm-linux-gnueabihf-g++ arm-linux-gnueabihf-g++ \ /usr/bin/arm-linux-gnueabihf-g++-14 && \ - update-alternatives \ - --install /usr/bin/x86_64-linux-gnu-gcc x86_64-linux-gnu-gcc \ - /usr/bin/x86_64-linux-gnu-gcc-14 100 \ - --slave /usr/bin/x86_64-linux-gnu-g++ x86_64-linux-gnu-g++ \ - /usr/bin/x86_64-linux-gnu-g++-14 && \ apt-get autoremove -y && apt-get clean -y && \ - rm -rf /var/lib/apt/lists/* + rm -rf /var/lib/apt/lists/* && \ + mkdir -p /srv/tftp diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index aca834149..6bc5f5fb2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,8 +18,7 @@ "plorefice.devicetree", "zixuanwang.linkerscript", "dan-c-underwood.arm", - "zhwu95.riscv", - "13xforever.language-x86-64-assembly" + "zhwu95.riscv" ] } } diff --git a/.github/workflows/dev-image.yml b/.github/workflows/dev-image.yml new file mode 100644 index 000000000..0ca13bb53 --- /dev/null +++ b/.github/workflows/dev-image.yml @@ -0,0 +1,48 @@ +# Copyright The SimpleKernel Contributors + +name: dev-image + +on: + push: + branches: [main] + paths: + - '.devcontainer/Dockerfile' + - '.github/workflows/dev-image.yml' + workflow_dispatch: + +env: + IMAGE: ghcr.io/simple-xx/simplekernel-dev + +jobs: + build-and-push: + runs-on: ubuntu-24.04-arm + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: .devcontainer + file: .devcontainer/Dockerfile + push: true + tags: | + ${{ env.IMAGE }}:latest + ${{ env.IMAGE }}:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index bf40ed9b8..deb271cb6 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -3,54 +3,132 @@ name: build on: - - push - - pull_request - - release + push: + pull_request: + release: + types: [published] + +# Cancel in-progress runs for the same branch/PR +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + # PR: 3 runs for speed; push/release: 10 runs for stability + SYSTEM_TEST_RUNS: ${{ github.event_name == 'pull_request' && 3 || 10 }} jobs: - build: - runs-on: ubuntu-latest + build-riscv64: + runs-on: ubuntu-24.04-arm permissions: - contents: write + contents: read packages: read - statuses: write + container: + image: ghcr.io/simple-xx/simplekernel-dev:latest steps: - name: Checkout uses: actions/checkout@v4 with: - fetch-depth: 0 - submodules: recursive + fetch-depth: 1 - - name: x86_64 - uses: devcontainers/ci@v0.3 + - name: Shallow submodule init + run: git submodule update --init --recursive --depth 1 + + - name: Cache CMake build (riscv64) + uses: actions/cache@v4 with: - runCmd: | - cmake --preset=build_x86_64 - cmake --build build_x86_64 --target SimpleKernel unit-test coverage docs + path: build_riscv64/ + key: cmake-riscv64-${{ runner.arch }}-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'src/**') }} + restore-keys: | + cmake-riscv64-${{ runner.arch }}- + + - name: Configure & Build + run: | + cmake --preset=build_riscv64 + cmake --build build_riscv64 --target SimpleKernel docs + + - name: System Test + run: | + for i in $(seq 1 $SYSTEM_TEST_RUNS); do + echo "=== riscv64 System Test Run $i/$SYSTEM_TEST_RUNS ===" + if ! timeout 300 cmake --build build_riscv64 --target system_test_run; then + echo "riscv64 system test run $i/$SYSTEM_TEST_RUNS FAILED" + exit 1 + fi + echo "riscv64 system test run $i/$SYSTEM_TEST_RUNS passed" + done - - name: riscv64 - uses: devcontainers/ci@v0.3 + - name: Upload docs artifact + if: github.ref == 'refs/heads/main' + uses: actions/upload-artifact@v4 with: - runCmd: | - cmake --preset=build_riscv64 - cmake --build build_riscv64 --target SimpleKernel + name: docs-html + path: docs/html/ + retention-days: 1 - - name: aarch64 - uses: devcontainers/ci@v0.3 + build-aarch64: + runs-on: ubuntu-24.04-arm + permissions: + contents: read + packages: read + container: + image: ghcr.io/simple-xx/simplekernel-dev:latest + steps: + - name: Checkout + uses: actions/checkout@v4 with: - runCmd: | - cmake --preset=build_aarch64 - cmake --build build_aarch64 --target SimpleKernel + fetch-depth: 1 + + - name: Shallow submodule init + run: git submodule update --init --recursive --depth 1 + + - name: Cache CMake build (aarch64) + uses: actions/cache@v4 + with: + path: build_aarch64/ + key: cmake-aarch64-${{ runner.arch }}-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'src/**') }} + restore-keys: | + cmake-aarch64-${{ runner.arch }}- + + - name: Configure & Build + run: | + cmake --preset=build_aarch64 -DQEMU_NORMAL_WORLD_DEV_PATH=null -DQEMU_SECURE_WORLD_DEV_PATH=null + cmake --build build_aarch64 --target SimpleKernel unit-test coverage + + - name: System Test + run: | + for i in $(seq 1 $SYSTEM_TEST_RUNS); do + echo "=== aarch64 System Test Run $i/$SYSTEM_TEST_RUNS ===" + timeout 300 cmake --build build_aarch64 --target system_test_run > /tmp/st_out_$i.txt 2>&1 || true + cat /tmp/st_out_$i.txt + if ! grep -q "Failed: 0" /tmp/st_out_$i.txt; then + echo "aarch64 system test run $i/$SYSTEM_TEST_RUNS FAILED" + exit 1 + fi + echo "aarch64 system test run $i/$SYSTEM_TEST_RUNS passed" + done - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: - files: ${{ github.workspace }}/build_x86_64/coverage/coverage.info + files: ${{ github.workspace }}/build_aarch64/coverage/coverage.info verbose: true - - name: Publish - if: github.ref == 'refs/heads/main' - uses: peaceiris/actions-gh-pages@v3 + publish: + needs: [build-riscv64, build-aarch64] + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download docs artifact + uses: actions/download-artifact@v4 + with: + name: docs-html + path: docs/html/ + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ${{ github.workspace }}/docs/html + publish_dir: docs/html/ diff --git a/.gitignore b/.gitignore index 970f3d0e2..c6082907f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ Doxyfile *.elf .pre-commit-config.yaml .worktrees/ +.claude diff --git a/3rd/cpu_io b/3rd/cpu_io index 9c0a81cd6..2d20e494d 160000 --- a/3rd/cpu_io +++ b/3rd/cpu_io @@ -1 +1 @@ -Subproject commit 9c0a81cd6179b886779496a7ef4e8df192763742 +Subproject commit 2d20e494d53dc92b33d254b1a2043b31e08a5e80 diff --git a/AGENTS.md b/AGENTS.md index 91e924fe0..2961d4b98 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ # AGENTS.md — SimpleKernel ## OVERVIEW -Interface-driven OS kernel for AI-assisted learning. C++23/C23, freestanding, no RTTI/exceptions. Three architectures: x86_64, riscv64, aarch64. Headers define contracts (Doxygen @pre/@post), AI generates .cpp implementations, tests verify compliance. +Interface-driven OS kernel for AI-assisted learning. C++23/C23, freestanding, no RTTI/exceptions. Two architectures: riscv64, aarch64. Headers define contracts (Doxygen @pre/@post), AI generates .cpp implementations, tests verify compliance. ## STRUCTURE ``` @@ -72,7 +72,7 @@ cmake/ # Toolchain files, build helpers ## COMMANDS ```bash git submodule update --init --recursive # First clone setup -cmake --preset build_{riscv64|aarch64|x86_64} +cmake --preset build_{riscv64|aarch64} cd build_{arch} && make SimpleKernel # Build kernel (NOT 'make kernel') make run # Run in QEMU make debug # GDB on localhost:1234 @@ -83,7 +83,7 @@ pre-commit run --all-files # Format check ## NOTES - Interface-driven: headers are contracts, .cpp files are implementations AI generates -- Boot chains differ: x86_64 (U-Boot), riscv64 (U-Boot SPL→OpenSBI→U-Boot), aarch64 (U-Boot→ATF→OP-TEE) +- Boot chains differ: riscv64 (U-Boot SPL→OpenSBI→U-Boot), aarch64 (U-Boot→ATF→OP-TEE) - aarch64 needs two serial terminal tasks (::54320, ::54321) before `make run` - Unit tests only run on host arch (`build_{arch}` on {arch} host) - Git commits: `(): ` with `--signoff` diff --git a/CMakePresets.json b/CMakePresets.json index 600adffc9..9aa7cf105 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -77,15 +77,15 @@ }, "QEMU_DEBUG_FLAGS": { "type": "STRING", - "value": "-S;-gdb;tcp::1234;-d;int,cpu_reset,guest_errors" + "value": "-S;-gdb;tcp::1234;-d;cpu_reset,guest_errors" }, "QEMU_COMMON_FLAG": { "type": "STRING", - "value": "-nographic;-serial;stdio;-monitor;telnet::2333,server,nowait;-m;1024M;-smp;2;-d;guest_errors,int,cpu_reset" + "value": "-nographic;-serial;stdio;-monitor;telnet::2333,server,nowait;-m;1024M;-smp;2;-d;guest_errors,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" + "value": "-global;virtio-mmio.force-legacy=false;-netdev;user,id=net0,tftp=/srv/tftp;-device;virtio-net-device,netdev=net0;-device;virtio-gpu-device" }, "KERNEL_ELF_OUTPUT_NAME": { "type": "STRING", @@ -105,43 +105,6 @@ } } }, - { - "name": "build_x86_64", - "hidden": false, - "inherits": [ - "configurePresets_base" - ], - "displayName": "build x86_64 kernel", - "description": "build x86_64 kernel", - "toolchainFile": "${sourceDir}/cmake/x86_64-gcc.cmake", - "binaryDir": "${sourceDir}/build_x86_64", - "cacheVariables": { - "CMAKE_SYSTEM_PROCESSOR": { - "type": "STRING", - "value": "x86_64" - }, - "EXECUTABLE_OUTPUT_PATH": { - "type": "STRING", - "value": "${sourceDir}/build_x86_64/bin" - }, - "LIBRARY_OUTPUT_PATH": { - "type": "STRING", - "value": "${sourceDir}/build_x86_64/lib" - }, - "QEMU_MACHINE_FLAGS": { - "type": "STRING", - "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", - "value": "0" - }, - "SIMPLEKERNEL_PER_CPU_ALIGN_SIZE": { - "type": "STRING", - "value": "128" - } - } - }, { "name": "build_riscv64", "hidden": false, diff --git a/README.md b/README.md index fadcc20f6..e936baf7b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ ## ✨ 项目简介 -SimpleKernel 是一个**面向 AI 辅助学习的现代化操作系统内核项目**。采用 C++23 编写,支持 x86_64、RISC-V 64 和 AArch64 三种架构。 +SimpleKernel 是一个**面向 AI 辅助学习的现代化操作系统内核项目**。采用 C++23 编写,支持 RISC-V 64 和 AArch64 两种架构。 与传统 OS 教学项目不同,SimpleKernel 采用**接口驱动(Interface-Driven)** 的设计: @@ -44,7 +44,7 @@ SimpleKernel 是一个**面向 AI 辅助学习的现代化操作系统内核项 |------|------| | 🤖 **AI-First 设计** | 接口文档即 prompt,AI 可直接根据头文件生成完整实现 | | 📐 **接口与实现分离** | 头文件只有声明和契约,实现在独立的 `.cpp` 中 | -| 🌐 **三架构支持** | x86_64、RISC-V 64、AArch64,同一套接口适配不同硬件 | +| 🌐 **双架构支持** | RISC-V 64、AArch64,同一套接口适配不同硬件 | | 🧪 **测试驱动验证** | GoogleTest 测试套件验证 AI 生成的实现是否符合接口契约 | | 📖 **完整 Doxygen 文档** | 每个接口都有职责描述、前置条件、后置条件、使用示例 | | 🏗️ **工程化基础设施** | CMake 构建、Dev Container 环境、CI/CD、clang-format/clang-tidy | @@ -93,7 +93,7 @@ SimpleKernel 提出一种新范式:**读接口 → 理解契约 → AI 实现 * @pre 硬件中断控制器已初始化 * @post 可通过 RegisterInterruptFunc 注册中断处理函数 * - * 已知实现:PLIC(RISC-V)、GIC(AArch64)、APIC(x86_64) + * 已知实现:PLIC(RISC-V)、GIC(AArch64) */ class InterruptBase { public: @@ -167,7 +167,7 @@ SimpleKernel 的接口按功能分为以下层次: │ libcxx (kstd_vector, __cxa_*, ...) │ ├──────────────────────────────────────────┤ │ 硬件 / QEMU │ -│ x86_64 · RISC-V 64 · AArch64 │ +│ RISC-V 64 · AArch64 │ └──────────────────────────────────────────┘ ``` @@ -194,7 +194,6 @@ SimpleKernel 的接口按功能分为以下层次: | 架构 | 引导链 | 串口 | 中断控制器 | 时钟 | |:---:|:---:|:---:|:---:|:---:| -| **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 | @@ -256,7 +255,6 @@ make unit-test **支持的架构预设:** - `build_riscv64` - RISC-V 64 位架构 - `build_aarch64` - ARM 64 位架构 -- `build_x86_64` - x86 64 位架构 ### 🎯 AI 辅助开发工作流 @@ -293,8 +291,7 @@ SimpleKernel/ │ ├── arch/ # 架构相关代码 │ │ ├── arch.h # 📐 架构无关统一接口 │ │ ├── aarch64/ # AArch64 实现 -│ │ ├── riscv64/ # RISC-V 64 实现 -│ │ └── x86_64/ # x86_64 实现 +│ │ └── riscv64/ # RISC-V 64 实现 │ ├── device/ # 设备管理框架 │ │ ├── include/ # 📐 设备框架接口(DeviceManager, DriverRegistry, Bus 等) │ │ │ └── driver/ # 具体驱动(ns16550a_driver.hpp, virtio_blk_driver.hpp) @@ -336,7 +333,7 @@ SimpleKernel/ | 模块 | 接口文件 | 难度 | 说明 | |------|---------|:---:|------| | 中断基类 | `interrupt_base.h` | ⭐⭐ | 理解中断处理的统一抽象 | -| 中断控制器 | 各架构驱动头文件 | ⭐⭐⭐ | GIC/PLIC/PIC 硬件编程 | +| 中断控制器 | 各架构驱动头文件 | ⭐⭐⭐ | GIC/PLIC 硬件编程 | | 时钟中断 | `arch.h → TimerInit` | ⭐⭐ | 定时器配置,tick 驱动 | ### 阶段 3:内存管理(Memory) diff --git a/README_ENG.md b/README_ENG.md index 5cabeb02c..84b4e4a70 100644 --- a/README_ENG.md +++ b/README_ENG.md @@ -9,7 +9,7 @@ # SimpleKernel -**Interface-Driven OS Kernel for AI-Assisted Learning | Multi-Architecture: x86_64, RISC-V 64, AArch64** +**Interface-Driven OS Kernel for AI-Assisted Learning | Multi-Architecture: RISC-V 64, AArch64** > 🤖 **Design Philosophy**: Define clear kernel interfaces, let AI generate the implementation — a new paradigm for learning operating systems @@ -29,7 +29,7 @@ ## ✨ Project Overview -SimpleKernel is a **modern OS kernel project designed for AI-assisted learning**. Written in C++23, it supports x86_64, RISC-V 64, and AArch64 architectures. +SimpleKernel is a **modern OS kernel project designed for AI-assisted learning**. Written in C++23, it supports RISC-V 64 and AArch64 architectures. Unlike traditional OS teaching projects, SimpleKernel adopts an **Interface-Driven** design: @@ -43,7 +43,7 @@ Unlike traditional OS teaching projects, SimpleKernel adopts an **Interface-Driv |---------|-------------| | 🤖 **AI-First Design** | Interface docs serve as prompts — AI can generate complete implementations directly from header files | | 📐 **Interface-Implementation Separation** | Headers contain only declarations and contracts; implementations live in separate `.cpp` files | -| 🌐 **Three-Architecture Support** | x86_64, RISC-V 64, AArch64 — one set of interfaces adapting to different hardware | +| 🌐 **Two-Architecture Support** | RISC-V 64, AArch64 — one set of interfaces adapting to different hardware | | 🧪 **Test-Driven Verification** | GoogleTest test suites verify whether AI-generated implementations conform to interface contracts | | 📖 **Complete Doxygen Documentation** | Every interface has responsibility descriptions, preconditions, postconditions, and usage examples | | 🏗️ **Engineering Infrastructure** | CMake build, Dev Container environment, CI/CD, clang-format/clang-tidy | @@ -93,7 +93,7 @@ Each module's header file contains complete interface documentation: * @pre Hardware interrupt controller has been initialized * @post Can register interrupt handlers via RegisterInterruptFunc * - * Known implementations: PLIC (RISC-V), GIC (AArch64), APIC (x86_64) + * Known implementations: PLIC (RISC-V), GIC (AArch64) */ class InterruptBase { public: @@ -168,7 +168,7 @@ SimpleKernel's interfaces are organized into the following layers: │ libcxx (kstd_vector, __cxa_*, ...) │ ├──────────────────────────────────────────┤ │ Hardware / QEMU │ -│ x86_64 · RISC-V 64 · AArch64 │ +│ RISC-V 64 · AArch64 │ └──────────────────────────────────────────┘ ``` @@ -195,7 +195,6 @@ SimpleKernel's interfaces are organized into the following layers: | Architecture | Boot Chain | Serial | Interrupt Controller | Timer | |:---:|:---:|:---:|:---:|:---:| -| **x86_64** | U-Boot | NS16550A | 8259A PIC | 8253/8254 | | **RISC-V 64** | U-Boot + OpenSBI | SBI Call | Direct Mode | SBI Timer | | **AArch64** | U-Boot + ATF + OP-TEE | PL011 | GICv3 | Generic Timer | @@ -257,7 +256,6 @@ make unit-test **Supported Architecture Presets:** - `build_riscv64` - RISC-V 64-bit architecture - `build_aarch64` - ARM 64-bit architecture -- `build_x86_64` - x86 64-bit architecture ### 🎯 AI-Assisted Development Workflow @@ -294,8 +292,7 @@ SimpleKernel/ │ ├── arch/ # Architecture-specific code │ │ ├── arch.h # 📐 Architecture-independent unified interface │ │ ├── aarch64/ # AArch64 implementation -│ │ ├── riscv64/ # RISC-V 64 implementation -│ │ └── x86_64/ # x86_64 implementation +│ │ └── riscv64/ # RISC-V 64 implementation │ ├── device/ # Device management framework │ │ ├── include/ # 📐 Device framework interfaces (DeviceManager, DriverRegistry, Bus, etc.) │ │ │ └── driver/ # Concrete drivers (ns16550a_driver.hpp, virtio_blk_driver.hpp) @@ -337,7 +334,7 @@ We recommend learning and implementing modules in the following order: | Module | Interface File | Difficulty | Description | |--------|---------------|:---:|-------------| | Interrupt Base | `interrupt_base.h` | ⭐⭐ | Understand unified interrupt abstraction | -| Interrupt Controller | Per-arch driver headers | ⭐⭐⭐ | GIC/PLIC/PIC hardware programming | +| Interrupt Controller | Per-arch driver headers | ⭐⭐⭐ | GIC/PLIC hardware programming | | Timer Interrupt | `arch.h → TimerInit` | ⭐⭐ | Timer configuration, tick-driven | ### Phase 3: Memory Management diff --git a/cmake/3rd.cmake b/cmake/3rd.cmake index 68222c87b..0c1e66eca 100644 --- a/cmake/3rd.cmake +++ b/cmake/3rd.cmake @@ -119,7 +119,6 @@ ADD_CUSTOM_TARGET ( make O=${u-boot_BINARY_DIR} $<$:qemu_arm64_defconfig> $<$:qemu-riscv64_spl_defconfig> - $<$:qemu-x86_64_defconfig> -j${CMAKE_BUILD_PARALLEL_LEVEL} COMMAND make CROSS_COMPILE=${TOOLCHAIN_PREFIX} O=${u-boot_BINARY_DIR} diff --git a/cmake/compile_config.cmake b/cmake/compile_config.cmake index 4b359b836..92d5cccdd 100644 --- a/cmake/compile_config.cmake +++ b/cmake/compile_config.cmake @@ -74,12 +74,6 @@ TARGET_COMPILE_OPTIONS ( # 禁用 new 的异常支持 -fcheck-new # 目标平台编译选项 - $<$: - # 仅使用通用寄存器 - -mgeneral-regs-only - # 禁用 red-zone - -mno-red-zone - > $<$: # 严格对齐 -mstrict-align @@ -120,12 +114,6 @@ ADD_LIBRARY (kernel_compile_options INTERFACE) TARGET_COMPILE_OPTIONS ( kernel_compile_options INTERFACE - $<$: - # @todo 这里需要判断一下能不能都用 large - # 使用 large 内存模型 - # https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mcmodel_003dlarge-4 - -mcmodel=large - > $<$: # 使用 large 内存模型 # https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mcmodel_003dlarge @@ -148,11 +136,6 @@ TARGET_LINK_OPTIONS ( -static # 不链接标准库 -nostdlib - $<$: - # 设置最大页大小为 0x1000(4096) 字节 - -z - max-page-size=0x1000 - > $<$: # 禁用 relax 优化 $<$:-mno-relax> diff --git a/cmake/functions.cmake b/cmake/functions.cmake index 37ad32a44..5ea5e740a 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -121,6 +121,7 @@ FUNCTION(add_run_target) mkimage -T script -d ${CMAKE_SOURCE_DIR}/tools/${CMAKE_SYSTEM_PROCESSOR}_boot_scr.txt $/boot.scr.uimg + COMMAND ${CMAKE_COMMAND} -E make_directory /srv/tftp COMMAND ln -s -f $/boot.scr.uimg /srv/tftp/boot.scr.uimg COMMAND ln -s -f $ /srv/tftp) diff --git a/cmake/x86_64-gcc.cmake b/cmake/x86_64-gcc.cmake deleted file mode 100644 index 5e3d3c769..000000000 --- a/cmake/x86_64-gcc.cmake +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright The SimpleKernel Contributors - -IF(NOT UNIX) - MESSAGE (FATAL_ERROR "Only support Linux.") -ENDIF() - -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() - - 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/docs/0_\345\267\245\345\205\267\351\223\276.md" "b/docs/0_\345\267\245\345\205\267\351\223\276.md" index 13ac44112..71fb9f391 100644 --- "a/docs/0_\345\267\245\345\205\267\351\223\276.md" +++ "b/docs/0_\345\267\245\345\205\267\351\223\276.md" @@ -8,7 +8,7 @@ SimpleKernel 为了保证在各个平台上的可用性,选择了较为通用 2. **内核的编译** - 使用 GCC 工具链进行交叉编译,支持 x86_64、RISC-V 64、AArch64 三种架构。 + 使用 GCC 工具链进行交叉编译,支持 RISC-V 64、AArch64 两种架构。 3. **模拟器** @@ -45,7 +45,6 @@ SimpleKernel 采用现代化的 CMake 构建系统,具有以下特点: 项目使用 `CMakePresets.json` 提供预定义的构建配置: -- **build_x86_64**: x86_64 架构构建配置 - **build_riscv64**: RISC-V 64 架构构建配置 - **build_aarch64**: AArch64 架构构建配置 @@ -53,7 +52,7 @@ SimpleKernel 采用现代化的 CMake 构建系统,具有以下特点: 位于 `./cmake/` 目录下的模块文件: -- **工具链文件**: `x86_64-gcc.cmake`, `riscv64-gcc.cmake`, `aarch64-gcc.cmake` - 定义各架构的交叉编译工具链 +- **工具链文件**: `riscv64-gcc.cmake`, `aarch64-gcc.cmake` - 定义各架构的交叉编译工具链 - **3rd.cmake**: 管理第三方依赖库 - **project_config.cmake**: 项目配置和变量定义 - **compile_config.cmake**: 编译选项和标志配置 @@ -65,13 +64,13 @@ SimpleKernel 采用现代化的 CMake 构建系统,具有以下特点: ```bash # 使用预设配置构建 -cmake --preset build_x86_64 -cd build_x86_64 -make kernel +cmake --preset build_riscv64 +cd build_riscv64 +make SimpleKernel make run # 或者手动指定配置 -cmake -B build_custom -DTARGET_ARCH=x86_64 +cmake -B build_custom -DTARGET_ARCH=riscv64 ``` ## GCC 交叉编译工具链 @@ -80,7 +79,6 @@ SimpleKernel 支持多架构交叉编译,需要根据目标平台配置相应 ### 支持的架构 -- **x86_64**: 64位 x86 架构 - **riscv64**: 64位 RISC-V 架构 - **aarch64**: 64位 ARM 架构 @@ -96,20 +94,14 @@ SimpleKernel 支持多架构交叉编译,需要根据目标平台配置相应 - 从 [ARM Developer](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads) 下载 - Ubuntu: `sudo apt install gcc-aarch64-linux-gnu` -**x86_64 工具链**: -- 通常使用系统自带的 GCC,或安装 `gcc-multilib` - ### VS Code 任务集成 项目提供了预配置的 VS Code 任务,位于 `.vscode/tasks.json`: -- **build_x86_64**: 构建 x86_64 内核 - **build_riscv64**: 构建 RISC-V 64 内核 - **build_aarch64**: 构建 AArch64 内核 -- **run_x86_64**: 在 QEMU 中运行 x86_64 内核 - **run_riscv64**: 在 QEMU 中运行 RISC-V 64 内核 - **run_aarch64**: 在 QEMU 中运行 ARM 64 内核 -- **debug_x86_64**: 在 QEMU 中调试 x86_64 内核 - **debug_riscv64**: 在 QEMU 中调试 RISC-V 64 内核 - **debug_aarch64**: 在 QEMU 中调试 ARM 64 内核 @@ -121,7 +113,7 @@ QEMU 是 SimpleKernel 的主要运行环境,支持所有目标架构的模拟 ```bash # Ubuntu/Debian -sudo apt install qemu-system-x86 qemu-system-riscv64 qemu-system-aarch64 +sudo apt install qemu-system-riscv64 qemu-system-aarch64 # macOS brew install qemu @@ -129,7 +121,6 @@ brew install qemu ### 架构支持 -- **qemu-system-x86_64**: 运行 x86_64 内核 - **qemu-system-riscv64**: 运行 RISC-V 64 内核 - **qemu-system-aarch64**: 运行 AArch64 内核 @@ -137,7 +128,7 @@ brew install qemu ```bash # 构建并运行 -cd build_x86_64 +cd build_riscv64 make run # 或者直接使用 VS Code 任务 @@ -164,7 +155,7 @@ gdb bin/kernel.elf 项目提供 [Dev Container](https://containers.dev/) 支持,确保开发环境的一致性: -- 基于 Ubuntu,包含 GCC 14 交叉编译工具链(x86_64 / riscv64 / aarch64) +- 基于 Ubuntu,包含 GCC 14 交叉编译工具链(riscv64 / aarch64) - 预装 QEMU、CMake、GDB 等全部构建和调试依赖 - 支持 VS Code Dev Containers、GitHub Codespaces 和 devcontainer CLI - 详细使用方法见 [Dev Container 文档](./docker.md) diff --git "a/docs/1_\347\263\273\347\273\237\345\220\257\345\212\250.md" "b/docs/1_\347\263\273\347\273\237\345\220\257\345\212\250.md" index befedfdb5..3c03fc194 100644 --- "a/docs/1_\347\263\273\347\273\237\345\220\257\345\212\250.md" +++ "b/docs/1_\347\263\273\347\273\237\345\220\257\345\212\250.md" @@ -1,407 +1,10 @@ # 系统启动 -## x86_64 - -### 概述 - -x86_64 架构的多核唤醒机制基于 APIC (Advanced Programmable Interrupt Controller) 系统,通过 INIT-SIPI-SIPI 序列来唤醒应用处理器 (Application Processors, APs)。 - -### 关键组件 - -#### 1. APIC 系统 -- **Local APIC**: 每个 CPU 核心都有一个 Local APIC,负责处理核心间通信 -- **IO APIC**: 系统级别的中断控制器,处理外部中断 -- **支持两种模式**: - - xAPIC: 传统模式,通过内存映射访问 - - x2APIC: 扩展模式,通过 MSR 访问,性能更好 - -#### 2. 核心数据结构 - -**SIPI 参数结构** (`src/arch/x86_64/sipi.h`): -```cpp -struct sipi_params_t { - uint32_t cr3; // 页表基地址 -} __attribute__((packed)); -``` - -**Per-CPU 数据** (`src/include/per_cpu.hpp`): -```cpp -class PerCpu { - static constexpr size_t kMaxCoreCount = 4; - const size_t core_id_; - ssize_t noff_{0}; // 中断嵌套深度 - bool intr_enable_{false}; // 中断使能状态 -}; -``` - -### 多核启动流程 - -#### 1. BSP (Bootstrap Processor) 初始化 -在 `src/arch/x86_64/arch_main.cpp` 的 `ArchInit()` 函数中: - -```cpp -auto ArchInit(int, const char **) -> int { - // 1. 初始化串口和基本信息 - SerialSingleton::create(cpu_io::kCom1); - BasicInfoSingleton::create(0, nullptr); - - // 2. 解析内核 ELF 信息 - KernelElfSingleton::create(BasicInfoSingleton::instance().elf_addr); - - // 3. 初始化 APIC 系统 - ApicSingleton::create(BasicInfoSingleton::instance().core_count); - ApicSingleton::instance().InitCurrentCpuLocalApic(); - - // 4. 填充 SIPI 参数 - auto target_sipi_params = reinterpret_cast(sipi_params); - target_sipi_params->cr3 = cpu_io::Cr3::Read(); - - // 5. 唤醒所有 AP - ApicSingleton::instance().StartupAllAps( - reinterpret_cast(ap_start16), - reinterpret_cast(ap_start64_end) - - reinterpret_cast(ap_start16), - kDefaultAPBase); - - return 0; -} -``` - -#### 2. AP 启动代码 -位于 `src/arch/x86_64/boot.S`,包含完整的模式切换序列: - -**16位实模式启动** (`ap_start16`): -```assembly -// 关中断,清除 TLB -cli -xor %eax, %eax -mov %eax, %cr3 - -// 加载 GDT 并启用保护模式 -mov $gdtdesc, %ebx -sub $ap_start16, %ebx -data32 lgdt (%ebx) - -mov %cr0, %eax -or $PROTECTION_MODE_BIT,%eax -mov %eax, %cr0 -``` - -**32位保护模式** (`ap_start32`): -```assembly -// 启用 PAE -mov %cr4, %eax -or $PAE_BIT, %eax -mov %eax, %cr4 - -// 设置页表(使用 BSP 的 CR3) -mov $cr3, %ebx -sub $ap_start16, %ebx -mov (%ebx), %eax -mov %eax, %cr3 - -// 启用长模式 -mov $IA32_EFER_MSR, %ecx -rdmsr -or $LONG_MODE_BIT, %eax -wrmsr - -// 启用分页 -mov %cr0, %eax -or $PAGING_BIT, %eax -mov %eax, %cr0 -``` - -**64位长模式** (`_boot`): -```assembly -// 关中断 -cli - -// 获取 APIC ID 作为 CPU ID -mov $CPUID_LEAF_1, %eax -cpuid -// EBX bits 31-24 是初始 APIC ID -shr $APIC_ID_SHIFT, %ebx -// 保留低 8 位 -and $APIC_ID_MASK, %ebx - -// 根据 CPU ID 设置独立栈 -mov %ebx, %eax -mov $STACK_SIZE_PER_CPU, %edx -mul %edx - -// 计算该 CPU 的栈顶地址 -mov $stack_top, %rsp -add %rax, %rsp -add $STACK_SIZE_PER_CPU, %rsp - -// 跳转到内核入口 -call _start -hlt -``` - -#### 3. INIT-SIPI-SIPI 序列 -在 `src/driver/apic/local_apic.cpp` 的 `WakeupAp()` 函数中实现: - -```cpp -void LocalApic::WakeupAp(uint32_t destination_apic_id, uint8_t start_vector) const { - // 1. 发送 INIT IPI - SendInitIpi(destination_apic_id); - - // 2. 等待 10ms (INIT IPI 后的标准等待时间) - auto delay = 10 * kCalibrationDelayLoop; - while (delay--) { __asm__ volatile("nop"); } - - // 3. 发送第一个 SIPI - SendStartupIpi(destination_apic_id, start_vector); - - // 4. 等待 200μs (SIPI 后的标准等待时间) - delay = 200 * (kCalibrationDelayLoop / 1000); - while (delay--) { __asm__ volatile("nop"); } - - // 5. 发送第二个 SIPI(提高可靠性) - SendStartupIpi(destination_apic_id, start_vector); - - // 6. 等待 200μs - delay = 200 * (kCalibrationDelayLoop / 1000); - while (delay--) { __asm__ volatile("nop"); } -} -``` - -其中 `kCalibrationDelayLoop = 1000000` 用于延时校准。 - -#### 4. AP 入口处理 -AP 启动后通过 `src/main.cpp` 的 `_start()` 函数进入: - -```cpp -void _start(int argc, const char **argv) { - if (argv != nullptr) { - // BSP 路径 - CppInit(); - main(argc, argv); - CppDeInit(); - } else { - // AP 路径 - main_smp(argc, argv); - } - - // 进入死循环 - while (true) { ; } -} -``` - -AP 执行 `main_smp()` -> `ArchInitSMP()` -> `InitCurrentCpuLocalApic()`: - -```cpp -auto ArchInitSMP(int, const char **) -> int { - ApicSingleton::instance().InitCurrentCpuLocalApic(); - return 0; -} -``` - -### 内存布局 - -#### 栈分配 -每个 CPU 分配 4KB 独立栈空间: -```cpp -// 每个 CPU 的栈大小:4KB -#define STACK_SIZE_PER_CPU 0x1000 -// 最大 CPU 数量 -#define MAX_CPU_COUNT 4 -// 总栈大小 -#define TOTAL_STACK_SIZE (STACK_SIZE_PER_CPU * MAX_CPU_COUNT) - -// CPU ID * 4KB + stack_top 作为该 CPU 的栈顶 -``` - -```assembly -// 声明所属段 -.section .bss -// MAX_CPU_COUNT 个 CPU × STACK_SIZE_PER_CPU 每个 = TOTAL_STACK_SIZE 总栈空间 -stack_top: - .space TOTAL_STACK_SIZE -``` - -#### AP 启动代码位置 -- 默认复制到物理地址 `0x30000` (`kDefaultAPBase`) -- 必须在 1MB 以下(实模式限制) -- 必须 4KB 对齐(SIPI 要求) - -### APIC 配置 - -#### Local APIC 初始化 -```cpp -bool LocalApic::Init() { - // 1. 检查并启用 APIC 模式(优先 x2APIC) - if (EnableX2Apic()) { - is_x2apic_mode_ = true; - } else { - if (!EnableXApic()) { - return false; - } - is_x2apic_mode_ = false; - } - - // 2. 启用 Local APIC (通过设置 SIVR) - uint32_t sivr = is_x2apic_mode_ ? - cpu_io::msr::apic::ReadSivr() : - io::In(apic_base_ + kXApicSivrOffset); - - sivr |= kApicSoftwareEnableBit; // 启用 APIC - sivr |= kSpuriousVector; // 设置虚假中断向量 - - // 3. 清除任务优先级 - SetTaskPriority(0); - - // 4. 屏蔽所有 LVT 条目 - if (is_x2apic_mode_) { - cpu_io::msr::apic::WriteLvtTimer(kLvtMaskBit); - cpu_io::msr::apic::WriteLvtLint0(kLvtMaskBit); - cpu_io::msr::apic::WriteLvtLint1(kLvtMaskBit); - cpu_io::msr::apic::WriteLvtError(kLvtMaskBit); - } else { - io::Out(apic_base_ + kXApicLvtTimerOffset, kLvtMaskBit); - io::Out(apic_base_ + kXApicLvtLint0Offset, kLvtMaskBit); - io::Out(apic_base_ + kXApicLvtLint1Offset, kLvtMaskBit); - io::Out(apic_base_ + kXApicLvtErrorOffset, kLvtMaskBit); - } - - return true; -} -``` - -#### IPI 发送机制 -支持两种 IPI 类型: -- **点对点 IPI**: `SendIpi(target_apic_id, vector)` -- **广播 IPI**: `BroadcastIpi(vector)` - -**INIT IPI 发送**: -```cpp -void LocalApic::SendInitIpi(uint32_t destination_apic_id) const { - if (is_x2apic_mode_) { - auto icr = kInitIpiMode; // 0x500 - icr |= static_cast(destination_apic_id) << 32; - cpu_io::msr::apic::WriteIcr(icr); - } else { - // xAPIC 模式通过内存映射访问 - auto icr_high = (destination_apic_id & kApicIdMask) << kIcrDestShift; - io::Out(apic_base_ + kXApicIcrHighOffset, icr_high); - io::Out(apic_base_ + kXApicIcrLowOffset, kInitIpiMode); - } - - // 等待传递状态清除 - while ((ReadIcr() & kIcrDeliveryStatusBit) != 0) { ; } -} -``` - -**SIPI 发送**: -```cpp -void LocalApic::SendStartupIpi(uint32_t destination_apic_id, - uint8_t start_page) const { - if (is_x2apic_mode_) { - auto icr = kSipiMode | start_page; // 0x600 | start_page - icr |= static_cast(destination_apic_id) << 32; - cpu_io::msr::apic::WriteIcr(icr); - } else { - auto icr_high = (destination_apic_id & kApicIdMask) << kIcrDestShift; - io::Out(apic_base_ + kXApicIcrHighOffset, icr_high); - io::Out(apic_base_ + kXApicIcrLowOffset, kSipiMode | start_page); - } - - // 等待传递状态清除 - while ((ReadIcr() & kIcrDeliveryStatusBit) != 0) { ; } -} -``` - -### 关键常量定义 - -```cpp -// APIC 基址和向量 -static constexpr uint64_t kDefaultAPBase = 0x30000; - -// 模式切换位 -#define PAE_BIT 0x20 // CR4.PAE -#define PROTECTION_MODE_BIT 0x1 // CR0.PE -#define LONG_MODE_BIT 0x100 // EFER.LME -#define PAGING_BIT 0x80000000 // CR0.PG - -// APIC ID 相关 -#define APIC_ID_SHIFT 24 -#define APIC_ID_MASK 0xFF -#define CPUID_LEAF_1 1 - -// IPI 模式 -static constexpr uint32_t kInitIpiMode = 0x500; // INIT -static constexpr uint32_t kSipiMode = 0x600; // SIPI - -// 栈配置 -#define STACK_SIZE_PER_CPU 0x1000 // 4KB per CPU -#define MAX_CPU_COUNT 4 - -// 延时校准 -static constexpr uint32_t kCalibrationDelayLoop = 1000000; -``` - -### 特性和限制 - -#### 支持特性 -- 支持最多 4 个 CPU 核心 -- 支持 x2APIC 和 xAPIC 两种模式 -- 完整的 INIT-SIPI-SIPI 启动序列 -- 每个 CPU 独立的栈空间 -- Per-CPU 数据结构管理 -- 基于 APIC ID 的 CPU 标识 - -#### 当前限制 -- 最大 CPU 数量硬编码为 4 -- IO APIC 功能部分待实现 -- 缺少 CPU 热插拔支持 -- 简化的错误处理机制 -- 延时使用简单的循环计数,不够精确 - -### 调试和监控 - -系统提供 APIC 信息打印功能: -```cpp -void LocalApic::PrintInfo() const { - klog::Info("=== Local APIC Information ===\n"); - klog::Info("APIC Version: 0x%x\n", GetApicVersion()); - klog::Info("Mode: %s\n", is_x2apic_mode_ ? "x2APIC" : "xAPIC"); - klog::Info("Task Priority: 0x%x\n", GetTaskPriority()); - // ... 更多寄存器状态 -} -``` - -**CPU ID 获取机制**: -```cpp -// 通过 APIC ID 获取当前 CPU ID -static __always_inline auto GetCurrentCoreId() -> size_t { - auto apic_info = GetApicInfo(); - return apic_info.apic_id; -} -``` - -**多核启动管理**: -```cpp -void Apic::StartupAllAps(uint64_t ap_code_addr, size_t ap_code_size, - uint64_t target_addr) const { - // 尝试启动 APIC ID 0 到 cpu_count_-1 的所有处理器 - // 跳过当前的 BSP (Bootstrap Processor) - for (size_t apic_id = 0; apic_id < cpu_count_; apic_id++) { - if (static_cast(apic_id) == cpu_io::GetApicInfo().apic_id) { - continue; // 跳过当前 BSP - } - StartupAp(static_cast(apic_id), ap_code_addr, - ap_code_size, target_addr); - } -} -``` - ## RISCV64 ### 概述 -RISC-V64 架构的多核唤醒机制基于 OpenSBI (Supervisor Binary Interface) 标准,通过 HSM (Hart State Management) 扩展来管理硬件线程(Hart)的启动和状态。与x86_64的复杂INIT-SIPI-SIPI序列不同,RISC-V提供了更简单统一的多核启动接口。 +RISC-V64 架构的多核唤醒机制基于 OpenSBI (Supervisor Binary Interface) 标准,通过 HSM (Hart State Management) 扩展来管理硬件线程(Hart)的启动和状态。RISC-V 提供了简单统一的多核启动接口。 ### 关键组件 diff --git "a/docs/2_\350\260\203\350\257\225\350\276\223\345\207\272.md" "b/docs/2_\350\260\203\350\257\225\350\276\223\345\207\272.md" index faf38c531..c24bd04cd 100644 --- "a/docs/2_\350\260\203\350\257\225\350\276\223\345\207\272.md" +++ "b/docs/2_\350\260\203\350\257\225\350\276\223\345\207\272.md" @@ -8,7 +8,6 @@ 通过 CMake 预设变量 `SIMPLEKERNEL_EARLY_CONSOLE` 启用。该变量定义了串口控制器的物理基地址。 -- **x86_64**: 默认使用 COM1 - **RISC-V 64**: 0x10000000 (QEMU virt machine) - **AArch64**: 0x9000000 (QEMU virt machine) @@ -29,128 +28,6 @@ EarlyConsole early_console; 此阶段的串口驱动通常采用轮询模式,不依赖中断。 -## x86_64 - -### 概述 - -x86_64 架构使用标准的串口(COM口)进行调试输出,主要基于16550 UART兼容的串口控制器。系统默认使用 COM1 端口(0x3F8)进行调试信息的输出。 - -### 关键组件 - -#### 1. Serial 类实现 -位于 `3rd/cpu_io/include/x86_64/cpu.hpp`,提供完整的串口通信功能: - -```cpp -class Serial { - public: - explicit Serial(uint32_t port) : port_(port) { - // 禁用所有中断 - Out(port_ + 1, 0x00); - // 启用 DLAB (设置波特率分频器) - Out(port_ + 3, 0x80); - // 设置分频器为 3 (低字节) - 38400 波特率 - Out(port_ + 0, 0x03); - // 分频器高字节 - Out(port_ + 1, 0x00); - // 8位数据,无奇偶校验,1个停止位 - Out(port_ + 3, 0x03); - // 启用FIFO,清空缓冲区,14字节阈值 - Out(port_ + 2, 0xC7); - // 启用中断,设置RTS/DSR - Out(port_ + 4, 0x0B); - - // 自检测试 - Out(port_ + 4, 0x1E); // 设置回环模式 - Out(port_ + 0, 0xAE); // 发送测试字节 - if (In(port_ + 0) != 0xAE) { - asm("hlt"); // 串口故障,停机 - } - - // 设置正常操作模式 - Out(port_ + 4, 0x0F); - } - - void Write(uint8_t byte) const { - while (!IsTransmitEmpty()) { ; } - Out(port_, byte); - } - - private: - [[nodiscard]] auto IsTransmitEmpty() const -> bool { - return bool((In(port_ + 5) & 0x20) != 0); - } -}; -``` - -#### 2. 调试输出接口 -在 `src/arch/x86_64/arch_main.cpp` 中实现输出函数: - -```cpp -// 基本输出实现 -namespace { -cpu_io::Serial *serial = nullptr; -} - -extern "C" void sk_putchar(int c, [[maybe_unused]] void *ctx) { - if (serial) { - serial->Write(c); - } -} -``` - -### 初始化流程 - -1. **串口初始化**:在 `ArchInit()` 中创建 Serial 实例 -```cpp -SerialSingleton::create(cpu_io::kCom1); -serial = &SerialSingleton::instance(); -``` - -2. **端口配置**: - - COM1 端口地址:`0x3F8` - - 波特率:38400 bps - - 数据位:8位 - - 停止位:1位 - - 奇偶校验:无 - - 流控:硬件流控 - -3. **自检验证**:通过回环测试确保串口正常工作 - -### 寄存器定义 - -```cpp -// 串口寄存器偏移 -// port + 0: 数据寄存器 (RHR/THR) -// port + 1: 中断使能寄存器 (IER) -// port + 2: FIFO控制寄存器 (FCR) / 中断状态寄存器 (ISR) -// port + 3: 线路控制寄存器 (LCR) -// port + 4: 调制解调器控制寄存器 (MCR) -// port + 5: 线路状态寄存器 (LSR) - -static constexpr uint32_t kCom1 = 0x3F8; // COM1端口基址 -``` - -### 特性和优势 - -- **硬件兼容性**:支持标准16550 UART -- **可靠性**:包含硬件自检功能 -- **性能**:使用FIFO缓冲提高传输效率 -- **调试友好**:直接输出到主机串口终端 - -### 使用示例 - -```cpp -// 初始化串口 -auto& serial = SerialSingleton::instance(); - -// 通过内核日志输出(会调用sk_putchar) -klog::Info("Hello x86_64 Debug Output\n"); - -// 直接串口写入 -serial.Write('H'); -serial.Write('i'); -``` - ## RISCV64 ### 概述 diff --git "a/docs/3_\344\270\255\346\226\255.md" "b/docs/3_\344\270\255\346\226\255.md" index 26f3311ba..eb846eda4 100644 --- "a/docs/3_\344\270\255\346\226\255.md" +++ "b/docs/3_\344\270\255\346\226\255.md" @@ -4,285 +4,14 @@ SimpleKernel 实现了跨多个架构的完整中断处理系统,每个架构 ## 🏗️ 架构对比总览 -| 特性 | x86_64 | RISC-V 64 | AArch64 | -|------|--------|-----------|---------| -| 中断控制器 | APIC/IO-APIC | PLIC/CLINT | GIC | -| 中断描述符 | IDT (256条目) | CSR + trap handler | 异常向量表 | -| 定时器 | APIC Timer | SBI Timer | Generic Timer | -| 外部中断 | IRQ重定向 | PLIC中断路由 | GIC分发 | -| 多核启动 | SIPI | SBI Hart管理 | PSCI | -| 特权级别 | Ring 0-3 | M/S/U Mode | EL0-3 | - ---- - -# x86_64 中断处理系统 - -## 🏗️ 系统架构 - -### 核心组件 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ x86_64 中断处理系统 │ -├─────────────────────────────────────────────────────────────┤ -│ InterruptBase (抽象基类) │ -│ ├── Do(cause, context) │ -│ └── RegisterInterruptFunc(cause, func) │ -├─────────────────────────────────────────────────────────────┤ -│ Interrupt (x86_64实现) │ -│ ├── 中断描述符表(IDT) 初始化 │ -│ ├── 中断处理函数数组管理 │ -│ ├── 模板递归初始化 IDT 条目 │ -│ └── 中断分发机制 │ -├─────────────────────────────────────────────────────────────┤ -│ APIC 控制器集成 │ -│ ├── Local APIC 定时器 │ -│ ├── 中断向量分配 │ -│ ├── EOI (End of Interrupt) 处理 │ -│ └── IRQ 重定向配置 │ -├─────────────────────────────────────────────────────────────┤ -│ 具体中断处理器 │ -│ ├── APIC 定时器中断 (0xF0) │ -│ ├── 键盘中断 (0xF1) │ -│ └── 默认异常处理器 │ -└─────────────────────────────────────────────────────────────┘ -``` - -## 📋 中断描述符表 (IDT) 管理 - -### IDT 初始化流程 - -```cpp -class Interrupt { - // 256个中断处理函数数组 - static std::array interrupt_handlers; - - // 256个中断描述符条目 - static std::array idts; - - // 模板递归初始化所有IDT条目 - template - void SetUpIdtr(); -}; -``` - -### 模板递归初始化机制 - -```cpp -template -void Interrupt::SetUpIdtr() { - if constexpr (no < kInterruptMaxCount - 1) { - // 为每个中断号创建IDT条目 - idts[no] = cpu_io::IdtrInfo::Idt( - reinterpret_cast(TarpEntry), // 中断入口地址 - 8, // 代码段选择子 - 0x0, // IST (中断栈表) - Idt::Type::k64BitInterruptGate, // 64位中断门 - Idt::DPL::kRing0, // 特权级别0 - Idt::P::kPresent // 存在位 - ); - SetUpIdtr(); // 递归初始化下一个 - } else { - // 最后一步:写入IDTR寄存器 - static auto idtr = IdtrInfo::Idtr{ - .limit = sizeof(Idt) * kInterruptMaxCount - 1, - .base = idts.data(), - }; - cpu_io::Idtr::Write(idtr); - } -} -``` - -### 中断入口模板 - -每个中断号都有独立的入口函数: - -```cpp -template -__attribute__((target("general-regs-only"))) -__attribute__((interrupt)) -void TarpEntry(uint8_t *interrupt_context) { - // 调用统一的中断分发器 - InterruptSingleton::instance().Do(no, interrupt_context); -} -``` - -## 🎯 中断处理流程 - -### 1. 硬件中断触发 -``` -硬件事件 → CPU 中断 → 查找 IDT[vector] → 调用 TarpEntry -``` - -### 2. 软件中断分发 -```cpp -void Interrupt::Do(uint64_t cause, uint8_t *context) { - if (cause < kInterruptMaxCount) { - // 调用注册的中断处理函数 - interrupt_handlers[cause](cause, context); - } -} -``` - -### 3. 中断处理函数注册 -```cpp -void Interrupt::RegisterInterruptFunc(uint64_t cause, InterruptFunc func) { - if (cause < kInterruptMaxCount) { - interrupt_handlers[cause] = func; - klog::Debug("RegisterInterruptFunc [%s] 0x%X\n", - kInterruptNames[cause], cause); - } -} -``` - -## ⏰ APIC 定时器中断 - -### 定时器配置 - -```cpp -// 定时器配置常量 -constexpr uint8_t kApicTimerVector = 0xF0; // 定时器中断向量 -constexpr uint32_t kApicTimerFrequencyHz = 100; // 100Hz 频率 - -// 初始化定时器 -ApicSingleton::instance().SetupPeriodicTimer( - kApicTimerFrequencyHz, - kApicTimerVector -); -``` - -### 定时器中断处理 - -```cpp -uint64_t ApicTimerHandler(uint64_t cause, uint8_t *context) { - static uint64_t tick_count = 0; - tick_count++; - - // 降低日志输出频率 - if (tick_count % 100 == 0) { - klog::Info("APIC Timer interrupt %lu, vector 0x%X\n", - tick_count, cause); - } - - // 发送EOI信号确认中断处理完成 - ApicSingleton::instance().SendEoi(); - return 0; -} -``` - -## ⌨️ 键盘中断处理 - -### 键盘中断配置 - -```cpp -constexpr uint8_t kKeyboardVector = 0xF1; // 键盘中断向量 -constexpr uint8_t kKeyboardIrq = 1; // 键盘使用IRQ1 - -bool EnableKeyboardInterrupt(uint8_t vector) { - // 获取当前CPU的APIC ID - uint32_t destination_apic_id = cpu_io::GetCurrentCoreId(); - - // 配置IRQ重定向 - return ApicSingleton::instance().SetIrqRedirection( - kKeyboardIrq, vector, destination_apic_id, false - ); -} -``` - -### 键盘扫描码处理 - -```cpp -uint64_t KeyboardHandler(uint64_t cause, uint8_t *context) { - // 读取键盘扫描码 (端口0x60) - uint8_t scancode = cpu_io::In(0x60); - - // 处理按键按下事件 (最高位为0) - if (!(scancode & 0x80)) { - klog::Info("Key pressed: scancode 0x%02X\n", scancode); - - // 扫描码到ASCII转换 - static const char scancode_to_ascii[] = { - 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', - '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', - 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', - 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ' - }; - - if (scancode < sizeof(scancode_to_ascii) && - scancode_to_ascii[scancode]) { - char ascii_char = scancode_to_ascii[scancode]; - klog::Info("Key: '%c'\n", ascii_char); - } - } - - // 发送EOI信号 - ApicSingleton::instance().SendEoi(); - return 0; -} -``` - -## 🚀 系统初始化 - -### 主处理器初始化 (BSP) - -```cpp -void InterruptInit(int, const char **) { - // 1. 初始化IDT - InterruptSingleton::instance().SetUpIdtr(); - - // 2. 注册APIC定时器中断处理函数 - InterruptSingleton::instance().RegisterInterruptFunc( - kApicTimerVector, ApicTimerHandler - ); - - // 3. 注册键盘中断处理函数 - InterruptSingleton::instance().RegisterInterruptFunc( - kKeyboardVector, KeyboardHandler - ); - - // 4. 启用外部中断 - cpu_io::EnableInterrupt(); - - // 5. 启动定时器 - ApicSingleton::instance().SetupPeriodicTimer( - kApicTimerFrequencyHz, kApicTimerVector - ); - - // 6. 启用键盘中断 - EnableKeyboardInterrupt(kKeyboardVector); - - klog::Info("x86_64 interrupt system initialized\n"); -} -``` - -### 从处理器初始化 (AP) - -```cpp -void InterruptInitSMP(int, const char **) { - // 1. 每个核心都需要初始化自己的IDT - InterruptSingleton::instance().SetUpIdtr(); - - // 2. 启用中断 - cpu_io::EnableInterrupt(); - - klog::Info("x86_64 SMP interrupt initialization complete\n"); -} -``` - -## 📊 性能特点 - -### 优势 -- **模板递归初始化**:编译时生成所有IDT条目,零运行时开销 -- **类型安全**:强类型约束防止中断向量错误 -- **APIC集成**:现代多核处理器高效中断处理 -- **统一接口**:InterruptBase 抽象层支持跨架构兼容 - -### 设计考量 -- **256个中断向量**:完整覆盖x86_64中断空间 -- **模板元编程**:减少代码重复,提高性能 -- **EOI处理**:确保中断控制器状态正确 -- **IRQ重定向**:支持多核中断负载均衡 +| 特性 | RISC-V 64 | AArch64 | +|------|-----------|---------| +| 中断控制器 | PLIC/CLINT | GIC | +| 中断描述符 | CSR + trap handler | 异常向量表 | +| 定时器 | SBI Timer | Generic Timer | +| 外部中断 | PLIC中断路由 | GIC分发 | +| 多核启动 | SBI Hart管理 | PSCI | +| 特权级别 | M/S/U Mode | EL0-3 | --- @@ -1084,200 +813,32 @@ void StartSecondaryCpus() { ## 中断控制器对比 -| 特性 | x86_64 APIC | RISC-V PLIC | AArch64 GIC | -|------|-------------|-------------|-------------| -| 中断数量 | 256个向量 | 可配置(通常1024) | 1024个INTID | -| 优先级支持 | 16级(硬件) | 可配置级别 | 256级 | -| 多核支持 | Local+IO APIC | 每hart独立 | 分发器+CPU接口 | -| 中断类型 | IRQ/NMI | 外部中断 | SGI/PPI/SPI | -| EOI机制 | APIC EOI寄存器 | PLIC Complete | GIC EOIR寄存器 | +| 特性 | RISC-V PLIC | AArch64 GIC | +|------|-------------|-------------| +| 中断数量 | 可配置(通常1024) | 1024个INTID | +| 优先级支持 | 可配置级别 | 256级 | +| 多核支持 | 每hart独立 | 分发器+CPU接口 | +| 中断类型 | 外部中断 | SGI/PPI/SPI | +| EOI机制 | PLIC Complete | GIC EOIR寄存器 | ## 定时器机制对比 | 架构 | 定时器类型 | 精度 | 编程接口 | |------|-----------|------|----------| -| x86_64 | APIC Timer | 可编程分频 | Local APIC寄存器 | | RISC-V | Machine Timer | 固定频率 | SBI调用 | | AArch64 | Generic Timer | 系统频率 | 系统寄存器 | ## 初始化复杂度 -1. **x86_64**: 模板递归初始化,编译时优化 -2. **RISC-V**: 设备树解析 + CSR配置 -3. **AArch64**: 异常向量表 + GIC多阶段初始化 +1. **RISC-V**: 设备树解析 + CSR配置 +2. **AArch64**: 异常向量表 + GIC多阶段初始化 ## 性能特征 -- **x86_64**: 模板元编程优化,零运行时开销 - **RISC-V**: 简洁的CSR接口,SBI服务集成 - **AArch64**: 硬件优先级仲裁,多级异常处理 这种多架构设计确保了SimpleKernel在不同硬件平台上的高效运行,同时保持了统一的编程接口。 - kKeyboardVector, KeyboardHandler - ); - - // 4. 启用键盘中断 - EnableKeyboardInterrupt(kKeyboardVector); - - // 5. 启用APIC定时器 - ApicSingleton::instance().SetupPeriodicTimer( - kApicTimerFrequencyHz, kApicTimerVector - ); - - // 6. 开启全局中断 - cpu_io::Rflags::If::Set(); - - klog::Info("Hello InterruptInit\n"); -} -``` - -### 应用处理器初始化 (SMP) - -```cpp -void InterruptInitSMP(int, const char **) { - // 1. 初始化IDT (每个CPU都需要) - InterruptSingleton::instance().SetUpIdtr(); - - // 2. 启用APIC定时器 - ApicSingleton::instance().SetupPeriodicTimer( - kApicTimerFrequencyHz, kApicTimerVector - ); - - // 3. 开启全局中断 - cpu_io::Rflags::If::Set(); - - klog::Info("Hello InterruptInit SMP\n"); -} -``` - -## 🔧 默认异常处理 - -系统为所有未注册的中断/异常提供默认处理器: - -```cpp -Interrupt::Interrupt() { - // 为所有中断注册默认处理函数 - for (auto &i : interrupt_handlers) { - i = [](uint64_t cause, uint8_t *context) -> uint64_t { - klog::Info("Default Interrupt handler [%s] 0x%X, 0x%p\n", - kInterruptNames[cause], cause, context); - DumpStack(); // 打印调用栈 - while (true) { ; } // 停机 - }; - } - klog::Info("Interrupt init.\n"); -} -``` - -## 📊 中断上下文结构 - -### 无错误码中断上下文 -```cpp -struct InterruptContext { - uint64_t rip; // 指令指针 - uint64_t cs; // 代码段选择子 - uint64_t rflags; // 标志寄存器 - uint64_t rsp; // 栈指针 - uint64_t ss; // 栈段选择子 -}; -``` - -### 有错误码中断上下文 -```cpp -struct InterruptContextErrorCode { - uint32_t error_code; // 错误码 - uint32_t padding; // 填充对齐 - uint64_t rip; - uint64_t cs; - uint64_t rflags; - uint64_t rsp; - uint64_t ss; -}; -``` - -## ⚡ 性能优化特性 - -### 1. 模板元编程 -- **编译时展开**: 使用模板递归在编译时生成所有IDT条目 -- **零运行时开销**: 避免循环初始化的运行时成本 - -### 2. 内存对齐优化 -```cpp -// 4KB对齐优化缓存行为 -alignas(4096) static std::array interrupt_handlers; -alignas(4096) static std::array idts; -``` - -### 3. 高效中断属性 -```cpp -// 编译器优化属性 -__attribute__((target("general-regs-only"))) // 只使用通用寄存器 -__attribute__((interrupt)) // 中断函数优化 -``` - -## 🔄 多核处理 (SMP) - -### BSP vs AP 初始化差异 - -| 组件 | BSP (主处理器) | AP (应用处理器) | -|------|---------------|----------------| -| IDT初始化 | ✅ | ✅ | -| 中断处理函数注册 | ✅ | ❌ (共享) | -| 键盘中断配置 | ✅ | ❌ | -| APIC定时器 | ✅ | ✅ | -| 全局中断使能 | ✅ | ✅ | - -### 多核中断安全性 -- **每核独立IDT**: 每个CPU核心都有自己的IDT副本 -- **共享中断处理函数**: 中断处理函数数组在所有核心间共享 -- **APIC EOI**: 每个核心独立发送EOI信号 - -## 🐛 调试和监控 - -### 中断信息输出 -```cpp -// 调试模式下输出中断注册信息 -klog::Debug("RegisterInterruptFunc [%s] 0x%X, 0x%p\n", - kInterruptNames[cause], cause, func); - -// IDT条目调试信息 -for (size_t i = 0; i < interrupt_count; i++) { - klog::Debug("idtr[%d] 0x%p\n", i, idtr.base + i); -} -``` - -### 错误处理 -- **栈转储**: 未处理异常时自动打印调用栈 -- **系统停机**: 严重错误时安全停机 -- **中断名称映射**: 通过名称数组提供可读的中断信息 - -## 📚 相关文档 - -- [APIC 驱动文档](../src/driver/apic/README.md) -- [CPU I/O 库文档](../3rd/cpu_io/README.md) -- [内核日志系统](../src/include/kernel_log.hpp) - -## 🔗 关键数据结构 - -### 中断向量分配 -```cpp -0x00-0x1F: CPU异常 (页错误、除零等) -0x20-0xEF: 可用用户中断向量 -0xF0: APIC定时器中断 -0xF1: 键盘中断 -0xF2-0xFF: 保留向量 -``` - -### APIC 集成点 -- **Local APIC**: 每核心定时器和EOI处理 -- **IO APIC**: IRQ重定向配置 -- **x2APIC/xAPIC**: 自动检测和配置 - ---- - -# RISC-V 64 中断处理系统 - -SimpleKernel 在 RISC-V 64 架构上实现了基于 CSR (Control and Status Registers) 和 PLIC (Platform-Level Interrupt Controller) 的完整中断处理系统。 ## 🏗️ 系统架构 diff --git a/docs/docker.md b/docs/docker.md index b98425253..7e8ad3dac 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -26,7 +26,7 @@ npm install -g @devcontainers/cli devcontainer up --workspace-folder . # 在容器内执行命令 -devcontainer exec --workspace-folder . cmake --preset=build_x86_64 +devcontainer exec --workspace-folder . cmake --preset=build_riscv64 ``` ## 验证环境 @@ -36,14 +36,13 @@ gcc --version # GCC 14 aarch64-linux-gnu-gcc --version # aarch64 交叉编译器 riscv64-linux-gnu-gcc --version # riscv64 交叉编译器 cmake --version -qemu-system-x86_64 --version +qemu-system-riscv64 --version ``` ## 构建与运行 ```shell -# 配置 + 编译(三选一) -cmake --preset=build_x86_64 && cmake --build build_x86_64 --target SimpleKernel +# 配置 + 编译(二选一) cmake --preset=build_riscv64 && cmake --build build_riscv64 --target SimpleKernel cmake --preset=build_aarch64 && cmake --build build_aarch64 --target SimpleKernel @@ -53,6 +52,6 @@ make run # 调试 make debug # GDB 连接 localhost:1234 -# 单元测试 + 覆盖率(仅 host 架构) -cmake --build build_x86_64 --target unit-test coverage +# 单元测试 + 覆盖率 +cmake --build build_riscv64 --target unit-test coverage ``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5ced93bcb..2db66c59f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,11 +38,7 @@ SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES OUTPUT_NAME LIST (APPEND ${PROJECT_NAME}_QEMU_BOOT_FLAGS "-D;${CMAKE_BINARY_DIR}/bin/${PROJECT_NAME}_qemu.log") # 目标平台参数 -IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - LIST (APPEND ${PROJECT_NAME}_QEMU_BOOT_FLAGS -drive - file=fat:rw:${CMAKE_BINARY_DIR}/bin,format=raw,media=disk -bios - ${u-boot_BINARY_DIR}/u-boot.rom) -ELSEIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") +IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") LIST (APPEND ${PROJECT_NAME}_QEMU_BOOT_FLAGS -bios ${u-boot_BINARY_DIR}/spl/u-boot-spl.bin -device loader,file=${u-boot_BINARY_DIR}/u-boot.itb,addr=0x80200000) diff --git a/src/arch/AGENTS.md b/src/arch/AGENTS.md index 93c2c6ff9..ecfb851ce 100644 --- a/src/arch/AGENTS.md +++ b/src/arch/AGENTS.md @@ -12,10 +12,6 @@ aarch64/ riscv64/ plic/ # PLIC interrupt controller (plic.cpp + include/plic.h) include/ # Arch-private headers -x86_64/ - apic/ # APIC: local_apic.cpp, io_apic.cpp, apic.cpp + include/ - include/ # Arch-private headers - sipi.h # SMP startup IPI definitions ``` ## WHERE TO LOOK @@ -29,20 +25,19 @@ x86_64/ ## CONVENTIONS - Each arch directory mirrors the same file set — 1:1 correspondence -- Interrupt controllers live in subdirectories (gic/, plic/, apic/) with own CMakeLists.txt -- Assembly files use `.S` (preprocessed), `macro.S` for shared macros (riscv64, x86_64) +- Interrupt controllers live in subdirectories (gic/, plic/) with own CMakeLists.txt +- Assembly files use `.S` (preprocessed), `macro.S` for shared macros (riscv64) - `include/` subdirs contain arch-private headers NOT exported to other modules - `early_console.cpp` provides pre-device-framework output via arch-specific MMIO/SBI ## ANTI-PATTERNS - **DO NOT** add arch-specific code outside `src/arch/{arch}/` — use `arch.h` abstraction - **DO NOT** include arch-private headers from outside the arch directory -- x86_64 interrupt: template-heavy ISR registration can cause stack overflow — watch stack depth - `static_assert` guards on PerCpu alignment and GuardType ABI — do not remove - `#error` in syscall.hpp for unsupported arch — intentional, add new arch case instead ## NOTES -- Boot chains: x86_64 (U-Boot), riscv64 (U-Boot SPL→OpenSBI→U-Boot), aarch64 (U-Boot→ATF→OP-TEE) -- SMP: aarch64=PSCI, riscv64=SBI hart_start, x86_64=SIPI via APIC -- Early console: aarch64=PL011 MMIO, riscv64=SBI call, x86_64=COM1 port I/O +- Boot chains: riscv64 (U-Boot SPL→OpenSBI→U-Boot), aarch64 (U-Boot→ATF→OP-TEE) +- SMP: aarch64=PSCI, riscv64=SBI hart_start +- Early console: aarch64=PL011 MMIO, riscv64=SBI call - Backtrace uses ELF symbol table from `kernel_elf.hpp` for address→name resolution diff --git a/src/arch/CMakeLists.txt b/src/arch/CMakeLists.txt index 79823a59f..abee7c416 100644 --- a/src/arch/CMakeLists.txt +++ b/src/arch/CMakeLists.txt @@ -22,8 +22,6 @@ 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 ( @@ -32,8 +30,6 @@ TARGET_LINK_LIBRARIES ( plic> $<$: gic> - $<$: - apic> # Documented exception: arch code needs direct driver access # aarch64: pl011_driver (early_console, interrupt_main) # riscv64: ns16550a_driver, virtio_driver (interrupt_main) diff --git a/src/arch/README.md b/src/arch/README.md index bae9dae23..06ae1fadd 100644 --- a/src/arch/README.md +++ b/src/arch/README.md @@ -1,10 +1,9 @@ # arch -架构相关代码目录,包含了不同 CPU 架构的特定实现,目前支持三种主流 64 位架构: +架构相关代码目录,包含了不同 CPU 架构的特定实现,目前支持两种主流 64 位架构: - **aarch64** - ARM 64 位架构 - **riscv64** - RISC-V 64 位架构 -- **x86_64** - x86 64 位架构 ## 目录结构 @@ -19,20 +18,13 @@ arch/ │ ├── boot.S # 启动汇编代码 │ ├── early_console.cpp # 早期调试输出 │ └── link.ld # 链接脚本 -├── riscv64/ # RISC-V 64 位架构实现 -│ ├── arch_main.cpp # 架构初始化主函数 -│ ├── backtrace.cpp # 调用栈回溯实现 -│ ├── boot.S # 启动汇编代码 -│ ├── early_console.cpp # 早期调试输出 -│ ├── link.ld # 链接脚本 -│ └── macro.S # 汇编宏定义 -└── x86_64/ # x86 64 位架构实现 +└── riscv64/ # RISC-V 64 位架构实现 ├── arch_main.cpp # 架构初始化主函数 ├── backtrace.cpp # 调用栈回溯实现 ├── boot.S # 启动汇编代码 ├── early_console.cpp # 早期调试输出 ├── link.ld # 链接脚本 - └── sipi.h # SMP 启动相关定义 + └── macro.S # 汇编宏定义 ``` ## 核心接口 @@ -95,14 +87,6 @@ void DumpStack(); - **帧指针**: 使用 FP 寄存器进行栈回溯 - **特殊文件**: `macro.S` 提供汇编宏定义,包括寄存器操作宏 -#### x86_64 -- **启动方式**: 通过 GRUB 或其他 Multiboot 兼容启动器 -- **多核启动**: 使用 APIC (Advanced Programmable Interrupt Controller) 和 SIPI (Startup Inter-Processor Interrupt) -- **早期控制台**: 通过 cpu_io 预设支持早期调试输出 (COM1) -- **硬件发现**: 通过 CPUID 指令获取处理器信息 -- **帧指针**: 使用 RBP 寄存器进行栈回溯 -- **特殊文件**: `sipi.h` 定义多核启动相关结构 - ## 编译配置 通过 CMake 根据 `CMAKE_SYSTEM_PROCESSOR` 变量自动选择对应的架构实现: diff --git a/src/arch/aarch64/interrupt.S b/src/arch/aarch64/interrupt.S index 9ddafeb03..67899d0be 100644 --- a/src/arch/aarch64/interrupt.S +++ b/src/arch/aarch64/interrupt.S @@ -172,18 +172,17 @@ trap_return: mov sp, x0 // 恢复 Trap 上下文 - // 检查 spsr_el1.M[0] (EL0t/EL1t=0b0000, EL1h=0b0101) 判断返回到用户态还是内核态 - // 偏移 832-855: elr_el1, spsr_el1, esr_el1 - add x28, sp, #768 - ldr x9, [x28, #72] + // 检查 spsr_el1.M[3:0] 判断返回到用户态还是内核态 + // spsr_el1 at offset 264 + ldr x9, [sp, #264] // 提取 M[3:0] and x9, x9, #0xF cbnz x9, .Lret_to_kernel .Lret_to_user: // 返回用户态 - // 恢复页表(如果有的话) - ldr x9, [x28, #104] + // ttbr0_el1 at offset 296 + ldr x9, [sp, #296] msr ttbr0_el1, x9 isb diff --git a/src/arch/aarch64/interrupt.cpp b/src/arch/aarch64/interrupt.cpp index dfb470668..4eb2acbde 100644 --- a/src/arch/aarch64/interrupt.cpp +++ b/src/arch/aarch64/interrupt.cpp @@ -61,8 +61,8 @@ Interrupt::Interrupt() { } auto Interrupt::Do(uint64_t cause, cpu_io::TrapContext* context) -> void { - interrupt_handlers_[cause](cause, context); cpu_io::ICC_EOIR1_EL1::Write(cause); + interrupt_handlers_[cause](cause, context); } auto Interrupt::RegisterInterruptFunc(uint64_t cause, InterruptDelegate func) diff --git a/src/arch/aarch64/interrupt_main.cpp b/src/arch/aarch64/interrupt_main.cpp index b6b0e445e..c5a9e8786 100644 --- a/src/arch/aarch64/interrupt_main.cpp +++ b/src/arch/aarch64/interrupt_main.cpp @@ -88,6 +88,11 @@ extern "C" auto sync_current_el_spx_handler(cpu_io::TrapContext* context) extern "C" auto irq_current_el_spx_handler(cpu_io::TrapContext* context) -> void { auto cause = cpu_io::ICC_IAR1_EL1::INTID::Get(); + // GICv3: INTID 1020-1023 为特殊值(spurious),不应处理也不应写 EOI + if (cause >= 1020) { + klog::Warn("Spurious IRQ: INTID {}", cause); + return; + } InterruptSingleton::instance().Do(cause, context); } diff --git a/src/arch/aarch64/macro.S b/src/arch/aarch64/macro.S index 5512365c7..07d0dfbf6 100644 --- a/src/arch/aarch64/macro.S +++ b/src/arch/aarch64/macro.S @@ -14,12 +14,12 @@ /** * @brief TrapContext 结构体大小 */ -.equ kTrapContextSize, 896 +.equ kTrapContextSize, 320 /** * @brief CalleeSavedContext 结构体大小 */ -.equ kCalleeSavedContextSize, 176 +.equ kCalleeSavedContextSize, 112 /** * @brief 保存中断/异常上下文 (TrapContext) @@ -27,17 +27,16 @@ * 保存所有寄存器到栈上 * 偏移 0-247: x0-x30 * 偏移 248-255: _padding0 - * 偏移 256-767: q0-q31 - * 偏移 768-783: fpsr, fpcr - * 偏移 784-831: _padding1 (6 个 uint64_t) - * 偏移 832-855: elr_el1, spsr_el1, esr_el1 - * 偏移 856-879: sp_el0, tpidr_el0, ttbr0_el1 - * 偏移 880-895: sp_el1, tpidr_el1 + * 偏移 256-271: elr_el1, spsr_el1 + * 偏移 272-279: esr_el1 + * 偏移 280-295: sp_el0, tpidr_el0 + * 偏移 296-303: ttbr0_el1 + * 偏移 304-319: sp_el1, tpidr_el1 * - * @note 此宏会自动分配栈空间 (sub sp, sp, #896)。 + * @note 此宏会自动分配栈空间 (sub sp, sp, #320)。 */ .macro SaveTrapContext - // 分配栈空间 (896 字节) + // 分配栈空间 (320 字节) sub sp, sp, #kTrapContextSize // 保存通用寄存器 x0-x30 (偏移 0-247) @@ -59,49 +58,23 @@ str x30, [sp, #240] // 偏移 248-255: _padding0 (不需要显式保存) - // 保存浮点/SIMD寄存器 q0-q31 (偏移 256-767) - stp q0, q1, [sp, #256] - stp q2, q3, [sp, #288] - stp q4, q5, [sp, #320] - stp q6, q7, [sp, #352] - stp q8, q9, [sp, #384] - stp q10, q11, [sp, #416] - stp q12, q13, [sp, #448] - stp q14, q15, [sp, #480] - stp q16, q17, [sp, #512] - stp q18, q19, [sp, #544] - stp q20, q21, [sp, #576] - stp q22, q23, [sp, #608] - stp q24, q25, [sp, #640] - stp q26, q27, [sp, #672] - stp q28, q29, [sp, #704] - stp q30, q31, [sp, #736] - - // 保存浮点状态寄存器 (偏移 768-783) 和系统寄存器 (偏移 832-895) - // 使用临时基址避免偏移超出 stp/ldp 的范围 (-512 到 504) - add x28, sp, #768 - mrs x9, fpsr - mrs x10, fpcr - stp x9, x10, [x28] - // 偏移 784-831: _padding1 (不需要显式保存) - - // 保存系统寄存器 (偏移 832-895,相对 x28 偏移 64-127) + // 保存系统寄存器 (偏移 256-319) mrs x9, elr_el1 mrs x10, spsr_el1 - mrs x11, esr_el1 - stp x9, x10, [x28, #64] - str x11, [x28, #80] + stp x9, x10, [sp, #256] + mrs x9, esr_el1 + str x9, [sp, #272] mrs x9, sp_el0 mrs x10, tpidr_el0 - mrs x11, ttbr0_el1 - stp x9, x10, [x28, #88] - str x11, [x28, #104] + stp x9, x10, [sp, #280] + mrs x9, ttbr0_el1 + str x9, [sp, #296] // sp_el1: 保存分配前的栈指针 add x9, sp, #kTrapContextSize mrs x10, tpidr_el1 - stp x9, x10, [x28, #112] + stp x9, x10, [sp, #304] .endm /** @@ -112,50 +85,22 @@ * @note 此宏会恢复 sp,等于隐式释放栈空间。 */ .macro RestoreTrapContext - // 恢复系统寄存器 (偏移 832-895) 和浮点状态寄存器 (偏移 768-783) - // 使用临时基址避免偏移超出 ldp 的范围 (-512 到 504) - add x28, sp, #768 - - // 恢复系统寄存器 (偏移 832-895,相对 x28 偏移 64-127) - ldp x9, x10, [x28, #64] - ldr x11, [x28, #80] + // 恢复系统寄存器 (偏移 256-319) + ldp x9, x10, [sp, #256] msr elr_el1, x9 msr spsr_el1, x10 // 注意:不恢复 esr_el1,它是硬件在异常时自动设置的只读寄存器 - ldp x9, x10, [x28, #88] - ldr x11, [x28, #104] + ldp x9, x10, [sp, #280] + ldr x11, [sp, #296] msr sp_el0, x9 msr tpidr_el0, x10 msr ttbr0_el1, x11 - ldp x9, x10, [x28, #112] + ldp x9, x10, [sp, #304] // sp_el1 最后恢复 msr tpidr_el1, x10 - // 恢复浮点状态寄存器 (偏移 768-783,相对 x28 偏移 0) - ldp x9, x10, [x28] - msr fpsr, x9 - msr fpcr, x10 - - // 恢复浮点/SIMD寄存器 q0-q31 (偏移 256-767) - ldp q0, q1, [sp, #256] - ldp q2, q3, [sp, #288] - ldp q4, q5, [sp, #320] - ldp q6, q7, [sp, #352] - ldp q8, q9, [sp, #384] - ldp q10, q11, [sp, #416] - ldp q12, q13, [sp, #448] - ldp q14, q15, [sp, #480] - ldp q16, q17, [sp, #512] - ldp q18, q19, [sp, #544] - ldp q20, q21, [sp, #576] - ldp q22, q23, [sp, #608] - ldp q24, q25, [sp, #640] - ldp q26, q27, [sp, #672] - ldp q28, q29, [sp, #704] - ldp q30, q31, [sp, #736] - // 恢复通用寄存器 x0-x30 (偏移 0-247) ldp x0, x1, [sp, #16 * 0] ldp x2, x3, [sp, #16 * 1] @@ -183,8 +128,7 @@ * * 按照 context.hpp 中 CalleeSavedContext 的布局: * 偏移 0-95: x19-x30 (12 regs) - * 偏移 96-159: d8-d15 (8 regs,注意是 64 位的 d 寄存器,不是 128 位的 q 寄存器) - * 偏移 160-175: sp, pc (2 regs) + * 偏移 96-111: sp, pc (2 regs) * * @param base_reg 基地址寄存器 (保存的目标地址) */ @@ -197,16 +141,10 @@ stp x27, x28, [\base_reg, #64] stp x29, x30, [\base_reg, #80] - // 保存 d8-d15 (偏移 96-159,64 位浮点寄存器) - stp d8, d9, [\base_reg, #96] - stp d10, d11, [\base_reg, #112] - stp d12, d13, [\base_reg, #128] - stp d14, d15, [\base_reg, #144] - - // 保存 sp 和 pc (偏移 160-175) + // 保存 sp 和 pc (偏移 96-111) mov x9, sp mov x10, x30 // pc: 保存返回地址(Link Register) - stp x9, x10, [\base_reg, #160] + stp x9, x10, [\base_reg, #96] .endm /** @@ -217,14 +155,8 @@ * @param base_reg 基地址寄存器 (恢复的源地址) */ .macro RestoreCalleeSavedContext base_reg - // 恢复 d8-d15 (偏移 96-159) - ldp d8, d9, [\base_reg, #96] - ldp d10, d11, [\base_reg, #112] - ldp d12, d13, [\base_reg, #128] - ldp d14, d15, [\base_reg, #144] - - // 恢复 sp 和 pc (偏移 160-175) - ldp x9, x10, [\base_reg, #160] + // 恢复 sp 和 pc (偏移 96-111) + ldp x9, x10, [\base_reg, #96] mov sp, x9 // 恢复 x19-x30 (偏移 0-95) diff --git a/src/arch/aarch64/switch.S b/src/arch/aarch64/switch.S index 54297f523..e963d5dbb 100644 --- a/src/arch/aarch64/switch.S +++ b/src/arch/aarch64/switch.S @@ -27,18 +27,13 @@ switch_to: .global kernel_thread_entry .type kernel_thread_entry, @function +.extern kernel_thread_bootstrap kernel_thread_entry: // 上下文恢复时: // x30 (lr) = kernel_thread_entry // x19 = 真正的入口函数 entry // x20 = 参数 arg - // 将参数移动到 x0 - mov x0, x20 - - // 跳转到入口函数执行 - blr x19 - - // 如果函数返回,应该调用 exit 或者类似的处理 - // 目前暂时死循环,防止跑飞 - b . + mov x0, x19 + mov x1, x20 + bl kernel_thread_bootstrap diff --git a/src/arch/aarch64/timer.cpp b/src/arch/aarch64/timer.cpp index beeb54368..276f52c1d 100644 --- a/src/arch/aarch64/timer.cpp +++ b/src/arch/aarch64/timer.cpp @@ -27,7 +27,9 @@ uint64_t timer_intid{0}; auto TimerHandler(uint64_t /*cause*/, cpu_io::TrapContext* /*context*/) -> uint64_t { cpu_io::CNTV_TVAL_EL0::Write(interval); - TaskManagerSingleton::instance().TickUpdate(); + auto& tm = TaskManagerSingleton::instance(); + tm.TickUpdate(); + (void)tm.CheckPendingSignals(); return 0; } } // namespace diff --git a/src/arch/riscv64/interrupt.S b/src/arch/riscv64/interrupt.S index e6b2ba2f9..6dd53c53d 100644 --- a/src/arch/riscv64/interrupt.S +++ b/src/arch/riscv64/interrupt.S @@ -67,8 +67,8 @@ trap_return: mv sp, a0 // 恢复 Trap 上下文 - // 检查 sstatus.SPP (offset 64) 判断返回到用户态还是内核态 - ld t0, 64*8(sp) + // 检查 sstatus.SPP (offset 31) 判断返回到用户态还是内核态 + ld t0, 31*8(sp) andi t0, t0, 0x100 bnez t0, .Lret_to_kernel diff --git a/src/arch/riscv64/macro.S b/src/arch/riscv64/macro.S index 6a01ccaa4..4ab5432f9 100644 --- a/src/arch/riscv64/macro.S +++ b/src/arch/riscv64/macro.S @@ -14,26 +14,26 @@ /** * @brief 中断/异常上下文切换所需保存的寄存器数量 * - * 31 个通用寄存器(x1-x31) + 4 个 CSR + 32 个浮点寄存器(f0-f31) + 1 个 fcsr - * 总计: 31 + 4 + 32 + 1 = 68 + * 31 个通用寄存器(x1-x31) + 4 个 CSR + 1 个 padding + * 总计: 31 + 4 + 1 = 36 */ -.equ kTrapContextRegsCount, 68 +.equ kTrapContextRegsCount, 36 /** * @brief 中断/异常上下文切换所需保存的寄存器大小 - * 68 个寄存器 * 8 字节 = 544 字节(已对齐到 16 字节) + * 36 个寄存器 * 8 字节 = 288 字节(已对齐到 16 字节) */ .equ kTrapContextSize, ALIGN_16(kTrapContextRegsCount * kRegsBytes) /** * @brief 函数调用上下文切换所需保存的寄存器数量 (Callee-saved) - * ra, sp, s0-s11 (13 regs) + fs0-fs11 (12 regs) = 26 regs + * ra, sp, s0-s11 = 14 regs */ -.equ kCalleeSavedContextRegsCount, 26 +.equ kCalleeSavedContextRegsCount, 14 /** * @brief 函数调用上下文切换所需保存的寄存器大小 (Callee-saved) - * 26 * 8 = 208 字节(已对齐到 16 字节) + * 14 * 8 = 112 字节(已对齐到 16 字节) */ .equ kCalleeSavedContextSize, ALIGN_16(kCalleeSavedContextRegsCount * kRegsBytes) @@ -57,26 +57,6 @@ ld \a, ((\b) * kRegsBytes)(\c) .endm -/** - * @brief 将 float 寄存器 a 保存在 c 偏移 b 的位置 - * @param a 源浮点寄存器 - * @param b 偏移量 (乘以 kRegsBytes) - * @param c 基地址寄存器 - */ -.macro FsdBase a, b, c - fsd \a, ((\b) * kRegsBytes)(\c) -.endm - -/** - * @brief 从 c 的偏移 b 处获取数据并赋值给 float 寄存器 a - * @param a 目标浮点寄存器 - * @param b 偏移量 (乘以 kRegsBytes) - * @param c 基地址寄存器 - */ -.macro FldBase a, b, c - fld \a, ((\b) * kRegsBytes)(\c) -.endm - /** * @brief 保存中断/异常上下文 (TrapContext) * @@ -126,53 +106,15 @@ addi t0, sp, kTrapContextSize SdBase t0, 1, sp - // 保存浮点寄存器 - FsdBase ft0, 31, sp - FsdBase ft1, 32, sp - FsdBase ft2, 33, sp - FsdBase ft3, 34, sp - FsdBase ft4, 35, sp - FsdBase ft5, 36, sp - FsdBase ft6, 37, sp - FsdBase ft7, 38, sp - FsdBase fs0, 39, sp - FsdBase fs1, 40, sp - FsdBase fa0, 41, sp - FsdBase fa1, 42, sp - FsdBase fa2, 43, sp - FsdBase fa3, 44, sp - FsdBase fa4, 45, sp - FsdBase fa5, 46, sp - FsdBase fa6, 47, sp - FsdBase fa7, 48, sp - FsdBase fs2, 49, sp - FsdBase fs3, 50, sp - FsdBase fs4, 51, sp - FsdBase fs5, 52, sp - FsdBase fs6, 53, sp - FsdBase fs7, 54, sp - FsdBase fs8, 55, sp - FsdBase fs9, 56, sp - FsdBase fs10, 57, sp - FsdBase fs11, 58, sp - FsdBase ft8, 59, sp - FsdBase ft9, 60, sp - FsdBase ft10, 61, sp - FsdBase ft11, 62, sp - - // 保存 fcsr - frcsr t0 - SdBase t0, 63, sp - // 保存 CSRs (使用 t0 作为临时寄存器,t0 原值已 preservation at offset 4) csrr t0, sstatus - SdBase t0, 64, sp + SdBase t0, 31, sp csrr t0, sepc - SdBase t0, 65, sp + SdBase t0, 32, sp csrr t0, stval - SdBase t0, 66, sp + SdBase t0, 33, sp csrr t0, scause - SdBase t0, 67, sp + SdBase t0, 34, sp .endm /** @@ -184,53 +126,15 @@ */ .macro RestoreTrapContext // 恢复 CSRs (使用 t0 作为临时寄存器) - LdBase t0, 64, sp + LdBase t0, 31, sp csrw sstatus, t0 - LdBase t0, 65, sp + LdBase t0, 32, sp csrw sepc, t0 - LdBase t0, 66, sp + LdBase t0, 33, sp csrw stval, t0 - LdBase t0, 67, sp + LdBase t0, 34, sp csrw scause, t0 - // 恢复 fcsr - LdBase t0, 63, sp - fscsr t0 - - // 恢复浮点寄存器 - FldBase ft0, 31, sp - FldBase ft1, 32, sp - FldBase ft2, 33, sp - FldBase ft3, 34, sp - FldBase ft4, 35, sp - FldBase ft5, 36, sp - FldBase ft6, 37, sp - FldBase ft7, 38, sp - FldBase fs0, 39, sp - FldBase fs1, 40, sp - FldBase fa0, 41, sp - FldBase fa1, 42, sp - FldBase fa2, 43, sp - FldBase fa3, 44, sp - FldBase fa4, 45, sp - FldBase fa5, 46, sp - FldBase fa6, 47, sp - FldBase fa7, 48, sp - FldBase fs2, 49, sp - FldBase fs3, 50, sp - FldBase fs4, 51, sp - FldBase fs5, 52, sp - FldBase fs6, 53, sp - FldBase fs7, 54, sp - FldBase fs8, 55, sp - FldBase fs9, 56, sp - FldBase fs10, 57, sp - FldBase fs11, 58, sp - FldBase ft8, 59, sp - FldBase ft9, 60, sp - FldBase ft10, 61, sp - FldBase ft11, 62, sp - // 恢复通用寄存器 (除了 sp 和 t0) LdBase ra, 0, sp LdBase gp, 2, sp @@ -272,7 +176,7 @@ /** * @brief 保存 Callee-saved 寄存器到指定地址 - * 保存 ra, sp, s0-s11, fs0-fs11 + * 保存 ra, sp, s0-s11 * * @param base_reg 基地址寄存器 (保存的目标地址) */ @@ -291,24 +195,11 @@ SdBase s9, 11, \base_reg SdBase s10, 12, \base_reg SdBase s11, 13, \base_reg - - FsdBase fs0, 14, \base_reg - FsdBase fs1, 15, \base_reg - FsdBase fs2, 16, \base_reg - FsdBase fs3, 17, \base_reg - FsdBase fs4, 18, \base_reg - FsdBase fs5, 19, \base_reg - FsdBase fs6, 20, \base_reg - FsdBase fs7, 21, \base_reg - FsdBase fs8, 22, \base_reg - FsdBase fs9, 23, \base_reg - FsdBase fs10, 24, \base_reg - FsdBase fs11, 25, \base_reg .endm /** * @brief 从指定地址恢复 Callee-saved 寄存器 - * 恢复 ra, sp, s0-s11, fs0-fs11 + * 恢复 ra, sp, s0-s11 * * @param base_reg 基地址寄存器 (恢复的源地址) */ @@ -327,17 +218,4 @@ LdBase s9, 11, \base_reg LdBase s10, 12, \base_reg LdBase s11, 13, \base_reg - - FldBase fs0, 14, \base_reg - FldBase fs1, 15, \base_reg - FldBase fs2, 16, \base_reg - FldBase fs3, 17, \base_reg - FldBase fs4, 18, \base_reg - FldBase fs5, 19, \base_reg - FldBase fs6, 20, \base_reg - FldBase fs7, 21, \base_reg - FldBase fs8, 22, \base_reg - FldBase fs9, 23, \base_reg - FldBase fs10, 24, \base_reg - FldBase fs11, 25, \base_reg .endm diff --git a/src/arch/riscv64/switch.S b/src/arch/riscv64/switch.S index 358b32e29..cc060f34a 100644 --- a/src/arch/riscv64/switch.S +++ b/src/arch/riscv64/switch.S @@ -12,6 +12,8 @@ .global kernel_thread_entry .type kernel_thread_entry, @function +.extern kernel_thread_bootstrap + /** * @brief 线程上下文切换 * @@ -35,12 +37,6 @@ kernel_thread_entry: // s0 = 真正的入口函数 entry // s1 = 参数 arg - // 将参数移动到 a0 - mv a0, s1 - - // 跳转到入口函数执行 - jalr s0 - - // 如果函数返回,应该调用 exit 或者类似的处理 - // 目前暂时死循环,防止跑飞 - j . + mv a0, s0 + mv a1, s1 + call kernel_thread_bootstrap diff --git a/src/arch/riscv64/timer.cpp b/src/arch/riscv64/timer.cpp index 789136c76..2efb4f96b 100644 --- a/src/arch/riscv64/timer.cpp +++ b/src/arch/riscv64/timer.cpp @@ -18,7 +18,9 @@ uint64_t interval{0}; auto TimerHandler(uint64_t /*cause*/, cpu_io::TrapContext* /*context*/) -> uint64_t { sbi_set_timer(cpu_io::Time::Read() + interval); - TaskManagerSingleton::instance().TickUpdate(); + auto& tm = TaskManagerSingleton::instance(); + tm.TickUpdate(); + (void)tm.CheckPendingSignals(); return 0; } } // namespace diff --git a/src/arch/x86_64/apic/CMakeLists.txt b/src/arch/x86_64/apic/CMakeLists.txt deleted file mode 100644 index 634bb8cfb..000000000 --- a/src/arch/x86_64/apic/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright The SimpleKernel Contributors - -ADD_LIBRARY (apic INTERFACE) - -TARGET_INCLUDE_DIRECTORIES (apic INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) - -TARGET_SOURCES ( - apic - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/apic.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/local_apic.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/io_apic.cpp) diff --git a/src/arch/x86_64/apic/README.md b/src/arch/x86_64/apic/README.md deleted file mode 100644 index 6534c7168..000000000 --- a/src/arch/x86_64/apic/README.md +++ /dev/null @@ -1,204 +0,0 @@ -# APIC 驱动 - -这个目录包含了 Advanced Programmable Interrupt Controller (APIC) 的驱动实现,专为多核系统设计。 - -## 架构设计 - -### 多核系统中的 APIC 结构 - -在多核系统中,APIC 系统包含: - -1. **Local APIC**: 每个 CPU 核心都有一个 Local APIC - - 支持传统 xAPIC 和现代 x2APIC 模式 - - 优先使用 x2APIC,不可用时自动回退到 xAPIC - - 处理本地中断、IPI、定时器 - - xAPIC 通过内存映射访问,x2APIC 通过 MSR 访问 - -2. **IO APIC**: 系统级别的中断控制器 - - 通常有 1-2 个 IO APIC - - 处理外部设备中断并路由到不同 CPU - - 通过内存映射 I/O 访问 - -### 支持的 APIC 模式 - -#### x2APIC 模式 (推荐) -- 使用 MSR 接口访问 -- 支持更多 CPU(最多 2^32 个) -- 更高的性能 -- 需要 CPU 硬件支持 - -#### xAPIC 模式 (兼容性) -- 使用内存映射接口访问 -- 支持最多 256 个 CPU -- 兼容旧系统 -- 所有现代 CPU 都支持 - -驱动会自动检测 CPU 能力并选择最佳模式。 - -### 类结构 - -1. **LocalApic** - 管理 Local APIC 功能(per-CPU) - - 自动模式检测(x2APIC 优先,回退到 xAPIC) - - 处理器间中断 (IPI) - - 定时器功能 - - 中断优先级管理 - - CPU 启动支持(INIT/SIPI) - -2. **IoApic** - 管理 IO APIC 功能(系统级) - - IRQ 重定向 - - 中断屏蔽/取消屏蔽 - - 重定向表管理 - -3. **Apic** - 多核系统管理类 - - 管理多个 CPU 的 Local APIC - - 管理多个 IO APIC 实例 - - CPU 在线状态管理 - - AP (Application Processor) 启动 - -### 使用方式 - -```cpp -#include "apic.h" -#include "kernel.h" - -// 通过单例访问 APIC 管理器 -auto& apic = ApicSingleton::instance(); - -// 初始化 APIC 系统(在 BSP 上执行) -if (!apic.Init(max_cpu_count)) { - // 处理初始化失败 -} - -// 添加 IO APIC(通过 ACPI 或其他方式发现) -if (!apic.AddIoApic(io_apic_base_address, gsi_base)) { - // 处理添加失败 -} - -// 在每个 CPU 核心上初始化 Local APIC -if (!apic.InitCurrentCpuLocalApic()) { - // 处理初始化失败 -} - -// 发送 EOI 信号 -apic.SendEoi(); - -// 设置 Local APIC 定时器 -apic.SetupPeriodicTimer(100, 0xF0); - -// 发送 IPI 到指定 CPU -apic.SendIpi(target_apic_id, 0x30); - -// 启动 AP -apic.StartupAp(ap_apic_id, start_vector); - -// 设置 IRQ 重定向 -apic.SetIrqRedirection(1, 0x21, target_apic_id); // 键盘中断 - -// 标记 CPU 为在线 -apic.SetCpuOnline(apic.GetCurrentApicId()); -``` - -## 特性 - -### Local APIC (per-CPU) -- ✅ x2APIC 模式支持 -- ✅ 处理器间中断 (IPI) -- ✅ 定时器功能(周期性和单次) -- ✅ 任务优先级管理 -- ✅ CPU 启动支持 (INIT/SIPI) - -### IO APIC (系统级) -- ✅ IRQ 重定向 -- ✅ 中断屏蔽控制 -- ✅ 多 IO APIC 支持 -- ✅ GSI (Global System Interrupt) 管理 -- ✅ 重定向表管理 - -### 多核系统管理 -- ✅ 支持最多 256 个 CPU 核心 -- ✅ CPU 在线状态管理 -- ✅ AP 启动序列 -- ✅ IPI 广播和单播 -- ✅ 多 IO APIC 管理(最多 8 个) - -## 多核系统工作流程 - -### 1. 系统启动阶段(BSP) -```cpp -auto& apic = ApicSingleton::instance(); - -// 1. 初始化 APIC 系统 -apic.Init(detected_cpu_count); - -// 2. 通过 ACPI 发现并添加 IO APIC -for (auto& io_apic_entry : acpi_io_apics) { - apic.AddIoApic(io_apic_entry.base, io_apic_entry.gsi_base); -} - -// 3. 初始化 BSP 的 Local APIC -apic.InitCurrentCpuLocalApic(); -apic.SetCpuOnline(apic.GetCurrentApicId()); -``` - -### 2. AP 启动阶段 -```cpp -// 在 BSP 上启动 AP -for (auto apic_id : ap_list) { - apic.StartupAp(apic_id, ap_startup_vector); -} - -// 在每个 AP 上执行 -void ap_main() { - auto& apic = ApicSingleton::instance(); - apic.InitCurrentCpuLocalApic(); - apic.SetCpuOnline(apic.GetCurrentApicId()); -} -``` - -### 3. 运行时中断管理 -```cpp -// 设置设备中断路由 -apic.SetIrqRedirection(keyboard_irq, KEYBOARD_VECTOR, target_cpu); -apic.SetIrqRedirection(timer_irq, TIMER_VECTOR, target_cpu); - -// 发送 IPI 通知其他 CPU -apic.SendIpi(target_cpu, IPI_VECTOR); -apic.BroadcastIpi(BROADCAST_VECTOR); -``` - -## 限制 - -- 仅支持 x2APIC 模式 -- 不支持传统 xAPIC 模式 -- 最多支持 256 个 CPU 核心 -- 最多支持 8 个 IO APIC -- 当前实现为接口定义,具体功能需要实现 - -## 文件结构 - -``` -apic/ -├── include/ -│ └── apic.h # 头文件,包含所有类定义 -├── apic.cpp # Apic 多核管理类实现 -├── local_apic.cpp # LocalApic 类实现 -├── io_apic.cpp # IoApic 类实现 -├── CMakeLists.txt # 构建配置 -└── README.md # 说明文档 -``` - -## 依赖 - -- cpu_io 库(用于 MSR 操作) -- kernel_log.hpp(用于日志输出) -- kernel.h(用于 etl::singleton 命名别名,如 ApicSingleton) - -## 注意事项 - -1. Local APIC 操作是 per-CPU 的,每个 CPU 访问自己的 Local APIC -2. IO APIC 是系统级别的,多个 CPU 可能同时访问,需要考虑同步 -3. 使用 klog 进行日志输出 -4. 使用 std::array 而不是 std::vector -5. 所有接口目前都是空实现,需要根据具体需求填充 -6. 支持 GSI 到 IRQ 的映射管理 -7. CPU 在线状态管理用于多核协调 diff --git a/src/arch/x86_64/apic/apic.cpp b/src/arch/x86_64/apic/apic.cpp deleted file mode 100644 index 08ddcff94..000000000 --- a/src/arch/x86_64/apic/apic.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "apic.h" - -#include - -#include -#include - -#include "kernel_log.hpp" - -Apic::Apic(size_t cpu_count) : cpu_count_(cpu_count) { - // 禁用传统的 8259A PIC 以避免与 APIC 冲突 - cpu_io::Pic::Disable(); -} - -auto Apic::InitCurrentCpuLocalApic() -> Expected { - return local_apic_.Init() - .and_then([]() -> Expected { - klog::Info( - "Local APIC initialized successfully for CPU with APIC ID {:#x}", - cpu_io::GetCurrentCoreId()); - return {}; - }) - .or_else([](Error err) -> Expected { - klog::Err("Failed to initialize Local APIC for current CPU: {}", - err.message()); - return std::unexpected(err); - }); -} - -auto Apic::SetIrqRedirection(uint8_t irq, uint8_t vector, - uint32_t destination_apic_id, bool mask) - -> Expected { - // 检查 IRQ 是否在有效范围内 - if (irq >= io_apic_.GetMaxRedirectionEntries()) { - klog::Err("IRQ {} exceeds IO APIC range (max: {})", irq, - io_apic_.GetMaxRedirectionEntries() - 1); - return std::unexpected(Error(ErrorCode::kApicInvalidIrq)); - } - - // 设置重定向 - io_apic_.SetIrqRedirection(irq, vector, destination_apic_id, mask); - return {}; -} - -auto Apic::MaskIrq(uint8_t irq) -> Expected { - // 检查 IRQ 是否在有效范围内 - if (irq >= io_apic_.GetMaxRedirectionEntries()) { - klog::Err("IRQ {} exceeds IO APIC range (max: {})", irq, - io_apic_.GetMaxRedirectionEntries() - 1); - return std::unexpected(Error(ErrorCode::kApicInvalidIrq)); - } - - io_apic_.MaskIrq(irq); - return {}; -} - -auto Apic::UnmaskIrq(uint8_t irq) -> Expected { - // 检查 IRQ 是否在有效范围内 - if (irq >= io_apic_.GetMaxRedirectionEntries()) { - klog::Err("IRQ {} exceeds IO APIC range (max: {})", irq, - io_apic_.GetMaxRedirectionEntries() - 1); - return std::unexpected(Error(ErrorCode::kApicInvalidIrq)); - } - - io_apic_.UnmaskIrq(irq); - return {}; -} - -auto Apic::SendIpi(uint32_t target_apic_id, uint8_t vector) const - -> Expected { - return local_apic_.SendIpi(target_apic_id, vector); -} - -auto Apic::BroadcastIpi(uint8_t vector) const -> Expected { - return local_apic_.BroadcastIpi(vector); -} - -auto Apic::StartupAp(uint32_t apic_id, uint64_t ap_code_addr, - size_t ap_code_size, uint64_t target_addr) const - -> Expected { - assert(ap_code_addr != 0 && "AP code address must not be null"); - assert(ap_code_size != 0 && "AP code size must not be zero"); - assert((target_addr & 0xFFF) == 0 && "Target address must be 4KB aligned"); - assert(target_addr < 0x100000 && - "Target address exceeds real mode limit (1MB)"); - - std::memcpy(reinterpret_cast(target_addr), - reinterpret_cast(ap_code_addr), ap_code_size); - - // 验证复制是否成功 - if (std::memcmp(reinterpret_cast(target_addr), - reinterpret_cast(ap_code_addr), - ap_code_size) != 0) { - klog::Err("AP code copy verification failed"); - return std::unexpected(Error(ErrorCode::kApicCodeCopyFailed)); - } - - // 计算启动向量 (物理地址 / 4096) - auto start_vector = static_cast(target_addr >> 12); - // 使用 Local APIC 发送 INIT-SIPI-SIPI 序列 - local_apic_.WakeupAp(apic_id, start_vector); - - return {}; -} - -auto Apic::StartupAllAps(uint64_t ap_code_addr, size_t ap_code_size, - uint64_t target_addr) const -> void { - assert(ap_code_addr != 0 && "AP code address must not be null"); - assert(ap_code_size != 0 && "AP code size must not be zero"); - assert((target_addr & 0xFFF) == 0 && "Target address must be 4KB aligned"); - assert(target_addr < 0x100000 && - "Target address exceeds real mode limit (1MB)"); - - // 启动 APIC ID 0 到 cpu_count_-1 的所有处理器 - // 跳过当前的 BSP (Bootstrap Processor) - for (size_t apic_id = 0; apic_id < cpu_count_; apic_id++) { - // 跳过当前 BSP - if (static_cast(apic_id) == cpu_io::GetCurrentCoreId()) { - continue; - } - StartupAp(static_cast(apic_id), ap_code_addr, ap_code_size, - target_addr) - .or_else([apic_id](Error err) -> Expected { - klog::Err("Failed to start AP with APIC ID {:#x}: {}", apic_id, - err.message()); - return std::unexpected(err); - }); - } -} - -auto Apic::SendEoi() const -> void { local_apic_.SendEoi(); } - -auto Apic::SetupPeriodicTimer(uint32_t frequency_hz, uint8_t vector) const - -> void { - local_apic_.SetupPeriodicTimer(frequency_hz, vector); -} - -auto Apic::PrintInfo() const -> void { - local_apic_.PrintInfo(); - io_apic_.PrintInfo(); -} diff --git a/src/arch/x86_64/apic/include/apic.h b/src/arch/x86_64/apic/include/apic.h deleted file mode 100644 index cdeecaf7b..000000000 --- a/src/arch/x86_64/apic/include/apic.h +++ /dev/null @@ -1,134 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#pragma once - -#include - -#include -#include - -#include "expected.hpp" -#include "io_apic.h" -#include "local_apic.h" - -/** - * @brief APIC 管理类,管理整个系统的 Local APIC 和 IO APIC - * @note 在多核系统中: - * - Local APIC 是 per-CPU 的,每个核心通过 MSR 访问自己的 Local APIC - * - IO APIC 是系统级别的,通常有 1-2 个,处理外部中断 - */ -class Apic { - public: - /** - * @brief 初始化当前 CPU 的 Local APIC - * @return Expected 初始化成功返回空值,失败返回错误 - * @note 每个 CPU 核心启动时都需要调用此函数 - */ - [[nodiscard]] auto InitCurrentCpuLocalApic() -> Expected; - - /** - * @brief 设置 IRQ 重定向 - * @param irq IRQ 号 - * @param vector 中断向量 - * @param destination_apic_id 目标 APIC ID - * @param mask 是否屏蔽中断 - * @return Expected 设置成功返回空值,失败返回错误 - */ - [[nodiscard]] auto SetIrqRedirection(uint8_t irq, uint8_t vector, - uint32_t destination_apic_id, - bool mask = false) -> Expected; - - /** - * @brief 屏蔽 IRQ - * @param irq IRQ 号 - * @return Expected 操作成功返回空值,失败返回错误 - */ - [[nodiscard]] auto MaskIrq(uint8_t irq) -> Expected; - - /** - * @brief 取消屏蔽 IRQ - * @param irq IRQ 号 - * @return Expected 操作成功返回空值,失败返回错误 - */ - [[nodiscard]] auto UnmaskIrq(uint8_t irq) -> Expected; - - /** - * @brief 发送 IPI 到指定 CPU - * @param target_apic_id 目标 CPU 的 APIC ID - * @param vector 中断向量 - * @return Expected 发送成功返回空值,失败返回错误 - */ - [[nodiscard]] auto SendIpi(uint32_t target_apic_id, uint8_t vector) const - -> Expected; - - /** - * @brief 广播 IPI 到所有其他 CPU - * @param vector 中断向量 - * @return Expected 广播成功返回空值,失败返回错误 - */ - [[nodiscard]] auto BroadcastIpi(uint8_t vector) const -> Expected; - - /** - * @brief 启动 AP (Application Processor) - * @param apic_id 目标 APIC ID - * @param ap_code_addr AP 启动代码的虚拟地址 - * @param ap_code_size AP 启动代码的大小 - * @param target_addr AP 代码要复制到的目标物理地址 - * @return Expected 启动成功返回空值,失败返回错误 - * @note 函数内部会将启动代码复制到指定的目标地址,并计算 start_vector - */ - [[nodiscard]] auto StartupAp(uint32_t apic_id, uint64_t ap_code_addr, - size_t ap_code_size, uint64_t target_addr) const - -> Expected; - - /** - * @brief 唤醒所有应用处理器 (AP) - * @param ap_code_addr AP 启动代码的虚拟地址 - * @param ap_code_size AP 启动代码的大小 - * @param target_addr AP 代码要复制到的目标物理地址 - * @note 此方法会尝试唤醒除当前 BSP 外的所有 CPU 核心 - * @note 函数内部会将启动代码复制到指定的目标地址,并计算 start_vector - */ - auto StartupAllAps(uint64_t ap_code_addr, size_t ap_code_size, - uint64_t target_addr) const -> void; - - /** - * @brief 发送 EOI 信号给当前 CPU 的 Local APIC - */ - auto SendEoi() const -> void; - - /** - * @brief 设置 Local APIC 定时器 - * @param frequency_hz 定时器频率(Hz) - * @param vector 中断向量号 - */ - auto SetupPeriodicTimer(uint32_t frequency_hz, uint8_t vector) const -> void; - - /// @name 构造/析构函数 - /// @{ - explicit Apic(const size_t cpu_count); - Apic() = default; - Apic(const Apic&) = delete; - Apic(Apic&&) = default; - auto operator=(const Apic&) -> Apic& = delete; - auto operator=(Apic&&) -> Apic& = default; - ~Apic() = default; - /// @} - - /** - * @brief 打印所有 APIC 信息(调试用) - */ - auto PrintInfo() const -> void; - - private: - /// Local APIC 操作接口(静态实例,用于当前 CPU) - LocalApic local_apic_{}; - - /// 只支持一个 IO APIC - IoApic io_apic_{}; - - /// 系统 CPU 数量 - size_t cpu_count_{0}; -}; diff --git a/src/arch/x86_64/apic/include/io_apic.h b/src/arch/x86_64/apic/include/io_apic.h deleted file mode 100644 index a6597295e..000000000 --- a/src/arch/x86_64/apic/include/io_apic.h +++ /dev/null @@ -1,171 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#pragma once - -#include - -#include -#include - -/** - * @brief IO APIC 驱动类 - * @note IO APIC 是系统级别的中断控制器,负责处理外部设备中断 - */ -class IoApic { - public: - /** - * @brief 设置 IO APIC 重定向表项 - * @param irq IRQ 号 - * @param vector 中断向量 - * @param destination_apic_id 目标 APIC ID - * @param mask 是否屏蔽中断 - */ - auto SetIrqRedirection(uint8_t irq, uint8_t vector, - uint32_t destination_apic_id, bool mask = false) const - -> void; - - /** - * @brief 屏蔽 IRQ - * @param irq IRQ 号 - */ - auto MaskIrq(uint8_t irq) const -> void; - - /** - * @brief 取消屏蔽 IRQ - * @param irq IRQ 号 - */ - auto UnmaskIrq(uint8_t irq) const -> void; - - /** - * @brief 获取 IO APIC ID - * @return uint32_t IO APIC ID - */ - [[nodiscard]] auto GetId() const -> uint32_t; - - /** - * @brief 获取 IO APIC 版本 - * @return uint32_t IO APIC 版本 - */ - [[nodiscard]] auto GetVersion() const -> uint32_t; - - /** - * @brief 获取 IO APIC 最大重定向条目数 - * @return uint32_t 最大重定向条目数 - */ - [[nodiscard]] auto GetMaxRedirectionEntries() const -> uint32_t; - - /** - * @brief 打印 IO APIC 信息(调试用) - */ - auto PrintInfo() const -> void; - - /// @name 构造/析构函数 - /// @{ - IoApic(); - IoApic(const IoApic&) = delete; - IoApic(IoApic&&) = default; - auto operator=(const IoApic&) -> IoApic& = delete; - auto operator=(IoApic&&) -> IoApic& = default; - ~IoApic() = default; - /// @} - - private: - /// @name IO APIC 寄存器偏移常数 - /// @{ - /// 寄存器选择偏移 - static constexpr uint32_t kRegSel = 0x00; - /// 寄存器窗口偏移 - static constexpr uint32_t kRegWin = 0x10; - /// @} - - /// @name IO APIC 寄存器索引常数 - /// @{ - /// IO APIC ID 寄存器索引 - static constexpr uint32_t kRegId = 0x00; - /// IO APIC 版本寄存器索引 - static constexpr uint32_t kRegVer = 0x01; - /// IO APIC 仲裁寄存器索引 - static constexpr uint32_t kRegArb = 0x02; - /// 重定向表基址索引 - static constexpr uint32_t kRedTblBase = 0x10; - /// @} - - /// @name 重定向表项位字段常数 - /// @{ - /// 中断向量位掩码 (位 0-7) - static constexpr uint64_t kVectorMask = 0xFF; - /// 传递模式位移 (位 8-10) - static constexpr uint32_t kDeliveryModeShift = 8; - /// 目标模式位 (位 11) - static constexpr uint64_t kDestModeBit = 1ULL << 11; - /// 传递状态位 (位 12) - static constexpr uint64_t kDeliveryStatusBit = 1ULL << 12; - /// 极性位 (位 13) - static constexpr uint64_t kPolarityBit = 1ULL << 13; - /// 远程 IRR 位 (位 14) - static constexpr uint64_t kRemoteIrrBit = 1ULL << 14; - /// 触发模式位 (位 15) - static constexpr uint64_t kTriggerModeBit = 1ULL << 15; - /// 屏蔽位 (位 16) - static constexpr uint64_t kMaskBit = 1ULL << 16; - /// 目标 APIC ID 位移 (位 56-63) - static constexpr uint32_t kDestApicIdShift = 56; - /// 目标 APIC ID 掩码 - static constexpr uint64_t kDestApicIdMask = 0xFF; - /// @} - - /// @name 传递模式常数 - /// @{ - /// 固定传递模式 - static constexpr uint32_t kDeliveryModeFixed = 0x0; - /// 最低优先级传递模式 - static constexpr uint32_t kDeliveryModeLowestPriority = 0x1; - /// SMI 传递模式 - static constexpr uint32_t kDeliveryModeSmi = 0x2; - /// NMI 传递模式 - static constexpr uint32_t kDeliveryModeNmi = 0x4; - /// INIT 传递模式 - static constexpr uint32_t kDeliveryModeInit = 0x5; - /// ExtINT 传递模式 - static constexpr uint32_t kDeliveryModeExtInt = 0x7; - /// @} - - /// @name IO APIC 基地址相关常数 - /// @{ - /// 默认 IO APIC 基地址 - static constexpr uint64_t kDefaultIoApicBase = 0xFEC00000; - /// @} - - /// IO APIC 基地址 - uint64_t base_address_{kDefaultIoApicBase}; - - /** - * @brief 读取 IO APIC 寄存器 - * @param reg 寄存器索引 - * @return uint32_t 寄存器值 - */ - [[nodiscard]] auto Read(uint32_t reg) const -> uint32_t; - - /** - * @brief 写入 IO APIC 寄存器 - * @param reg 寄存器索引 - * @param value 要写入的值 - */ - auto Write(uint32_t reg, uint32_t value) const -> void; - - /** - * @brief 读取 IO APIC 重定向表项 - * @param irq IRQ 号 - * @return uint64_t 重定向表项值 - */ - [[nodiscard]] auto ReadRedirectionEntry(uint8_t irq) const -> uint64_t; - - /** - * @brief 写入 IO APIC 重定向表项 - * @param irq IRQ 号 - * @param value 重定向表项值 - */ - auto WriteRedirectionEntry(uint8_t irq, uint64_t value) const -> void; -}; diff --git a/src/arch/x86_64/apic/include/local_apic.h b/src/arch/x86_64/apic/include/local_apic.h deleted file mode 100644 index 516e97959..000000000 --- a/src/arch/x86_64/apic/include/local_apic.h +++ /dev/null @@ -1,303 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#pragma once - -#include - -#include -#include - -#include "expected.hpp" - -/** - * @brief Local APIC 驱动类 - * @note 支持 xAPIC 和 x2APIC 模式,优先使用 x2APIC - */ -class LocalApic { - public: - /** - * @brief 初始化 Local APIC - * @return Expected 初始化成功返回空值,失败返回错误 - */ - [[nodiscard]] auto Init() -> Expected; - - /** - * @brief 获取 APIC 版本信息 - * @return uint32_t APIC 版本 - */ - [[nodiscard]] auto GetApicVersion() const -> uint32_t; - - /** - * @brief 发送中断结束信号 (EOI) - */ - auto SendEoi() const -> void; - - /** - * @brief 发送处理器间中断 (IPI) - * @param destination_apic_id 目标 APIC ID - * @param vector 中断向量 - * @return Expected 发送成功返回空值,失败返回错误 - */ - [[nodiscard]] auto SendIpi(uint32_t destination_apic_id, uint8_t vector) const - -> Expected; - - /** - * @brief 广播 IPI 到所有其他 CPU - * @param vector 中断向量 - * @return Expected 广播成功返回空值,失败返回错误 - */ - [[nodiscard]] auto BroadcastIpi(uint8_t vector) const -> Expected; - - /** - * @brief 设置任务优先级 - * @param priority 优先级值 - */ - auto SetTaskPriority(uint8_t priority) const -> void; - - /** - * @brief 获取任务优先级 - * @return uint8_t 当前任务优先级 - */ - [[nodiscard]] auto GetTaskPriority() const -> uint8_t; - - /** - * @brief 启用 Local APIC 定时器 - * @param initial_count 初始计数值 - * @param divide_value 分频值 - * @param vector 定时器中断向量 - * @param periodic 是否为周期性定时器 - */ - auto EnableTimer(uint32_t initial_count, uint32_t divide_value, - uint8_t vector, bool periodic = true) const -> void; - - /** - * @brief 禁用 Local APIC 定时器 - */ - auto DisableTimer() const -> void; - - /** - * @brief 获取定时器当前计数值 - * @return uint32_t 当前计数值 - */ - [[nodiscard]] auto GetTimerCurrentCount() const -> uint32_t; - - /** - * @brief 设置周期性定时器 - * @param frequency_hz 定时器频率(Hz) - * @param vector 定时器中断向量 - */ - auto SetupPeriodicTimer(uint32_t frequency_hz, uint8_t vector) const -> void; - - /** - * @brief 设置单次定时器 - * @param microseconds 延时时间(微秒) - * @param vector 定时器中断向量 - */ - auto SetupOneShotTimer(uint32_t microseconds, uint8_t vector) const -> void; - - /** - * @brief 发送 INIT IPI - * @param destination_apic_id 目标 APIC ID - */ - auto SendInitIpi(uint32_t destination_apic_id) const -> void; - - /** - * @brief 发送 SIPI (Startup IPI) - * @param destination_apic_id 目标 APIC ID - * @param start_page 启动页面地址(4KB 页面) - */ - auto SendStartupIpi(uint32_t destination_apic_id, uint8_t start_page) const - -> void; - - /** - * @brief 唤醒应用处理器 (AP) - * @param destination_apic_id 目标 APIC ID - * @param start_vector 启动向量(启动代码的物理地址 / 4096) - * @return true 唤醒成功 - * @note 执行标准的 INIT-SIPI-SIPI 序列来唤醒 AP - */ - auto WakeupAp(uint32_t destination_apic_id, uint8_t start_vector) const - -> void; - - /** - * @brief 配置 Local Vector Table 条目 - */ - auto ConfigureLvtEntries() const -> void; - - /** - * @brief 读取错误状态 - * @return uint32_t 错误状态寄存器值 - */ - [[nodiscard]] auto ReadErrorStatus() const -> uint32_t; - - /** - * @brief 打印 Local APIC 信息(调试用) - */ - auto PrintInfo() const -> void; - - /// @name 构造/析构函数 - /// @{ - LocalApic() = default; - LocalApic(const LocalApic&) = delete; - LocalApic(LocalApic&&) = default; - auto operator=(const LocalApic&) -> LocalApic& = delete; - auto operator=(LocalApic&&) -> LocalApic& = default; - ~LocalApic() = default; - /// @} - - private: - /// @name xAPIC 寄存器偏移量常数 - /// @{ - /// APIC ID 寄存器偏移 - static constexpr uint32_t kXApicIdOffset = 0x20; - /// 版本寄存器偏移 - static constexpr uint32_t kXApicVersionOffset = 0x30; - /// 任务优先级寄存器偏移 - static constexpr uint32_t kXApicTprOffset = 0x80; - /// EOI 寄存器偏移 - static constexpr uint32_t kXApicEoiOffset = 0xB0; - /// 虚假中断向量寄存器偏移 - static constexpr uint32_t kXApicSivrOffset = 0xF0; - /// 错误状态寄存器偏移 - static constexpr uint32_t kXApicEsrOffset = 0x280; - /// ICR 低位寄存器偏移 - static constexpr uint32_t kXApicIcrLowOffset = 0x300; - /// ICR 高位寄存器偏移 - static constexpr uint32_t kXApicIcrHighOffset = 0x310; - /// LVT 定时器寄存器偏移 - static constexpr uint32_t kXApicLvtTimerOffset = 0x320; - /// LVT LINT0 寄存器偏移 - static constexpr uint32_t kXApicLvtLint0Offset = 0x350; - /// LVT LINT1 寄存器偏移 - static constexpr uint32_t kXApicLvtLint1Offset = 0x360; - /// LVT 错误寄存器偏移 - static constexpr uint32_t kXApicLvtErrorOffset = 0x370; - /// 定时器初始计数寄存器偏移 - static constexpr uint32_t kXApicTimerInitCountOffset = 0x380; - /// 定时器当前计数寄存器偏移 - static constexpr uint32_t kXApicTimerCurrCountOffset = 0x390; - /// 定时器分频寄存器偏移 - static constexpr uint32_t kXApicTimerDivideOffset = 0x3E0; - /// @} - - /// @name 位掩码和位移常数 - /// @{ - /// xAPIC ID 位移 - static constexpr uint32_t kApicIdShift = 24; - /// xAPIC ID 掩码 - static constexpr uint32_t kApicIdMask = 0xFF; - /// APIC 软件启用位 - static constexpr uint32_t kApicSoftwareEnableBit = 0x100; - /// 虚假中断向量 - static constexpr uint32_t kSpuriousVector = 0xFF; - /// LVT 掩码位 - static constexpr uint32_t kLvtMaskBit = 0x10000; - /// LVT 周期模式位 - static constexpr uint32_t kLvtPeriodicMode = 0x20000; - /// ICR 传递状态位 - static constexpr uint32_t kIcrDeliveryStatusBit = 0x1000; - /// ICR 目标位移 - static constexpr uint32_t kIcrDestShift = 24; - /// ICR 广播模式位 - static constexpr uint32_t kIcrBroadcastMode = 0xC0000; - /// INIT IPI 模式 - static constexpr uint32_t kInitIpiMode = 0x500; - /// SIPI 模式 - static constexpr uint32_t kSipiMode = 0x600; - /// ExtINT 传递模式 - static constexpr uint32_t kExtIntMode = 0x700; - /// NMI 传递模式 - static constexpr uint32_t kNmiMode = 0x400; - /// 错误中断向量 - static constexpr uint8_t kErrorVector = 0xEF; - /// @} - - /// @name 定时器相关常数 - /// @{ - /// 默认 APIC 时钟频率 (100MHz) - static constexpr uint32_t kDefaultApicClockHz = 100000000; - /// 定时器分频 1 - static constexpr uint32_t kTimerDivideBy1 = 0x0B; - /// 定时器分频 16 - static constexpr uint32_t kTimerDivideBy16 = 0x03; - /// 校准用的计数值 - static constexpr uint32_t kCalibrationCount = 0xFFFFFFFF; - /// 校准延时循环次数 - static constexpr uint32_t kCalibrationDelayLoop = 1000000; - /// 校准倍数 (10ms -> 1s) - static constexpr uint32_t kCalibrationMultiplier = 100; - /// 每秒微秒数 - static constexpr uint32_t kMicrosecondsPerSecond = 1000000; - /// @} - - /// @name APIC 基地址相关常数 - /// @{ - /// 默认 APIC 基地址 - static constexpr uint64_t kDefaultApicBase = 0xFEE00000; - /// APIC 基地址掩码 - static constexpr uint64_t kApicBaseMask = 0xFFFFF000ULL; - /// APIC 全局启用位 - static constexpr uint64_t kApicGlobalEnableBit = 1ULL << 11; - /// x2APIC 启用位 - static constexpr uint64_t kX2ApicEnableBit = 1ULL << 10; - /// APIC 基地址控制位掩码 - static constexpr uint64_t kApicBaseControlMask = 0xFFF; - /// @} - - /// 当前 APIC 模式(true = x2APIC, false = xAPIC) - bool is_x2apic_mode_{false}; - - /// APIC 基地址(仅用于 xAPIC 模式) - uint64_t apic_base_{kDefaultApicBase}; - - /** - * @brief 检查 CPU 是否支持 x2APIC - * @return true 支持 x2APIC - * @return false 不支持 x2APIC - */ - [[nodiscard]] auto CheckX2ApicSupport() const -> bool; - - /** - * @brief 启用传统 xAPIC 模式 - * @return true 启用成功 - * @return false 启用失败 - */ - [[nodiscard]] auto EnableXApic() const -> bool; - - /** - * @brief 禁用传统 xAPIC 模式 - * @return true 禁用成功 - * @return false 禁用失败或xAPIC未启用 - */ - [[nodiscard]] auto DisableXApic() const -> bool; - - /** - * @brief 检查传统 xAPIC 是否启用 - * @return true xAPIC 已启用 - * @return false xAPIC 未启用 - */ - [[nodiscard]] auto IsXApicEnabled() const -> bool; - - /** - * @brief 启用 x2APIC 模式 - * @return true 启用成功 - * @return false 启用失败 - */ - [[nodiscard]] auto EnableX2Apic() const -> bool; - - /** - * @brief 禁用 x2APIC 模式 - * @return true 禁用成功 - * @return false 禁用失败或x2APIC未启用 - */ - [[nodiscard]] auto DisableX2Apic() const -> bool; - - /** - * @brief 检查 x2APIC 是否启用 - * @return true x2APIC 已启用 - * @return false x2APIC 未启用 - */ - [[nodiscard]] auto IsX2ApicEnabled() const -> bool; -}; diff --git a/src/arch/x86_64/apic/io_apic.cpp b/src/arch/x86_64/apic/io_apic.cpp deleted file mode 100644 index a80914a1f..000000000 --- a/src/arch/x86_64/apic/io_apic.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "io_apic.h" - -#include - -#include "kernel_log.hpp" - -IoApic::IoApic() { - // 禁用所有重定向条目(设置为屏蔽状态) - for (uint32_t i = 0; i < GetMaxRedirectionEntries(); i++) { - auto entry = ReadRedirectionEntry(i); - entry |= kMaskBit; - WriteRedirectionEntry(i, entry); - } - - klog::Info("IO APIC initialization completed"); -} - -auto IoApic::SetIrqRedirection(uint8_t irq, uint8_t vector, - uint32_t destination_apic_id, bool mask) const - -> void { - // 检查 IRQ 是否在有效范围内 - auto max_entries = GetMaxRedirectionEntries(); - if (irq >= max_entries) { - klog::Err("IRQ {} exceeds maximum entries {}", irq, max_entries); - return; - } - - // 构造重定向表项 - uint64_t entry = 0; - - // 设置中断向量 (位 0-7) - entry |= vector & kVectorMask; - // 设置屏蔽位 (位 16) - if (mask) { - entry |= kMaskBit; - } - - // 设置目标 APIC ID (位 56-63) - entry |= (static_cast(destination_apic_id & kDestApicIdMask) - << kDestApicIdShift); - - WriteRedirectionEntry(irq, entry); -} - -auto IoApic::MaskIrq(uint8_t irq) const -> void { - auto max_entries = GetMaxRedirectionEntries(); - if (irq >= max_entries) { - klog::Err("IRQ {} exceeds maximum entries {}", irq, max_entries); - return; - } - - auto entry = ReadRedirectionEntry(irq); - entry |= kMaskBit; - WriteRedirectionEntry(irq, entry); -} - -auto IoApic::UnmaskIrq(uint8_t irq) const -> void { - auto max_entries = GetMaxRedirectionEntries(); - if (irq >= max_entries) { - klog::Err("IRQ {} exceeds maximum entries {}", irq, max_entries); - return; - } - - auto entry = ReadRedirectionEntry(irq); - entry &= ~kMaskBit; - WriteRedirectionEntry(irq, entry); -} - -auto IoApic::GetId() const -> uint32_t { - // ID 位于位 24-27 - return (Read(kRegId) >> 24) & 0x0F; -} - -auto IoApic::GetVersion() const -> uint32_t { - // 版本位于位 0-7 - return Read(kRegVer) & 0xFF; -} - -auto IoApic::GetMaxRedirectionEntries() const -> uint32_t { - // MRE 位于位 16-23,实际数量需要 +1 - return ((Read(kRegVer) >> 16) & 0xFF) + 1; -} - -auto IoApic::PrintInfo() const -> void { - klog::Info("IO APIC Information"); - klog::Info("Base Address: {:#x}", base_address_); - klog::Info("ID: {:#x}", GetId()); - klog::Info("Version: {:#x}", GetVersion()); - klog::Info("Max Redirection Entries: {}", GetMaxRedirectionEntries()); -} - -auto IoApic::Read(uint32_t reg) const -> uint32_t { - etl::io_port_wo sel{ - reinterpret_cast(base_address_ + kRegSel)}; - sel.write(reg); - etl::io_port_ro win{ - reinterpret_cast(base_address_ + kRegWin)}; - return win.read(); -} - -auto IoApic::Write(uint32_t reg, uint32_t value) const -> void { - etl::io_port_wo sel{ - reinterpret_cast(base_address_ + kRegSel)}; - sel.write(reg); - etl::io_port_wo win{ - reinterpret_cast(base_address_ + kRegWin)}; - win.write(value); -} - -auto IoApic::ReadRedirectionEntry(uint8_t irq) const -> uint64_t { - auto low_reg = kRedTblBase + (irq * 2); - auto high_reg = low_reg + 1; - - auto low = Read(low_reg); - auto high = Read(high_reg); - - return (static_cast(high) << 32) | low; -} - -auto IoApic::WriteRedirectionEntry(uint8_t irq, uint64_t value) const -> void { - auto low_reg = kRedTblBase + (irq * 2); - auto high_reg = low_reg + 1; - - auto low = static_cast(value & 0xFFFFFFFF); - auto high = static_cast((value >> 32) & 0xFFFFFFFF); - - Write(low_reg, low); - Write(high_reg, high); -} diff --git a/src/arch/x86_64/apic/local_apic.cpp b/src/arch/x86_64/apic/local_apic.cpp deleted file mode 100644 index 14fcff9d2..000000000 --- a/src/arch/x86_64/apic/local_apic.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include - -#include "apic.h" -#include "kernel_log.hpp" - -auto LocalApic::Init() -> Expected { - // 检查 APIC 是否全局启用 - if (!cpu_io::msr::apic::IsGloballyEnabled()) { - cpu_io::msr::apic::EnableGlobally(); - } - - // 首先尝试启用 x2APIC 模式 - if (EnableX2Apic()) { - is_x2apic_mode_ = true; - } else { - if (!EnableXApic()) { - klog::Err("Failed to enable APIC in any mode"); - return std::unexpected(Error(ErrorCode::kApicInitFailed)); - } - is_x2apic_mode_ = false; - } - - // 启用 Local APIC(通过设置 SIVR) - uint32_t sivr; - if (is_x2apic_mode_) { - sivr = cpu_io::msr::apic::ReadSivr(); - } else { - etl::io_port_ro sivr_reg{ - reinterpret_cast(apic_base_ + kXApicSivrOffset)}; - sivr = sivr_reg.read(); - } - - // 设置 APIC Software Enable 位 - sivr |= kApicSoftwareEnableBit; - // 设置虚假中断向量为 0xFF - sivr |= kSpuriousVector; - - if (is_x2apic_mode_) { - cpu_io::msr::apic::WriteSivr(sivr); - } else { - etl::io_port_wo sivr_reg{ - reinterpret_cast(apic_base_ + kXApicSivrOffset)}; - sivr_reg.write(sivr); - } - - // 清除任务优先级 - SetTaskPriority(0); - - // 禁用所有 LVT 条目(设置 mask 位) - if (is_x2apic_mode_) { - cpu_io::msr::apic::WriteLvtTimer(kLvtMaskBit); - cpu_io::msr::apic::WriteLvtLint0(kLvtMaskBit); - cpu_io::msr::apic::WriteLvtLint1(kLvtMaskBit); - cpu_io::msr::apic::WriteLvtError(kLvtMaskBit); - } else { - // xAPIC 模式下通过内存映射写入 - etl::io_port_wo lvt_timer{ - reinterpret_cast(apic_base_ + kXApicLvtTimerOffset)}; - lvt_timer.write(kLvtMaskBit); - etl::io_port_wo lvt_lint0{ - reinterpret_cast(apic_base_ + kXApicLvtLint0Offset)}; - lvt_lint0.write(kLvtMaskBit); - etl::io_port_wo lvt_lint1{ - reinterpret_cast(apic_base_ + kXApicLvtLint1Offset)}; - lvt_lint1.write(kLvtMaskBit); - etl::io_port_wo lvt_error{ - reinterpret_cast(apic_base_ + kXApicLvtErrorOffset)}; - lvt_error.write(kLvtMaskBit); - } - return {}; -} - -auto LocalApic::GetApicVersion() const -> uint32_t { - if (is_x2apic_mode_) { - return cpu_io::msr::apic::ReadVersion(); - } else { - // 版本寄存器在偏移 0x30 处 - etl::io_port_ro ver_reg{ - reinterpret_cast(apic_base_ + kXApicVersionOffset)}; - return ver_reg.read(); - } -} - -auto LocalApic::SendEoi() const -> void { - if (is_x2apic_mode_) { - cpu_io::msr::apic::WriteEoi(0); - } else { - // 写入 EOI 寄存器(偏移 0xB0) - etl::io_port_wo eoi_reg{ - reinterpret_cast(apic_base_ + kXApicEoiOffset)}; - eoi_reg.write(0); - } -} - -auto LocalApic::SendIpi(uint32_t destination_apic_id, uint8_t vector) const - -> Expected { - if (is_x2apic_mode_) { - auto icr = static_cast(vector); - icr |= static_cast(destination_apic_id) << 32; - - cpu_io::msr::apic::WriteIcr(icr); - - while ((cpu_io::msr::apic::ReadIcr() & kIcrDeliveryStatusBit) != 0) { - ; - } - } else { - // ICR 分为两个 32 位寄存器:ICR_LOW (0x300) 和 ICR_HIGH (0x310) - - // 设置目标 APIC ID(ICR_HIGH 的位 24-31) - auto icr_high = (destination_apic_id & kApicIdMask) << kIcrDestShift; - etl::io_port_wo icr_high_reg{ - reinterpret_cast(apic_base_ + kXApicIcrHighOffset)}; - icr_high_reg.write(icr_high); - - // 设置向量和传递模式(ICR_LOW) - auto icr_low = static_cast(vector); - etl::io_port_wo icr_low_wo{ - reinterpret_cast(apic_base_ + kXApicIcrLowOffset)}; - icr_low_wo.write(icr_low); - - { - etl::io_port_ro icr_low_ro{ - reinterpret_cast(apic_base_ + kXApicIcrLowOffset)}; - while ((icr_low_ro.read() & kIcrDeliveryStatusBit) != 0) { - ; - } - } - } - return {}; -} - -auto LocalApic::BroadcastIpi(uint8_t vector) const -> Expected { - if (is_x2apic_mode_) { - auto icr = static_cast(vector); - // 目标简写:除自己外的所有 CPU - icr |= 0x80000; - - cpu_io::msr::apic::WriteIcr(icr); - - while ((cpu_io::msr::apic::ReadIcr() & kIcrDeliveryStatusBit) != 0) { - ; - } - } else { - // 广播到除自己外的所有 CPU(目标简写模式) - // ICR_HIGH 设为 0(不使用具体目标 ID) - etl::io_port_wo icr_high_reg{ - reinterpret_cast(apic_base_ + kXApicIcrHighOffset)}; - icr_high_reg.write(0); - - // ICR_LOW:向量 + 目标简写模式(位 18-19 = 11) - auto icr_low = static_cast(vector) | kIcrBroadcastMode; - etl::io_port_wo icr_low_wo{ - reinterpret_cast(apic_base_ + kXApicIcrLowOffset)}; - icr_low_wo.write(icr_low); - - { - etl::io_port_ro icr_low_ro{ - reinterpret_cast(apic_base_ + kXApicIcrLowOffset)}; - while ((icr_low_ro.read() & kIcrDeliveryStatusBit) != 0) { - ; - } - } - } - return {}; -} - -auto LocalApic::SetTaskPriority(uint8_t priority) const -> void { - if (is_x2apic_mode_) { - cpu_io::msr::apic::WriteTpr(static_cast(priority)); - } else { - etl::io_port_wo tpr_reg{ - reinterpret_cast(apic_base_ + kXApicTprOffset)}; - tpr_reg.write(static_cast(priority)); - } -} - -auto LocalApic::GetTaskPriority() const -> uint8_t { - if (is_x2apic_mode_) { - return static_cast(cpu_io::msr::apic::ReadTpr() & kApicIdMask); - } else { - etl::io_port_ro tpr_reg{ - reinterpret_cast(apic_base_ + kXApicTprOffset)}; - auto tpr = tpr_reg.read(); - return static_cast(tpr & kApicIdMask); - } -} - -auto LocalApic::EnableTimer(uint32_t initial_count, uint32_t divide_value, - uint8_t vector, bool periodic) const -> void { - if (is_x2apic_mode_) { - cpu_io::msr::apic::WriteTimerDivide(divide_value); - - auto lvt_timer = static_cast(vector); - if (periodic) { - lvt_timer |= kLvtPeriodicMode; - } - - cpu_io::msr::apic::WriteLvtTimer(lvt_timer); - cpu_io::msr::apic::WriteTimerInitCount(initial_count); - } else { - // 设置分频器(偏移 0x3E0) - etl::io_port_wo divide_reg{ - reinterpret_cast(apic_base_ + kXApicTimerDivideOffset)}; - divide_reg.write(divide_value); - - // 设置 LVT 定时器寄存器(偏移 0x320) - auto lvt_timer = static_cast(vector); - if (periodic) { - lvt_timer |= kLvtPeriodicMode; - } - - etl::io_port_wo lvt_timer_reg{ - reinterpret_cast(apic_base_ + kXApicLvtTimerOffset)}; - lvt_timer_reg.write(lvt_timer); - - // 设置初始计数值(偏移 0x380) - etl::io_port_wo init_count_reg{ - reinterpret_cast(apic_base_ + kXApicTimerInitCountOffset)}; - init_count_reg.write(initial_count); - } -} - -auto LocalApic::DisableTimer() const -> void { - if (is_x2apic_mode_) { - auto lvt_timer = cpu_io::msr::apic::ReadLvtTimer(); - lvt_timer |= kLvtMaskBit; - cpu_io::msr::apic::WriteLvtTimer(lvt_timer); - cpu_io::msr::apic::WriteTimerInitCount(0); - } else { - etl::io_port_rw lvt_timer_reg{ - reinterpret_cast(apic_base_ + kXApicLvtTimerOffset)}; - auto lvt_timer = lvt_timer_reg.read(); - lvt_timer |= kLvtMaskBit; - lvt_timer_reg.write(lvt_timer); - etl::io_port_wo init_count_reg{ - reinterpret_cast(apic_base_ + kXApicTimerInitCountOffset)}; - init_count_reg.write(0); - } -} - -auto LocalApic::GetTimerCurrentCount() const -> uint32_t { - if (is_x2apic_mode_) { - return cpu_io::msr::apic::ReadTimerCurrCount(); - } else { - // 当前计数寄存器在偏移 0x390 处 - etl::io_port_ro curr_count_reg{ - reinterpret_cast(apic_base_ + kXApicTimerCurrCountOffset)}; - return curr_count_reg.read(); - } -} - -auto LocalApic::SetupPeriodicTimer(uint32_t frequency_hz, uint8_t vector) const - -> void { - // 使用 APIC 定时器的典型配置 - // 假设 APIC 时钟频率为 100MHz(实际应从 CPU 频率计算) - - // 计算初始计数值 - auto initial_count = kDefaultApicClockHz / frequency_hz; - - // 选择合适的分频值以获得更好的精度 - auto divide_value = kTimerDivideBy1; - if (initial_count > 0xFFFFFFFF) { - // 如果计数值太大,使用分频 - divide_value = kTimerDivideBy16; - initial_count = (kDefaultApicClockHz / 16) / frequency_hz; - } - - EnableTimer(initial_count, divide_value, vector, true); -} - -auto LocalApic::SetupOneShotTimer(uint32_t microseconds, uint8_t vector) const - -> void { - // 假设 APIC 时钟频率为 100MHz - - // 计算初始计数值(微秒转换为时钟周期) - auto initial_count = - (kDefaultApicClockHz / kMicrosecondsPerSecond) * microseconds; - - // 选择合适的分频值 - auto divide_value = kTimerDivideBy1; - if (initial_count > 0xFFFFFFFF) { - divide_value = kTimerDivideBy16; - initial_count = - ((kDefaultApicClockHz / 16) / kMicrosecondsPerSecond) * microseconds; - } - - EnableTimer(initial_count, divide_value, vector, false); -} - -auto LocalApic::SendInitIpi(uint32_t destination_apic_id) const -> void { - if (is_x2apic_mode_) { - auto icr = kInitIpiMode; - icr |= static_cast(destination_apic_id) << 32; - - cpu_io::msr::apic::WriteIcr(icr); - - while ((cpu_io::msr::apic::ReadIcr() & kIcrDeliveryStatusBit) != 0) { - ; - } - } else { - // 设置目标 APIC ID(ICR_HIGH) - auto icr_high = (destination_apic_id & kApicIdMask) << kIcrDestShift; - etl::io_port_wo icr_high_reg{ - reinterpret_cast(apic_base_ + kXApicIcrHighOffset)}; - icr_high_reg.write(icr_high); - - // 发送 INIT IPI(ICR_LOW) - auto icr_low = kInitIpiMode; - etl::io_port_wo icr_low_wo{ - reinterpret_cast(apic_base_ + kXApicIcrLowOffset)}; - icr_low_wo.write(icr_low); - - { - etl::io_port_ro icr_low_ro{ - reinterpret_cast(apic_base_ + kXApicIcrLowOffset)}; - while ((icr_low_ro.read() & kIcrDeliveryStatusBit) != 0) { - ; - } - } - } - - klog::Info("INIT IPI sent to APIC ID {:#x}", destination_apic_id); -} - -auto LocalApic::SendStartupIpi(uint32_t destination_apic_id, - uint8_t start_page) const -> void { - if (is_x2apic_mode_) { - // SIPI with start page (delivery mode = 110b) - auto icr = kSipiMode | start_page; - icr |= static_cast(destination_apic_id) << 32; - - cpu_io::msr::apic::WriteIcr(icr); - - while ((cpu_io::msr::apic::ReadIcr() & kIcrDeliveryStatusBit) != 0) { - ; - } - } else { - // 设置目标 APIC ID(ICR_HIGH) - uint32_t icr_high = (destination_apic_id & kApicIdMask) << kIcrDestShift; - etl::io_port_wo icr_high_reg{ - reinterpret_cast(apic_base_ + kXApicIcrHighOffset)}; - icr_high_reg.write(icr_high); - - // 发送 SIPI(ICR_LOW) - uint32_t icr_low = kSipiMode | start_page; - etl::io_port_wo icr_low_wo{ - reinterpret_cast(apic_base_ + kXApicIcrLowOffset)}; - icr_low_wo.write(icr_low); - - { - etl::io_port_ro icr_low_ro{ - reinterpret_cast(apic_base_ + kXApicIcrLowOffset)}; - while ((icr_low_ro.read() & kIcrDeliveryStatusBit) != 0) { - ; - } - } - } -} - -auto LocalApic::ConfigureLvtEntries() const -> void { - if (is_x2apic_mode_) { - // 配置 LINT0 (通常连接到 8259 PIC 的 INTR) - cpu_io::msr::apic::WriteLvtLint0(kExtIntMode); - - // 配置 LINT1 (通常连接到 NMI) - cpu_io::msr::apic::WriteLvtLint1(kNmiMode); - - // 配置错误中断 - cpu_io::msr::apic::WriteLvtError(kErrorVector); - } else { - // 配置 LINT0 (偏移 0x350) - etl::io_port_wo lint0_reg{ - reinterpret_cast(apic_base_ + kXApicLvtLint0Offset)}; - lint0_reg.write(kExtIntMode); - - // 配置 LINT1 (偏移 0x360) - etl::io_port_wo lint1_reg{ - reinterpret_cast(apic_base_ + kXApicLvtLint1Offset)}; - lint1_reg.write(kNmiMode); - - // 配置错误中断 (偏移 0x370) - etl::io_port_wo error_reg{ - reinterpret_cast(apic_base_ + kXApicLvtErrorOffset)}; - error_reg.write(kErrorVector); - } -} - -auto LocalApic::ReadErrorStatus() const -> uint32_t { - if (is_x2apic_mode_) { - // x2APIC 模式下没有直接的 ESR 访问方式 - // 这里返回 0 表示没有错误 - return 0; - } else { - // 取 ESR (Error Status Register, 偏移 0x280) - // 读取 ESR 之前需要先写入 0 - etl::io_port_rw esr_reg{ - reinterpret_cast(apic_base_ + kXApicEsrOffset)}; - esr_reg.write(0); - return esr_reg.read(); - } -} - -auto LocalApic::PrintInfo() const -> void { - klog::Info("APIC Version: {:#x}", GetApicVersion()); - klog::Info("Mode: {}", is_x2apic_mode_ ? "x2APIC" : "xAPIC"); - klog::Info("x2APIC Enabled: {}", IsX2ApicEnabled() ? "Yes" : "No"); - klog::Info("Task Priority: {:#x}", GetTaskPriority()); - klog::Info("Timer Current Count: {}", GetTimerCurrentCount()); - - // 读取各种寄存器状态 - if (is_x2apic_mode_) { - uint32_t sivr = cpu_io::msr::apic::ReadSivr(); - klog::Info("SIVR: {:#x} (APIC {})", sivr, - (sivr & kApicSoftwareEnableBit) ? "Enabled" : "Disabled"); - - uint32_t lvt_timer = cpu_io::msr::apic::ReadLvtTimer(); - klog::Info("LVT Timer: {:#x}", lvt_timer); - - uint32_t lvt_lint0 = cpu_io::msr::apic::ReadLvtLint0(); - klog::Info("LVT LINT0: {:#x}", lvt_lint0); - - uint32_t lvt_lint1 = cpu_io::msr::apic::ReadLvtLint1(); - klog::Info("LVT LINT1: {:#x}", lvt_lint1); - - uint32_t lvt_error = cpu_io::msr::apic::ReadLvtError(); - klog::Info("LVT Error: {:#x}", lvt_error); - } else { - etl::io_port_ro sivr_reg{ - reinterpret_cast(apic_base_ + kXApicSivrOffset)}; - uint32_t sivr = sivr_reg.read(); - klog::Info("SIVR: {:#x} (APIC {})", sivr, - (sivr & kApicSoftwareEnableBit) ? "Enabled" : "Disabled"); - - etl::io_port_ro lvt_timer_reg{ - reinterpret_cast(apic_base_ + kXApicLvtTimerOffset)}; - uint32_t lvt_timer = lvt_timer_reg.read(); - klog::Info("LVT Timer: {:#x}", lvt_timer); - - etl::io_port_ro lvt_lint0_reg{ - reinterpret_cast(apic_base_ + kXApicLvtLint0Offset)}; - uint32_t lvt_lint0 = lvt_lint0_reg.read(); - klog::Info("LVT LINT0: {:#x}", lvt_lint0); - - etl::io_port_ro lvt_lint1_reg{ - reinterpret_cast(apic_base_ + kXApicLvtLint1Offset)}; - uint32_t lvt_lint1 = lvt_lint1_reg.read(); - klog::Info("LVT LINT1: {:#x}", lvt_lint1); - - etl::io_port_ro lvt_error_reg{ - reinterpret_cast(apic_base_ + kXApicLvtErrorOffset)}; - uint32_t lvt_error = lvt_error_reg.read(); - klog::Info("LVT Error: {:#x}", lvt_error); - - klog::Info("APIC Base Address: {:#x}", apic_base_); - } -} - -auto LocalApic::CheckX2ApicSupport() const -> bool { - return cpu_io::cpuid::HasX2Apic(); -} - -auto LocalApic::EnableXApic() const -> bool { - // 设置 IA32_APIC_BASE.Global_Enable (位11) = 1 - cpu_io::msr::apic::EnableGlobally(); - // 清除 IA32_APIC_BASE.x2APIC_Enable (位10) = 0 - cpu_io::msr::apic::DisableX2Apic(); - return IsXApicEnabled(); -} - -auto LocalApic::DisableXApic() const -> bool { - // 清除 IA32_APIC_BASE.Global_Enable (位11) = 0 - cpu_io::msr::apic::DisableGlobally(); - return !IsXApicEnabled(); -} - -auto LocalApic::IsXApicEnabled() const -> bool { - // Global_Enable = 1 && x2APIC_Enable = 0 - return cpu_io::msr::apic::IsGloballyEnabled() && - !cpu_io::msr::apic::IsX2ApicEnabled(); -} - -auto LocalApic::EnableX2Apic() const -> bool { - // 检查 CPU 是否支持 x2APIC - if (!CheckX2ApicSupport()) { - return false; - } - - // 设置 IA32_APIC_BASE.x2APIC_Enable (位10) = 1 - // 同时确保 IA32_APIC_BASE.Global_Enable (位11) = 1 - cpu_io::msr::apic::EnableX2Apic(); - - // 验证 x2APIC 是否成功启用 - return IsX2ApicEnabled(); -} - -auto LocalApic::DisableX2Apic() const -> bool { - // 清除 IA32_APIC_BASE.x2APIC_Enable (位10) = 0 - cpu_io::msr::apic::DisableX2Apic(); - return !IsX2ApicEnabled(); -} - -auto LocalApic::IsX2ApicEnabled() const -> bool { - return cpu_io::msr::apic::IsX2ApicEnabled(); -} - -auto LocalApic::WakeupAp(uint32_t destination_apic_id, - uint8_t start_vector) const -> void { - // 发送 INIT IPI - SendInitIpi(destination_apic_id); - - // 等待 10ms (INIT IPI 后的标准等待时间) - auto delay = 10 * kCalibrationDelayLoop; - while (delay--) { - __asm__ volatile("nop"); - } - - // 发送第一个 SIPI - SendStartupIpi(destination_apic_id, start_vector); - - // 等待 200μs (SIPI 后的标准等待时间) - delay = 200 * (kCalibrationDelayLoop / 1000); - while (delay--) { - __asm__ volatile("nop"); - } - - // 发送第二个 SIPI (为了可靠性) - SendStartupIpi(destination_apic_id, start_vector); - - // 等待 200μs - delay = 200 * (kCalibrationDelayLoop / 1000); - while (delay--) { - __asm__ volatile("nop"); - } -} diff --git a/src/arch/x86_64/arch_main.cpp b/src/arch/x86_64/arch_main.cpp deleted file mode 100644 index 5b12672b5..000000000 --- a/src/arch/x86_64/arch_main.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include - -#include -#include -#include - -#include "basic_info.hpp" -#include "interrupt.h" -#include "kernel.h" -#include "kernel_elf.hpp" -#include "kernel_log.hpp" -#include "per_cpu.hpp" -#include "sipi.h" - -namespace { - -/// gdt 描述符表,顺序与 cpu_io::GdtrInfo 中的定义一致 -std::array - kSegmentDescriptors = { - // 第一个全 0 - cpu_io::GdtrInfo::SegmentDescriptor(), - // 内核代码段描述符 - cpu_io::GdtrInfo::SegmentDescriptor( - cpu_io::GdtrInfo::SegmentDescriptor::Type::kCodeExecuteRead, - cpu_io::GdtrInfo::SegmentDescriptor::S::kCodeData, - cpu_io::GdtrInfo::SegmentDescriptor::DPL::kRing0, - cpu_io::GdtrInfo::SegmentDescriptor::P::kPresent, - cpu_io::GdtrInfo::SegmentDescriptor::AVL::kNotAvailable, - cpu_io::GdtrInfo::SegmentDescriptor::L::k64Bit), - // 内核数据段描述符 - cpu_io::GdtrInfo::SegmentDescriptor( - cpu_io::GdtrInfo::SegmentDescriptor::Type::kDataReadWrite, - cpu_io::GdtrInfo::SegmentDescriptor::S::kCodeData, - cpu_io::GdtrInfo::SegmentDescriptor::DPL::kRing0, - cpu_io::GdtrInfo::SegmentDescriptor::P::kPresent, - cpu_io::GdtrInfo::SegmentDescriptor::AVL::kNotAvailable, - cpu_io::GdtrInfo::SegmentDescriptor::L::k64Bit), - // 用户代码段描述符 - cpu_io::GdtrInfo::SegmentDescriptor( - cpu_io::GdtrInfo::SegmentDescriptor::Type::kCodeExecuteRead, - cpu_io::GdtrInfo::SegmentDescriptor::S::kCodeData, - cpu_io::GdtrInfo::SegmentDescriptor::DPL::kRing3, - cpu_io::GdtrInfo::SegmentDescriptor::P::kPresent, - cpu_io::GdtrInfo::SegmentDescriptor::AVL::kNotAvailable, - cpu_io::GdtrInfo::SegmentDescriptor::L::k64Bit), - // 用户数据段描述符 - cpu_io::GdtrInfo::SegmentDescriptor( - cpu_io::GdtrInfo::SegmentDescriptor::Type::kDataReadWrite, - cpu_io::GdtrInfo::SegmentDescriptor::S::kCodeData, - cpu_io::GdtrInfo::SegmentDescriptor::DPL::kRing3, - cpu_io::GdtrInfo::SegmentDescriptor::P::kPresent, - cpu_io::GdtrInfo::SegmentDescriptor::AVL::kNotAvailable, - cpu_io::GdtrInfo::SegmentDescriptor::L::k64Bit), -}; - -cpu_io::GdtrInfo::Gdtr gdtr{ - .limit = (sizeof(cpu_io::GdtrInfo::SegmentDescriptor) * - cpu_io::GdtrInfo::kMaxCount) - - 1, - .base = kSegmentDescriptors.data(), -}; - -/// 设置 GDT 和段寄存器 -auto SetupGdtAndSegmentRegisters() -> void { - // 设置 gdt - cpu_io::Gdtr::Write(gdtr); - - // 加载内核数据段描述符 - cpu_io::Ds::Write(sizeof(cpu_io::GdtrInfo::SegmentDescriptor) * - cpu_io::GdtrInfo::kKernelDataIndex); - cpu_io::Es::Write(sizeof(cpu_io::GdtrInfo::SegmentDescriptor) * - cpu_io::GdtrInfo::kKernelDataIndex); - cpu_io::Fs::Write(sizeof(cpu_io::GdtrInfo::SegmentDescriptor) * - cpu_io::GdtrInfo::kKernelDataIndex); - cpu_io::Gs::Write(sizeof(cpu_io::GdtrInfo::SegmentDescriptor) * - cpu_io::GdtrInfo::kKernelDataIndex); - cpu_io::Ss::Write(sizeof(cpu_io::GdtrInfo::SegmentDescriptor) * - cpu_io::GdtrInfo::kKernelDataIndex); - // 加载内核代码段描述符 - cpu_io::Cs::Write(sizeof(cpu_io::GdtrInfo::SegmentDescriptor) * - cpu_io::GdtrInfo::kKernelCodeIndex); -} - -} // namespace - -BasicInfo::BasicInfo(int, const char**) { - physical_memory_addr = 0; - physical_memory_size = 0; - - kernel_addr = reinterpret_cast(__executable_start); - kernel_size = reinterpret_cast(end) - - reinterpret_cast(__executable_start); - - elf_addr = kernel_addr; - - fdt_addr = 0; - - core_count = cpu_io::cpuid::GetLogicalProcessorCount(); -} - -auto ArchInit(int, const char**) -> void { - BasicInfoSingleton::create(0, nullptr); - - // 解析内核 elf 信息 - KernelElfSingleton::create(BasicInfoSingleton::instance().elf_addr); - - // 设置 GDT 和段寄存器 - SetupGdtAndSegmentRegisters(); - - klog::Info("Hello x86_64 ArchInit"); -} - -auto ArchInitSMP(int, const char**) -> void { - // 设置 GDT 和段寄存器 - SetupGdtAndSegmentRegisters(); - - InterruptSingleton::instance().apic().InitCurrentCpuLocalApic().or_else( - [](Error err) -> Expected { - klog::Err("Failed to initialize APIC for AP: {}", err.message()); - while (true) { - cpu_io::Pause(); - } - return std::unexpected(err); - }); -} - -auto WakeUpOtherCores() -> void { - // 填充 sipi_params 结构体 - auto target_sipi_params = reinterpret_cast(sipi_params); - target_sipi_params->cr3 = cpu_io::Cr3::Read(); - - InterruptSingleton::instance().apic().StartupAllAps( - reinterpret_cast(ap_start16), - reinterpret_cast(ap_start64_end) - - reinterpret_cast(ap_start16), - kDefaultAPBase); -} - -auto InitTaskContext(cpu_io::CalleeSavedContext* task_context, - void (*entry)(void*), void* arg, uint64_t stack_top) - -> void { - // 清零上下文 - std::memset(task_context, 0, sizeof(cpu_io::CalleeSavedContext)); - - /// @todo x86_64 实现待补充 - (void)task_context; - (void)entry; - (void)arg; - (void)stack_top; -} - -auto InitTaskContext(cpu_io::CalleeSavedContext* task_context, - cpu_io::TrapContext* trap_context_ptr, uint64_t stack_top) - -> void { - // 清零上下文 - std::memset(task_context, 0, sizeof(cpu_io::CalleeSavedContext)); - - /// @todo x86_64 实现待补充 - (void)task_context; - (void)trap_context_ptr; - (void)stack_top; -} diff --git a/src/arch/x86_64/backtrace.cpp b/src/arch/x86_64/backtrace.cpp deleted file mode 100644 index b79576157..000000000 --- a/src/arch/x86_64/backtrace.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include -#include - -#include -#include -#include - -#include "arch.h" -#include "basic_info.hpp" -#include "kernel.h" -#include "kernel_elf.hpp" -#include "kernel_log.hpp" - -auto backtrace(std::array& buffer) -> int { - auto* rbp = reinterpret_cast(cpu_io::Rbp::Read()); - size_t count = 0; - while ((rbp != nullptr) && (*rbp != 0U) && count < buffer.max_size()) { - auto rip = *(rbp + 1); - if (rip < reinterpret_cast(__executable_start) || - rip > reinterpret_cast(__etext)) { - break; - } - rbp = reinterpret_cast(*rbp); - buffer[count++] = rip; - } - - return static_cast(count); -} - -auto DumpStack() -> void { - std::array buffer{}; - - // 获取调用栈中的地址 - auto num_frames = backtrace(buffer); - - for (auto current_frame_idx = 0; current_frame_idx < num_frames; - current_frame_idx++) { - // 打印函数名 - for (auto symtab : KernelElfSingleton::instance().symtab) { - if ((ELF64_ST_TYPE(symtab.st_info) == STT_FUNC) && - (buffer[current_frame_idx] >= symtab.st_value) && - (buffer[current_frame_idx] <= symtab.st_value + symtab.st_size)) { - klog::Err("[{}] {:#x}", - reinterpret_cast( - KernelElfSingleton::instance().strtab + symtab.st_name), - buffer[current_frame_idx]); - } - } - } -} diff --git a/src/arch/x86_64/boot.S b/src/arch/x86_64/boot.S deleted file mode 100644 index b1781b0e9..000000000 --- a/src/arch/x86_64/boot.S +++ /dev/null @@ -1,134 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -// CR4 中的 PAE 位 (位 5) -#define PAE_BIT 0x20 -// CR0 中的保护模式位 -#define PROTECTION_MODE_BIT 0x1 -// IA32_EFER 中的长模式位 -#define LONG_MODE_BIT 0x100 -// CR0 中的分页位 -#define PAGING_BIT 0x80000000 -// IA32_EFER MSR 地址 -#define IA32_EFER_MSR 0xC0000080 -// CPUID leaf 1 -#define CPUID_LEAF_1 1 -// APIC ID 在 EBX 中的位移 -#define APIC_ID_SHIFT 24 -// APIC ID 掩码 -#define APIC_ID_MASK 0xFF - -// 以下是多核启动入口 -.section .text -.global ap_start16 -.align 16 -.code16 -ap_start16: - cli - // 清空 tlb - xor %eax, %eax - mov %eax, %cr3 - - // 设置数据段寄存器 - mov %cs, %ax - mov %ax, %ds - - // 计算 gdt 地址并加载 - mov $gdtdesc, %ebx - sub $ap_start16, %ebx - data32 lgdt (%ebx) - - // 启用保护模式 - mov %cr0, %eax - // cr0 的第一位为保护模式位 - or $PROTECTION_MODE_BIT,%eax - mov %eax, %cr0 - - // 跳转到保护模式 - jmpl $8, $(ap_start32) - -.code32 -ap_start32: - // 启用 PAE (Physical Address Extension) - mov %cr4, %eax - // 设置 PAE 位 (位 5) - or $PAE_BIT, %eax - mov %eax, %cr4 - - // 设置页表 - 使用 sipi_params 中的 cr3 值 - // 计算 sipi_params.cr3 的相对地址 - mov $cr3, %ebx - sub $ap_start16, %ebx - mov (%ebx), %eax - mov %eax, %cr3 - - // 启用长模式 (设置 IA32_EFER.LME) - mov $IA32_EFER_MSR, %ecx - rdmsr - or $LONG_MODE_BIT, %eax - wrmsr - - // 启用分页以激活长模式 - mov %cr0, %eax - or $PAGING_BIT, %eax - mov %eax, %cr0 - - // 现在处于兼容模式,需要跳转到64位代码段 - ljmp $0x10, $_boot - -.code64 -.global _boot -.type _boot, @function -.extern _start -_boot: - // 关中断 - cli - - // 读取 CPU ID (APIC ID) 使用 CPUID - mov $CPUID_LEAF_1, %eax - cpuid - // EBX bits 31-24 是初始 APIC ID - shr $APIC_ID_SHIFT, %ebx - // 保留低 8 位 - and $APIC_ID_MASK, %ebx - - // 现在 EBX 包含 CPU ID (APIC ID) - // 根据 CPU ID 计算栈地址: stack_top + CPU_ID * 4KB - mov %ebx, %eax - mov $SIMPLEKERNEL_DEFAULT_STACK_SIZE, %edx - mul %edx - - // 计算该 CPU 的栈顶地址 - mov $stack_top, %rsp - add %rax, %rsp - add $SIMPLEKERNEL_DEFAULT_STACK_SIZE, %rsp - - call _start - hlt - ret - -// 临时使用的 GDT 描述符 -gdt: - // 空描述符 (0x00) - .quad 0x0000000000000000 - // 32位统一段 (0x08) - 代码和数据共用 - .quad 0x00CF9A000000FFFF - // 64位统一段 (0x10) - 代码和数据共用 - .quad 0x00AF9A000000FFFF - -gdtdesc: - .word (gdtdesc - gdt - 1) - .long gdt - -.globl sipi_params -sipi_params: -cr3: - .long 0 - -.globl ap_start64_end -ap_start64_end: - -.section .bss -stack_top: - .space SIMPLEKERNEL_DEFAULT_STACK_SIZE * SIMPLEKERNEL_MAX_CORE_COUNT diff --git a/src/arch/x86_64/early_console.cpp b/src/arch/x86_64/early_console.cpp deleted file mode 100644 index 63cc6c8a1..000000000 --- a/src/arch/x86_64/early_console.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include -#include - -using SerialSingleton = etl::singleton; - -namespace { - -cpu_io::Serial* serial = nullptr; - -/// 早期控制台初始化结构体 -struct EarlyConsole { - EarlyConsole() { - SerialSingleton::create(cpu_io::kCom1); - serial = &SerialSingleton::instance(); - } -}; - -EarlyConsole early_console; - -} // namespace - -extern "C" auto etl_putchar(int c) -> void { - if (serial) { - serial->Write(c); - } -} diff --git a/src/arch/x86_64/include/interrupt.h b/src/arch/x86_64/include/interrupt.h deleted file mode 100644 index 6e941b601..000000000 --- a/src/arch/x86_64/include/interrupt.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#pragma once - -#include -#include - -#include -#include - -#include "apic.h" -#include "interrupt_base.h" -#include "sk_stdio.h" - -class Interrupt final : public InterruptBase { - public: - /// 外部中断向量基址(IO APIC IRQ 到 IDT 向量的映射) - static constexpr uint8_t kExternalVectorBase = 0x20; - - auto Do(uint64_t cause, cpu_io::TrapContext* context) -> void override; - - auto RegisterInterruptFunc(uint64_t cause, InterruptDelegate func) - -> void override; - - [[nodiscard]] auto SendIpi(uint64_t target_cpu_mask) - -> Expected override; - - [[nodiscard]] auto BroadcastIpi() -> Expected override; - - [[nodiscard]] auto RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, - uint32_t priority, - InterruptDelegate handler) - -> Expected override; - - /// @name APIC 访问接口 - /// @{ - [[nodiscard]] __always_inline auto apic() -> Apic& { return apic_; } - [[nodiscard]] __always_inline auto apic() const -> const Apic& { - return apic_; - } - /// @} - - /** - * @brief 初始化 APIC - * @param cpu_count CPU 核心数 - */ - auto InitApic(size_t cpu_count) -> void; - - /** - * @brief 初始化 idtr - */ - auto SetUpIdtr() -> void; - - /// @name 构造/析构函数 - /// @{ - Interrupt(); - Interrupt(const Interrupt&) = delete; - Interrupt(Interrupt&&) = delete; - auto operator=(const Interrupt&) -> Interrupt& = delete; - auto operator=(Interrupt&&) -> Interrupt& = delete; - ~Interrupt() override = default; - /// @} - - private: - /// 中断处理函数数组 - alignas(4096) - std::array interrupt_handlers_{}; - - alignas(4096) std::array idts_{}; - - /// APIC 中断控制器实例 - Apic apic_{}; - - /** - * @brief 初始化 idtr - * @note 注意模板展开时的栈溢出 - */ - template - auto SetUpIdtr() -> void; -}; - -using InterruptSingleton = etl::singleton; diff --git a/src/arch/x86_64/include/sipi.h b/src/arch/x86_64/include/sipi.h deleted file mode 100644 index d4b55a3ae..000000000 --- a/src/arch/x86_64/include/sipi.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#pragma once - -#include - -#include -#include - -/// 启动 APs 的默认地址 -inline constexpr uint64_t kDefaultAPBase = 0x30000; - -extern "C" void* ap_start16[]; -extern "C" void* ap_start64_end[]; -extern "C" void* sipi_params[]; - -/** - * @brief SIPI 参数结构体 - */ -struct [[gnu::packed]] SipiParams { - uint32_t cr3; -}; diff --git a/src/arch/x86_64/interrupt.S b/src/arch/x86_64/interrupt.S deleted file mode 100644 index c7158f3cb..000000000 --- a/src/arch/x86_64/interrupt.S +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "macro.S" - -.section .text - -.global trap_return -.type trap_return, @function -.align 4 - -trap_return: diff --git a/src/arch/x86_64/interrupt.cpp b/src/arch/x86_64/interrupt.cpp deleted file mode 100644 index 3c8ced46b..000000000 --- a/src/arch/x86_64/interrupt.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "interrupt.h" - -#include - -#include "arch.h" -#include "kernel.h" -#include "kernel_log.hpp" -#include "kstd_cstdio" - -namespace { -/** - * @brief 中断处理函数 - * @tparam no 中断号 - * @param interrupt_context 中断上下文,根据中断不同可能是 InterruptContext 或 - * InterruptContextErrorCode - */ -template -__attribute__((target("general-regs-only"))) __attribute__((interrupt)) auto -TarpEntry(cpu_io::TrapContext* interrupt_context) -> void { - InterruptSingleton::instance().Do(no, interrupt_context); -} - -auto DefaultInterruptHandler(uint64_t cause, cpu_io::TrapContext* context) - -> uint64_t { - klog::Info("Default Interrupt handler [{}] {:#X}, {:#x}", - cpu_io::IdtrInfo::kInterruptNames[cause], cause, - reinterpret_cast(context)); - while (true) { - ; - } -} -} // namespace - -Interrupt::Interrupt() { - // 注册默认中断处理函数 - for (auto& i : interrupt_handlers_) { - i = InterruptDelegate::create(); - } - - klog::Info("Interrupt init."); -} - -auto Interrupt::Do(uint64_t cause, cpu_io::TrapContext* context) -> void { - if (cause < cpu_io::IdtrInfo::kInterruptMaxCount) { - interrupt_handlers_[cause](cause, context); - } -} - -auto Interrupt::RegisterInterruptFunc(uint64_t cause, InterruptDelegate func) - -> void { - if (cause < cpu_io::IdtrInfo::kInterruptMaxCount) { - interrupt_handlers_[cause] = func; - klog::Debug("RegisterInterruptFunc [{}] {:#X}", - cpu_io::IdtrInfo::kInterruptNames[cause], cause); - } -} - -auto Interrupt::SetUpIdtr() -> void { SetUpIdtr<0>(); } - -template -auto Interrupt::SetUpIdtr() -> void { - if constexpr (no < cpu_io::IdtrInfo::kInterruptMaxCount - 1) { - idts_[no] = cpu_io::IdtrInfo::Idt( - reinterpret_cast(TarpEntry), 8, 0x0, - cpu_io::IdtrInfo::Idt::Type::k64BitInterruptGate, - cpu_io::IdtrInfo::Idt::DPL::kRing0, cpu_io::IdtrInfo::Idt::P::kPresent); - SetUpIdtr(); - } else { - // 写入 idtr - static auto idtr = cpu_io::IdtrInfo::Idtr{ - .limit = sizeof(cpu_io::IdtrInfo::Idt) * - cpu_io::IdtrInfo::kInterruptMaxCount - - 1, - .base = idts_.data(), - }; - cpu_io::Idtr::Write(idtr); - - // 输出 idtr 信息 - for (size_t i = 0; - i < (cpu_io::Idtr::Read().limit + 1) / sizeof(cpu_io::IdtrInfo::Idtr); - i++) { - klog::Debug("idtr[{}] {:#x}", i, - reinterpret_cast(cpu_io::Idtr::Read().base + i)); - } - } -} - -auto Interrupt::SendIpi(uint64_t target_cpu_mask) -> Expected { - /// @todo - return std::unexpected(Error(ErrorCode::kIpiSendFailed)); -} - -auto Interrupt::BroadcastIpi() -> Expected { - /// @todo - return std::unexpected(Error(ErrorCode::kIpiSendFailed)); -} - -auto Interrupt::RegisterExternalInterrupt(uint32_t irq, uint32_t cpu_id, - uint32_t priority, - InterruptDelegate handler) - -> Expected { - // 计算 IDT 向量号:kExternalVectorBase + irq - auto vector = static_cast(kExternalVectorBase + irq); - if (vector >= cpu_io::IdtrInfo::kInterruptMaxCount) { - return std::unexpected(Error(ErrorCode::kApicInvalidIrq)); - } - - // 先注册处理函数 - RegisterInterruptFunc(vector, handler); - - // 再在 IO APIC 上启用 IRQ 重定向到指定核心 - // 注: x86 APIC 优先级由向量号隐含决定,priority 参数不直接使用 - auto result = apic_.SetIrqRedirection( - static_cast(irq), static_cast(vector), cpu_id, false); - if (!result.has_value()) { - return std::unexpected(result.error()); - } - - klog::Info("RegisterExternalInterrupt: IRQ {} -> vector {:#X}, cpu {}", irq, - vector, cpu_id); - return {}; -} - -auto Interrupt::InitApic(size_t cpu_count) -> void { apic_ = Apic(cpu_count); } diff --git a/src/arch/x86_64/interrupt_main.cpp b/src/arch/x86_64/interrupt_main.cpp deleted file mode 100644 index a46ba3426..000000000 --- a/src/arch/x86_64/interrupt_main.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include - -#include "arch.h" -#include "basic_info.hpp" -#include "interrupt.h" -#include "kernel.h" -#include "kernel_log.hpp" -#include "kstd_cstdio" - -namespace { -using InterruptDelegate = InterruptBase::InterruptDelegate; - -// 定义 APIC 时钟中断向量号(使用高优先级向量) -static constexpr uint8_t kApicTimerVector{0xF0}; -static constexpr uint32_t kApicTimerFrequencyHz{100}; - -/** - * @brief APIC 时钟中断处理函数 - * @param cause 中断原因 - * @param context 中断上下文 - * @return uint64_t 返回值 - */ -auto ApicTimerHandler(uint64_t cause, cpu_io::TrapContext* context) - -> uint64_t { - // APIC 时钟中断处理 - static uint64_t tick_count = 0; - tick_count++; - - // 每100次中断打印一次信息(减少日志输出) - if (tick_count % 100 == 0) { - klog::Info("APIC Timer interrupt {}, vector {:#x}", tick_count, - static_cast(cause)); - } - - // 发送 EOI 信号给 Local APIC - InterruptSingleton::instance().apic().SendEoi(); - return 0; -} - -/** - * @brief 键盘中断处理函数 - * @param cause 中断原因 - * @param context 中断上下文 - * @return uint64_t 返回值 - */ -auto KeyboardHandler(uint64_t cause, cpu_io::TrapContext* context) -> uint64_t { - klog::Info("Keyboard interrupt received, vector {:#x}", - static_cast(cause)); - // 读取键盘扫描码 - // 8042 键盘控制器的数据端口是 0x60 - uint8_t scancode = cpu_io::In(0x60); - - // 简单的扫描码处理 - 仅显示按下的键(忽略释放事件) - if (!(scancode & 0x80)) { // 最高位为0表示按下 - klog::Info("Key pressed: scancode {:#x}", scancode); - - // 简单的扫描码到ASCII的映射(仅作为示例) - static constexpr char scancode_to_ascii[] = { - 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', - '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', - 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', - 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' '}; - - if (scancode < sizeof(scancode_to_ascii) && scancode_to_ascii[scancode]) { - char ascii_char = scancode_to_ascii[scancode]; - klog::Info("Key: '{}'", ascii_char); - } - } - - // 发送 EOI 信号给 Local APIC - InterruptSingleton::instance().apic().SendEoi(); - return 0; -} - -} // namespace - -auto InterruptInit(int, const char**) -> void { - InterruptSingleton::create(); - - // 初始化 APIC(从 ArchInit 移至此处) - InterruptSingleton::instance().InitApic( - BasicInfoSingleton::instance().core_count); - InterruptSingleton::instance().apic().InitCurrentCpuLocalApic().or_else( - [](Error err) -> Expected { - klog::Err("Failed to initialize APIC: {}", err.message()); - while (true) { - cpu_io::Pause(); - } - return std::unexpected(err); - }); - - InterruptSingleton::instance().SetUpIdtr(); - - // 注册 APIC Timer 中断处理函数(Local APIC 内部中断,不走 IO APIC) - InterruptSingleton::instance().RegisterInterruptFunc( - kApicTimerVector, InterruptDelegate::create()); - - // 通过统一接口注册键盘外部中断(IRQ 1 = PS/2 键盘,先注册 handler 再启用 IO - // APIC) - static constexpr uint8_t kKeyboardIrq = 1; - InterruptSingleton::instance() - .RegisterExternalInterrupt(kKeyboardIrq, cpu_io::GetCurrentCoreId(), 0, - InterruptDelegate::create()) - .or_else([](Error err) -> Expected { - klog::Err("Failed to register keyboard IRQ: {}", err.message()); - return std::unexpected(err); - }); - - // 启用 Local APIC 定时器 - InterruptSingleton::instance().apic().SetupPeriodicTimer( - kApicTimerFrequencyHz, kApicTimerVector); - // 开启中断 - cpu_io::Rflags::If::Set(); - - klog::Info("Hello InterruptInit"); -} - -auto InterruptInitSMP(int, const char**) -> void { - InterruptSingleton::instance().SetUpIdtr(); - - // 初始化当前 AP 核的 Local APIC - InterruptSingleton::instance().apic().InitCurrentCpuLocalApic().or_else( - [](Error err) -> Expected { - klog::Err("Failed to initialize APIC for AP: {}", err.message()); - while (true) { - cpu_io::Pause(); - } - return std::unexpected(err); - }); - - // 启用 Local APIC 定时器 - InterruptSingleton::instance().apic().SetupPeriodicTimer( - kApicTimerFrequencyHz, kApicTimerVector); - cpu_io::Rflags::If::Set(); - klog::Info("Hello InterruptInit SMP"); -} diff --git a/src/arch/x86_64/link.ld b/src/arch/x86_64/link.ld deleted file mode 100644 index 947e9918c..000000000 --- a/src/arch/x86_64/link.ld +++ /dev/null @@ -1,253 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -/* GNU ld (GNU Binutils for Ubuntu) 2.41 */ -/* Script for -z combreloc -z separate-code */ -/* Copyright (C) 2014-2023 Free Software Foundation, Inc. - Copying and distribution of this script, with or without modification, - are permitted in any medium without royalty provided the copyright - notice and this notice are preserved. */ -OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", - "elf64-x86-64") -OUTPUT_ARCH(i386:x86-64) -ENTRY(_boot) -SECTIONS -{ - PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); - . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS; - .interp : ALIGN(0x1000) { *(.interp) } - .note.gnu.build-id : ALIGN(0x1000) { *(.note.gnu.build-id) } - .hash : ALIGN(0x1000) { *(.hash) } - .gnu.hash : ALIGN(0x1000) { *(.gnu.hash) } - .dynsym : ALIGN(0x1000) { *(.dynsym) } - .dynstr : ALIGN(0x1000) { *(.dynstr) } - .gnu.version : ALIGN(0x1000) { *(.gnu.version) } - .gnu.version_d : ALIGN(0x1000) { *(.gnu.version_d) } - .gnu.version_r : ALIGN(0x1000) { *(.gnu.version_r) } - .rela.dyn : ALIGN(0x1000) - { - *(.rela.init) - *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) - *(.rela.fini) - *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) - *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) - *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) - *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) - *(.rela.ctors) - *(.rela.dtors) - *(.rela.got) - *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) - *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) - *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) - *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) - *(.rela.ifunc) - } - .rela.plt : ALIGN(0x1000) - { - *(.rela.plt) - PROVIDE_HIDDEN (__rela_iplt_start = .); - *(.rela.iplt) - PROVIDE_HIDDEN (__rela_iplt_end = .); - } - .relr.dyn : ALIGN(0x1000) { *(.relr.dyn) } - . = ALIGN(CONSTANT (MAXPAGESIZE)); - .init : ALIGN(0x1000) - { - KEEP (*(SORT_NONE(.init))) - } - .plt : { *(.plt) *(.iplt) } - .plt.got : { *(.plt.got) } - .plt.sec : { *(.plt.sec) } - .text : ALIGN(0x1000) - { - *(.text.unlikely .text.*_unlikely .text.unlikely.*) - *(.text.exit .text.exit.*) - *(.text.startup .text.startup.*) - *(.text.hot .text.hot.*) - *(SORT(.text.sorted.*)) - *(.text .stub .text.* .gnu.linkonce.t.*) - /* .gnu.warning sections are handled specially by elf.em. */ - *(.gnu.warning) - } - .fini : ALIGN(0x1000) - { - KEEP (*(SORT_NONE(.fini))) - } - PROVIDE (__etext = .); - PROVIDE (_etext = .); - PROVIDE (etext = .); - . = ALIGN(CONSTANT (MAXPAGESIZE)); - /* Adjust the address for the rodata segment. We want to adjust up to - the same address within the page on the next page up. */ - . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); - .rodata : ALIGN(0x1000) { *(.rodata .rodata.* .gnu.linkonce.r.*) } - .rodata1 : ALIGN(0x1000) { *(.rodata1) } - .eh_frame_hdr : ALIGN(0x1000) { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } - .eh_frame : ALIGN(0x1000) ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } - .sframe : ALIGN(0x1000) ONLY_IF_RO { *(.sframe) *(.sframe.*) } - .gcc_except_table : ALIGN(0x1000) ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } - .gnu_extab : ALIGN(0x1000) ONLY_IF_RO { *(.gnu_extab*) } - /* These sections are generated by the Sun/Oracle C++ compiler. */ - .exception_ranges : ALIGN(0x1000) ONLY_IF_RO { *(.exception_ranges*) } - /* Adjust the address for the data segment. We want to adjust up to - the same address within the page on the next page up. */ - . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); - /* Exception handling */ - .eh_frame : ALIGN(0x1000) ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } - .sframe : ALIGN(0x1000) ONLY_IF_RW { *(.sframe) *(.sframe.*) } - .gnu_extab : ALIGN(0x1000) ONLY_IF_RW { *(.gnu_extab) } - .gcc_except_table : ALIGN(0x1000) ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } - .exception_ranges : ALIGN(0x1000) ONLY_IF_RW { *(.exception_ranges*) } - /* Thread Local Storage sections */ - .tdata : ALIGN(0x1000) - { - PROVIDE_HIDDEN (__tdata_start = .); - *(.tdata .tdata.* .gnu.linkonce.td.*) - } - .tbss : ALIGN(0x1000) { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } - .preinit_array : ALIGN(0x1000) - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP (*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } - .init_array : ALIGN(0x1000) - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) - KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) - PROVIDE_HIDDEN (__init_array_end = .); - } - .fini_array : ALIGN(0x1000) - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) - KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) - PROVIDE_HIDDEN (__fini_array_end = .); - } - .ctors : ALIGN(0x1000) - { - /* gcc uses crtbegin.o to find the start of - the constructors, so we make sure it is - first. Because this is a wildcard, it - doesn't matter if the user does not - actually link against crtbegin.o; the - linker won't look for a file to match a - wildcard. The wildcard also means that it - doesn't matter which directory crtbegin.o - is in. */ - KEEP (*crtbegin.o(.ctors)) - KEEP (*crtbegin?.o(.ctors)) - /* We don't want to include the .ctor section from - the crtend.o file until after the sorted ctors. - The .ctor section from the crtend file contains the - end of ctors marker and it must be last */ - KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - } - .dtors : ALIGN(0x1000) - { - KEEP (*crtbegin.o(.dtors)) - KEEP (*crtbegin?.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) - } - .jcr : ALIGN(0x1000) { KEEP (*(.jcr)) } - .data.rel.ro : ALIGN(0x1000) { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } - .dynamic : ALIGN(0x1000) { *(.dynamic) } - .got : ALIGN(0x1000) { *(.got) *(.igot) } - . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); - .got.plt : ALIGN(0x1000) { *(.got.plt) *(.igot.plt) } - .data : ALIGN(0x1000) - { - *(.data .data.* .gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - } - .data1 : ALIGN(0x1000) { *(.data1) } - _edata = .; PROVIDE (edata = .); - . = .; - __bss_start = .; - .bss : ALIGN(0x1000) - { - *(.dynbss) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - /* Align here to ensure that the .bss section occupies space up to - _end. Align after .bss to ensure correct alignment even if the - .bss section disappears because there are no input sections. - FIXME: Why do we need it? When there is no .bss section, we do not - pad the .data section. */ - . = ALIGN(. != 0 ? 64 / 8 : 1); - } - .lbss : ALIGN(0x1000) - { - *(.dynlbss) - *(.lbss .lbss.* .gnu.linkonce.lb.*) - *(LARGE_COMMON) - } - . = ALIGN(64 / 8); - . = SEGMENT_START("ldata-segment", .); - .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : - { - *(.lrodata .lrodata.* .gnu.linkonce.lr.*) - } - .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : - { - *(.ldata .ldata.* .gnu.linkonce.l.*) - . = ALIGN(. != 0 ? 64 / 8 : 1); - } - . = ALIGN(64 / 8); - _end = .; PROVIDE (end = .); - . = DATA_SEGMENT_END (.); - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 (INFO) : { *(.comment); LINKER_VERSION; } - .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - /* DWARF 1. */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions. */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2. */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2. */ - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions. */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } - /* DWARF 3. */ - .debug_pubtypes 0 : { *(.debug_pubtypes) } - .debug_ranges 0 : { *(.debug_ranges) } - /* DWARF 5. */ - .debug_addr 0 : { *(.debug_addr) } - .debug_line_str 0 : { *(.debug_line_str) } - .debug_loclists 0 : { *(.debug_loclists) } - .debug_macro 0 : { *(.debug_macro) } - .debug_names 0 : { *(.debug_names) } - .debug_rnglists 0 : { *(.debug_rnglists) } - .debug_str_offsets 0 : { *(.debug_str_offsets) } - .debug_sup 0 : { *(.debug_sup) } - .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } - /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } -} diff --git a/src/arch/x86_64/macro.S b/src/arch/x86_64/macro.S deleted file mode 100644 index b41025ae7..000000000 --- a/src/arch/x86_64/macro.S +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -// clang-format off - -// clang-format on diff --git a/src/arch/x86_64/switch.S b/src/arch/x86_64/switch.S deleted file mode 100644 index d3bec8a82..000000000 --- a/src/arch/x86_64/switch.S +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "macro.S" - -.section .text - -.global switch_to -.type switch_to, @function - -switch_to: - -.global kernel_thread_entry -.type kernel_thread_entry, @function -kernel_thread_entry: diff --git a/src/arch/x86_64/syscall.cpp b/src/arch/x86_64/syscall.cpp deleted file mode 100644 index 1dba91aaa..000000000 --- a/src/arch/x86_64/syscall.cpp +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include "syscall.hpp" - -#include "interrupt.h" -#include "kernel_log.hpp" - -auto Syscall(uint64_t, cpu_io::TrapContext* context_ptr) -> void { - /// @todo -} diff --git a/src/arch/x86_64/timer.cpp b/src/arch/x86_64/timer.cpp deleted file mode 100644 index 2b12f10f2..000000000 --- a/src/arch/x86_64/timer.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @copyright Copyright The SimpleKernel Contributors - */ - -#include - -#include "arch.h" -#include "basic_info.hpp" -#include "interrupt.h" - -auto TimerInitSMP() -> void {} - -auto TimerInit() -> void {} diff --git a/src/filesystem/fatfs/fatfs.cpp b/src/filesystem/fatfs/fatfs.cpp index e933c5111..f6916900d 100644 --- a/src/filesystem/fatfs/fatfs.cpp +++ b/src/filesystem/fatfs/fatfs.cpp @@ -343,7 +343,7 @@ auto FatFsFileSystem::FatFsInodeOps::Create(vfs::Inode* dir, const char* name, if (type == vfs::FileType::kDirectory) { FRESULT fr = f_mkdir(full_path.data()); - if (fr != FR_OK) { + if (fr != FR_OK && fr != FR_EXIST) { return std::unexpected(Error{FresultToErrorCode(fr)}); } } else { @@ -393,6 +393,13 @@ auto FatFsFileSystem::FatFsInodeOps::Rmdir(vfs::Inode* dir, const char* name) return Unlink(dir, name); } +auto FatFsFileSystem::FatFsFileOps::Open(vfs::File* file) -> Expected { + if (file->inode->type == vfs::FileType::kDirectory) { + return {}; + } + return fs_->OpenFil(file->inode, file->flags); +} + auto FatFsFileSystem::FatFsFileOps::Read(vfs::File* file, void* buf, size_t count) -> Expected { auto* fi = static_cast(file->inode->fs_private); diff --git a/src/filesystem/fatfs/include/fatfs.hpp b/src/filesystem/fatfs/include/fatfs.hpp index 7182932a0..4c2a87a53 100644 --- a/src/filesystem/fatfs/include/fatfs.hpp +++ b/src/filesystem/fatfs/include/fatfs.hpp @@ -186,6 +186,13 @@ class FatFsFileSystem : public vfs::FileSystem { public: explicit FatFsFileOps(FatFsFileSystem* fs) : fs_(fs) {} + /** + * @brief 打开文件,准备底层 FIL 句柄 + * @param file 文件对象 + * @return Expected 成功或错误 + */ + auto Open(vfs::File* file) -> Expected override; + /** * @brief 从文件读取数据 * @param file 文件对象 diff --git a/src/filesystem/vfs/include/vfs_types.hpp b/src/filesystem/vfs/include/vfs_types.hpp index 14f0b4c3f..f531e4f46 100644 --- a/src/filesystem/vfs/include/vfs_types.hpp +++ b/src/filesystem/vfs/include/vfs_types.hpp @@ -47,44 +47,44 @@ enum class OpenFlags : uint32_t { kODirectory = 0x010000, }; -/// @brief 按位或 +/// 按位或 [[nodiscard]] inline constexpr auto operator|(OpenFlags lhs, OpenFlags rhs) -> OpenFlags { return static_cast(static_cast(lhs) | static_cast(rhs)); } -/// @brief 按位与 +/// 按位与 [[nodiscard]] inline constexpr auto operator&(OpenFlags lhs, OpenFlags rhs) -> OpenFlags { return static_cast(static_cast(lhs) & static_cast(rhs)); } -/// @brief 按位取反 +/// 按位取反 [[nodiscard]] inline constexpr auto operator~(OpenFlags flags) -> OpenFlags { return static_cast(~static_cast(flags)); } -/// @brief 按位或赋值 +/// 按位或赋值 inline constexpr auto operator|=(OpenFlags& lhs, OpenFlags rhs) -> OpenFlags& { lhs = lhs | rhs; return lhs; } -/// @brief 按位与赋值 +/// 按位与赋值 inline constexpr auto operator&=(OpenFlags& lhs, OpenFlags rhs) -> OpenFlags& { lhs = lhs & rhs; return lhs; } -/// @brief 检查 OpenFlags 是否为零(无标志位设置) +/// 检查 OpenFlags 是否为零(无标志位设置) [[nodiscard]] inline constexpr auto operator==(OpenFlags flags, uint32_t val) -> bool { return static_cast(flags) == val; } -/// @brief 检查 OpenFlags 是否不为零 +/// 检查 OpenFlags 是否不为零 [[nodiscard]] inline constexpr auto operator!=(OpenFlags flags, uint32_t val) -> bool { return static_cast(flags) != val; @@ -100,7 +100,7 @@ enum class SeekWhence : int { kEnd = 2, }; -/// @brief Inode 操作接口 +/// Inode 操作接口 class InodeOps { public: /** @@ -167,7 +167,8 @@ class InodeOps { virtual ~InodeOps() = default; /// @} }; -/// @brief 目录项结构(用于 readdir) + +/// 目录项结构(用于 readdir) struct DirEntry { /// inode 编号 uint64_t ino{0}; @@ -177,9 +178,18 @@ struct DirEntry { char name[256]{}; }; -/// @brief File 操作接口 +/// File 操作接口 class FileOps { public: + /** + * @brief 打开文件,准备底层 I/O 句柄 + * @param file 文件对象 + * @return Expected 成功或错误 + * @pre file != nullptr + * @note 默认实现为空操作,适用于不需要额外打开步骤的文件系统(如 ramfs) + */ + virtual auto Open(File* /*file*/) -> Expected { return {}; } + /** * @brief 从文件读取数据 * @param file 文件对象 diff --git a/src/filesystem/vfs/open.cpp b/src/filesystem/vfs/open.cpp index 3984ecb60..47634c6e6 100644 --- a/src/filesystem/vfs/open.cpp +++ b/src/filesystem/vfs/open.cpp @@ -127,6 +127,16 @@ auto Open(const char* path, OpenFlags flags) -> Expected { file->ops = file->inode->fs->GetFileOps(); } + // 让文件系统准备底层 I/O 句柄(如 FatFS 的 FIL 对象) + if (file->ops != nullptr) { + auto open_result = file->ops->Open(file); + if (!open_result.has_value()) { + dentry->ref_count--; + delete file; + return std::unexpected(open_result.error()); + } + } + // 处理 O_TRUNC if ((flags & OpenFlags::kOTruncate) != 0U && dentry->inode->type == FileType::kRegular) { diff --git a/src/include/expected.hpp b/src/include/expected.hpp index 1faa1f05b..14616d905 100644 --- a/src/include/expected.hpp +++ b/src/include/expected.hpp @@ -41,14 +41,6 @@ enum class ErrorCode : uint64_t { // IPI 相关错误 (0x500 - 0x5FF) kIpiTargetOutOfRange = 0x500, kIpiSendFailed = 0x501, - // APIC 相关错误 (0x600 - 0x6FF) - kApicInitFailed = 0x600, - kApicInvalidIrq = 0x601, - kApicInvalidParameter = 0x602, - kApicCodeCopyFailed = 0x603, - kApicAddressNotAligned = 0x604, - kApicAddressOutOfRange = 0x605, - kApicIpiTimeout = 0x606, // Task 相关错误 (0x700 - 0x7FF) kTaskNoCurrentTask = 0x700, kTaskPidAllocationFailed = 0x701, @@ -58,6 +50,12 @@ enum class ErrorCode : uint64_t { kTaskKernelStackAllocationFailed = 0x705, kTaskNoChildFound = 0x706, kTaskInvalidPid = 0x707, + // Signal 相关错误 (0xC00 - 0xCFF) + kSignalInvalidNumber = 0xC00, + kSignalInvalidPid = 0xC01, + kSignalPermissionDenied = 0xC02, + kSignalUncatchable = 0xC03, + kSignalTaskNotFound = 0xC04, // Device 相关错误 (0x800 - 0x8FF) kDeviceNotFound = 0x800, kDeviceAlreadyOpen = 0x801, @@ -192,20 +190,6 @@ constexpr auto GetErrorMessage(ErrorCode code) -> const char* { return "IPI target CPU mask out of range"; case ErrorCode::kIpiSendFailed: return "IPI send failed"; - case ErrorCode::kApicInitFailed: - return "APIC initialization failed"; - case ErrorCode::kApicInvalidIrq: - return "Invalid IRQ number"; - case ErrorCode::kApicInvalidParameter: - return "Invalid APIC parameter"; - case ErrorCode::kApicCodeCopyFailed: - return "AP code copy verification failed"; - case ErrorCode::kApicAddressNotAligned: - return "Address not aligned to required boundary"; - case ErrorCode::kApicAddressOutOfRange: - return "Address out of valid range"; - case ErrorCode::kApicIpiTimeout: - return "IPI delivery timeout"; case ErrorCode::kTaskNoCurrentTask: return "No current task"; case ErrorCode::kTaskPidAllocationFailed: @@ -222,6 +206,16 @@ constexpr auto GetErrorMessage(ErrorCode code) -> const char* { return "No child process found"; case ErrorCode::kTaskInvalidPid: return "Invalid PID"; + case ErrorCode::kSignalInvalidNumber: + return "Invalid signal number"; + case ErrorCode::kSignalInvalidPid: + return "Invalid PID for signal delivery"; + case ErrorCode::kSignalPermissionDenied: + return "Signal delivery permission denied"; + case ErrorCode::kSignalUncatchable: + return "Cannot catch or ignore this signal"; + case ErrorCode::kSignalTaskNotFound: + return "Target task not found for signal delivery"; case ErrorCode::kDeviceNotFound: return "Device not found"; case ErrorCode::kDeviceAlreadyOpen: diff --git a/src/include/interrupt_base.h b/src/include/interrupt_base.h index b402b0b1f..0b0e0d133 100644 --- a/src/include/interrupt_base.h +++ b/src/include/interrupt_base.h @@ -15,7 +15,7 @@ * @brief 中断子系统抽象基类 * * 所有架构的中断处理必须实现此接口。 - * 已知实现:PLIC(RISC-V)、GIC(AArch64)、APIC(x86_64) + * 已知实现:PLIC(RISC-V)、GIC(AArch64) * * @pre 硬件中断控制器已初始化 * @post 可通过 RegisterInterruptFunc 注册中断处理函数 @@ -71,7 +71,7 @@ class InterruptBase { /** * @brief 注册外部中断处理函数 - * @param irq 外部中断号(平台相关: PLIC source_id / GIC INTID / APIC IRQ) + * @param irq 外部中断号(平台相关: PLIC source_id / GIC INTID) * @param cpu_id 目标 CPU 核心 ID,中断将路由到该核心 * @param priority 中断优先级 * @param handler 中断处理函数 diff --git a/src/include/kernel_config.hpp b/src/include/kernel_config.hpp index 0fe4ab11c..1691e9608 100644 --- a/src/include/kernel_config.hpp +++ b/src/include/kernel_config.hpp @@ -11,27 +11,27 @@ namespace kernel::config { /// 全局最大任务数(task_table_ 容量) -inline constexpr size_t kMaxTasks = 128; -/// task_table_ 桶数(建议 = 2 × kMaxTasks) -inline constexpr size_t kMaxTasksBuckets = 256; +inline constexpr size_t kMaxTasks = 256; +/// task_table_ 桶数 +inline constexpr size_t kMaxTasksBuckets = 2 * kMaxTasks; /// 每个 CPU 的最大睡眠任务数(sleeping_tasks 容量) -inline constexpr size_t kMaxSleepingTasks = 64; +inline constexpr size_t kMaxSleepingTasks = 128; /// 阻塞队列:最大资源组数(blocked_tasks 的 map 容量) -inline constexpr size_t kMaxBlockedGroups = 32; +inline constexpr size_t kMaxBlockedGroups = 64; /// 阻塞队列:map 桶数 -inline constexpr size_t kMaxBlockedGroupsBuckets = 64; +inline constexpr size_t kMaxBlockedGroupsBuckets = 2 * kMaxBlockedGroups; /// 阻塞队列:每组最大阻塞任务数(etl::list 容量) -inline constexpr size_t kMaxBlockedPerGroup = 16; +inline constexpr size_t kMaxBlockedPerGroup = 32; /// 调度器就绪队列容量(FIFO / RR / CFS) -inline constexpr size_t kMaxReadyTasks = 64; +inline constexpr size_t kMaxReadyTasks = 128; /// 最大中断线程数 inline constexpr size_t kMaxInterruptThreads = 32; /// 中断线程 map 桶数 -inline constexpr size_t kMaxInterruptThreadsBuckets = 64; +inline constexpr size_t kMaxInterruptThreadsBuckets = 2 * kMaxInterruptThreads; /// 最大 tick 观察者数 inline constexpr size_t kTickObservers = 8; diff --git a/src/include/signal.hpp b/src/include/signal.hpp new file mode 100644 index 000000000..4a5e79934 --- /dev/null +++ b/src/include/signal.hpp @@ -0,0 +1,264 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#pragma once + +#include +#include +#include +#include + +/// 最大信号数 +inline constexpr uint8_t kMaxSignals = 32; + +/** + * @brief 信号编号 (参考 POSIX 标准) + */ +namespace signal_number { +/// 无信号 +inline constexpr int kSigNone = 0; +/// 终端挂起 +inline constexpr int kSigHup = 1; +/// 键盘中断 (Ctrl+C) +inline constexpr int kSigInt = 2; +/// 键盘退出 (Ctrl+\) +inline constexpr int kSigQuit = 3; +/// 非法指令 +inline constexpr int kSigIll = 4; +/// 异常终止 +inline constexpr int kSigAbrt = 6; +/// 浮点异常 +inline constexpr int kSigFpe = 8; +/// 强制终止 (不可捕获) +inline constexpr int kSigKill = 9; +/// 段错误 +inline constexpr int kSigSegv = 11; +/// 管道破裂 +inline constexpr int kSigPipe = 13; +/// 定时器到期 +inline constexpr int kSigAlrm = 14; +/// 终止请求 +inline constexpr int kSigTerm = 15; +/// 子进程状态变化 +inline constexpr int kSigChld = 17; +/// 继续 (如果已停止) +inline constexpr int kSigCont = 18; +/// 停止 (不可捕获) +inline constexpr int kSigStop = 19; +/// 终端停止 (Ctrl+Z) +inline constexpr int kSigTstp = 20; +/// 用户自定义信号 1 +inline constexpr int kSigUsr1 = 10; +/// 用户自定义信号 2 +inline constexpr int kSigUsr2 = 12; +} // namespace signal_number + +/** + * @brief 信号掩码操作 + */ +namespace signal_mask_op { +/// 添加到信号掩码 +inline constexpr int kSigBlock = 0; +/// 从信号掩码移除 +inline constexpr int kSigUnblock = 1; +/// 设置信号掩码 +inline constexpr int kSigSetmask = 2; +} // namespace signal_mask_op + +/// 信号处理函数类型 +using SignalHandler = void (*)(int); + +/// 默认信号处理 (终止进程) +inline constexpr SignalHandler kSigDfl = nullptr; + +/// 忽略信号 +inline const SignalHandler kSigIgn = reinterpret_cast(1); + +/** + * @brief 信号动作结构 + * + * 定义每个信号的处理方式。简化版的 POSIX sigaction。 + */ +struct SignalAction { + /// 信号处理函数 + SignalHandler handler{kSigDfl}; + /// 执行信号处理时阻塞的信号集 + uint32_t mask{0}; + + /// @name 构造/析构函数 + /// @{ + SignalAction() = default; + SignalAction(const SignalAction&) = default; + SignalAction(SignalAction&&) = default; + auto operator=(const SignalAction&) -> SignalAction& = default; + auto operator=(SignalAction&&) -> SignalAction& = default; + ~SignalAction() = default; + /// @} +}; + +/** + * @brief 每个任务的信号状态 + * + * 管理信号的投递、屏蔽和处理器注册。 + */ +struct SignalState { + /// 待处理信号位掩码 + std::atomic pending{0}; + /// 阻塞信号位掩码 + std::atomic blocked{0}; + /// 每个信号的处理动作 + std::array actions{}; + + /** + * @brief 检查是否有可投递的信号 + * @return true 有未屏蔽的待处理信号 + */ + [[nodiscard]] auto HasDeliverableSignal() const -> bool { + return (pending.load(std::memory_order_acquire) & + ~blocked.load(std::memory_order_acquire)) != 0; + } + + /** + * @brief 获取下一个可投递的信号编号 + * @return int 信号编号,无可投递信号返回 0 + * @note 优先级从低编号到高编号 + */ + [[nodiscard]] auto GetNextDeliverableSignal() const -> int { + uint32_t deliverable = pending.load(std::memory_order_acquire) & + ~blocked.load(std::memory_order_acquire); + if (deliverable == 0) { + return signal_number::kSigNone; + } + // 返回最低位的信号 + for (int i = 1; i < kMaxSignals; ++i) { + if (deliverable & (1U << i)) { + return i; + } + } + return signal_number::kSigNone; + } + + /** + * @brief 设置待处理信号 + * @param signum 信号编号 + */ + auto SetPending(int signum) -> void { + assert(IsValid(signum) && "signum is invalid"); + pending.fetch_or(1U << signum, std::memory_order_release); + } + + /** + * @brief 清除待处理信号 + * @param signum 信号编号 + */ + auto ClearPending(int signum) -> void { + assert(IsValid(signum) && "signum is invalid"); + pending.fetch_and(~(1U << signum), std::memory_order_release); + } + + /** + * @brief 检查信号是否不可捕获 (SIGKILL, SIGSTOP) + * @param signum 信号编号 + * @return true 信号不可捕获 + */ + [[nodiscard]] static constexpr auto IsUncatchable(int signum) -> bool { + return signum == signal_number::kSigKill || + signum == signal_number::kSigStop; + } + + /** + * @brief 检查信号编号是否有效 + * @param signum 信号编号 + * @return true 有效 + */ + [[nodiscard]] static constexpr auto IsValid(int signum) -> bool { + return signum > 0 && signum < kMaxSignals; + } + + /** + * @brief 获取信号名称(调试用) + * @param signum 信号编号 + * @return 信号名称字符串 + */ + [[nodiscard]] static constexpr auto GetSignalName(int signum) -> const char* { + switch (signum) { + case signal_number::kSigHup: + return "SIGHUP"; + case signal_number::kSigInt: + return "SIGINT"; + case signal_number::kSigQuit: + return "SIGQUIT"; + case signal_number::kSigIll: + return "SIGILL"; + case signal_number::kSigAbrt: + return "SIGABRT"; + case signal_number::kSigFpe: + return "SIGFPE"; + case signal_number::kSigKill: + return "SIGKILL"; + case signal_number::kSigSegv: + return "SIGSEGV"; + case signal_number::kSigPipe: + return "SIGPIPE"; + case signal_number::kSigAlrm: + return "SIGALRM"; + case signal_number::kSigTerm: + return "SIGTERM"; + case signal_number::kSigChld: + return "SIGCHLD"; + case signal_number::kSigCont: + return "SIGCONT"; + case signal_number::kSigStop: + return "SIGSTOP"; + case signal_number::kSigTstp: + return "SIGTSTP"; + case signal_number::kSigUsr1: + return "SIGUSR1"; + case signal_number::kSigUsr2: + return "SIGUSR2"; + default: + return "SIG???"; + } + } + + /// @name 构造/析构函数 + /// @{ + SignalState() = default; + SignalState(const SignalState&) = delete; + SignalState(SignalState&&) = delete; + auto operator=(const SignalState&) -> SignalState& = delete; + auto operator=(SignalState&&) -> SignalState& = delete; + ~SignalState() = default; + /// @} +}; + +/** + * @brief 获取信号的默认动作 + * @param signum 信号编号 + * @return 默认动作 ('T'=终止, 'I'=忽略, 'C'=核心转储, 'S'=停止, 'K'=继续) + */ +[[nodiscard]] constexpr auto GetDefaultSignalAction(int signum) -> char { + switch (signum) { + case signal_number::kSigChld: + // 忽略 + return 'I'; + case signal_number::kSigCont: + // 继续 + return 'K'; + case signal_number::kSigStop: + case signal_number::kSigTstp: + // 停止 + return 'S'; + case signal_number::kSigQuit: + case signal_number::kSigIll: + case signal_number::kSigAbrt: + case signal_number::kSigFpe: + case signal_number::kSigSegv: + // 核心转储 (简化为终止) + return 'C'; + default: + // 终止 + return 'T'; + } +} diff --git a/src/include/syscall.hpp b/src/include/syscall.hpp index c778900a4..175141d99 100644 --- a/src/include/syscall.hpp +++ b/src/include/syscall.hpp @@ -9,6 +9,8 @@ #include #include +#include "signal.hpp" + // 参考 Linux 系统调用号 #if defined(__riscv) || defined(__aarch64__) // RISC-V 64 和 AArch64 使用 asm-generic 编号 @@ -21,17 +23,11 @@ inline constexpr uint64_t kSyscallFutex = 98; inline constexpr uint64_t kSyscallSetTidAddress = 96; inline constexpr uint64_t kSyscallFork = 1220; inline constexpr uint64_t kSyscallSleep = 101; -#elif defined(__x86_64__) -// x86_64 使用自己的编号 -inline constexpr uint64_t kSyscallWrite = 1; -inline constexpr uint64_t kSyscallExit = 60; -inline constexpr uint64_t kSyscallYield = 24; -inline constexpr uint64_t kSyscallClone = 56; -inline constexpr uint64_t kSyscallGettid = 186; -inline constexpr uint64_t kSyscallFutex = 202; -inline constexpr uint64_t kSyscallSetTidAddress = 218; -inline constexpr uint64_t kSyscallFork = 57; -inline constexpr uint64_t kSyscallSleep = 35; +inline constexpr uint64_t kSyscallKill = 129; +inline constexpr uint64_t kSyscallSigaction = 134; +inline constexpr uint64_t kSyscallSigprocmask = 135; +inline constexpr uint64_t kSyscallSchedGetaffinity = 123; +inline constexpr uint64_t kSyscallSchedSetaffinity = 122; #else #error "Unsupported architecture for syscall numbers" #endif @@ -179,3 +175,29 @@ auto sys_exit(int code) -> int; */ [[nodiscard]] auto sys_sched_setaffinity(int pid, size_t cpusetsize, const uint64_t* mask) -> int; + +/** + * @brief 向指定进程发送信号 + * @param pid 目标进程 PID + * @param sig 信号编号 + * @return 成功返回 0,失败返回负数 + */ +[[nodiscard]] auto sys_kill(int pid, int sig) -> int; + +/** + * @brief 设置信号处理函数 + * @param signum 信号编号 + * @param handler 新的信号处理函数 + * @return 成功返回 0,失败返回负数 + */ +[[nodiscard]] auto sys_sigaction(int signum, SignalHandler handler) -> int; + +/** + * @brief 修改进程信号掩码 + * @param how 操作方式 (SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK) + * @param set 要操作的信号集 + * @param oldset 旧信号集存储位置(可为 nullptr) + * @return 成功返回 0,失败返回负数 + */ +[[nodiscard]] auto sys_sigprocmask(int how, uint32_t set, uint32_t* oldset) + -> int; diff --git a/src/libc/include/sk_stdlib.h b/src/libc/include/sk_stdlib.h index 3e8ff34b0..424b24be8 100644 --- a/src/libc/include/sk_stdlib.h +++ b/src/libc/include/sk_stdlib.h @@ -53,8 +53,7 @@ long long int atoll(const char* nptr); */ double strtod(const char* nptr, char** endptr); -#if (defined(__x86_64__) && defined(__SSE__)) || \ - (defined(__aarch64__) && defined(__ARM_FP)) || defined(__riscv) +#if (defined(__aarch64__) && defined(__ARM_FP)) || defined(__riscv) /** * @brief 将字符串转换为单精度浮点数,并可以获取转换结束位置 * @param nptr 指向要转换的字符串的指针 diff --git a/src/libc/sk_stdlib.c b/src/libc/sk_stdlib.c index 48be19784..7e129eb7f 100644 --- a/src/libc/sk_stdlib.c +++ b/src/libc/sk_stdlib.c @@ -156,8 +156,7 @@ long int atol(const char* nptr) { return strtol(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) +#if (defined(__aarch64__) && defined(__ARM_FP)) || defined(__riscv) double strtod(const char* nptr, char** endptr) { const char* s = nptr; double acc = 0.0; diff --git a/src/main.cpp b/src/main.cpp index 1059bb7bb..b771dbc08 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,112 +2,19 @@ * @copyright Copyright The SimpleKernel Contributors */ -#include -#include +#include #include -#include #include "arch.h" -#include "basic_info.hpp" -#include "expected.hpp" -#include "interrupt.h" #include "kernel.h" #include "kernel_log.hpp" -#include "kstd_cstdio" -#include "kstd_cstring" #include "kstd_libcxx.h" -#include "mutex.hpp" #include "per_cpu.hpp" -#include "sk_stdlib.h" -#include "syscall.hpp" -#include "task_control_block.hpp" #include "task_manager.hpp" #include "virtual_memory.hpp" namespace { -/// 全局变量,用于测试多核同步 -std::atomic global_counter{0}; - -/// Task1: 每 1s 打印一次,测试 sys_exit -auto task1_func(void* arg) -> void { - klog::Info("Task1: arg = {:#x}", - static_cast(reinterpret_cast(arg))); - for (int i = 0; i < 5; ++i) { - klog::Info("Task1: iteration {}/5", i + 1); - (void)sys_sleep(1000); - } - klog::Info("Task1: exiting with code 0"); - sys_exit(0); -} - -/// Task2: 每 2s 打印一次,测试 sys_yield -auto task2_func(void* arg) -> void { - klog::Info("Task2: arg = {:#x}", - static_cast(reinterpret_cast(arg))); - uint64_t count = 0; - while (1) { - klog::Info("Task2: yield count={}", count++); - (void)sys_sleep(2000); - // 主动让出 CPU - (void)sys_yield(); - } -} - -/// Task3: 每 3s 打印一次,同时修改全局变量,测试多核同步 -auto task3_func(void* arg) -> void { - klog::Info("Task3: arg = {:#x}", - static_cast(reinterpret_cast(arg))); - while (1) { - uint64_t old_value = global_counter.fetch_add(1, std::memory_order_relaxed); - klog::Info("Task3: global_counter {} -> {}", old_value, old_value + 1); - (void)sys_sleep(3000); - } -} - -/// Task4: 每 4s 打印一次,测试 sys_sleep -auto task4_func(void* arg) -> void { - klog::Info("Task4: arg = {:#x}", - static_cast(reinterpret_cast(arg))); - uint64_t iteration = 0; - while (1) { - auto* cpu_sched = per_cpu::GetCurrentCore().sched_data; - auto start_tick = cpu_sched->local_tick; - klog::Info("Task4: sleeping for 4s (iteration {})", iteration++); - (void)sys_sleep(4000); - auto end_tick = cpu_sched->local_tick; - klog::Info("Task4: woke up (slept ~{} ticks)", end_tick - start_tick); - } -} - -/// 为当前核心创建测试任务 -auto create_test_tasks() -> void { - size_t core_id = cpu_io::GetCurrentCoreId(); - auto& tm = TaskManagerSingleton::instance(); - - auto task1 = kstd::make_unique( - "Task1-Exit", 10, task1_func, reinterpret_cast(0x1111)); - auto task2 = kstd::make_unique( - "Task2-Yield", 10, task2_func, reinterpret_cast(0x2222)); - auto task3 = kstd::make_unique( - "Task3-Sync", 10, task3_func, reinterpret_cast(0x3333)); - auto task4 = kstd::make_unique( - "Task4-Sleep", 10, task4_func, reinterpret_cast(0x4444)); - - // 设置 CPU 亲和性,绑定到当前核心 - task1->aux->cpu_affinity = (1UL << core_id); - task2->aux->cpu_affinity = (1UL << core_id); - task3->aux->cpu_affinity = (1UL << core_id); - task4->aux->cpu_affinity = (1UL << core_id); - - tm.AddTask(std::move(task1)); - tm.AddTask(std::move(task2)); - tm.AddTask(std::move(task3)); - tm.AddTask(std::move(task4)); - - klog::Info("Created 4 test tasks"); -} - /// 非启动核入口 auto main_smp(int argc, const char** argv) -> int { per_cpu::GetCurrentCore() = per_cpu::PerCpu(cpu_io::GetCurrentCoreId()); @@ -119,9 +26,6 @@ auto main_smp(int argc, const char** argv) -> int { klog::Info("Hello SimpleKernel SMP"); - // 为当前核心创建测试任务 - create_test_tasks(); - // 启动调度器 TaskManagerSingleton::instance().Schedule(); @@ -131,8 +35,10 @@ auto main_smp(int argc, const char** argv) -> int { } // namespace +std::atomic_flag primary_booted_ = ATOMIC_FLAG_INIT; + auto _start(int argc, const char** argv) -> void { - if (argv != nullptr) { + if (!primary_booted_.test_and_set(std::memory_order_acquire)) { CppInit(); main(argc, argv); } else { @@ -172,13 +78,6 @@ auto main(int argc, const char** argv) -> int { klog::Info("Hello SimpleKernel"); - klog::Info("Initializing test tasks..."); - - // 为主核心创建测试任务 - create_test_tasks(); - - klog::Info("Main: Starting scheduler..."); - // 启动调度器,不再返回 TaskManagerSingleton::instance().Schedule(); diff --git a/src/memory/include/virtual_memory.hpp b/src/memory/include/virtual_memory.hpp index f2c4c9af4..d96fac519 100644 --- a/src/memory/include/virtual_memory.hpp +++ b/src/memory/include/virtual_memory.hpp @@ -17,7 +17,7 @@ * * @details 本类是内核虚拟内存子系统的唯一实现,通过 * cpu_io::virtual_memory 命名空间中的架构相关辅助函数 - * 实现跨架构(x86_64/riscv64/aarch64)的页表操作。 + * 实现跨架构(riscv64/aarch64)的页表操作。 * 架构差异由 cpu_io 库在编译期分发,本类本身不使用虚函数 * 或条件编译。 * diff --git a/src/memory/memory.cpp b/src/memory/memory.cpp index af0452c78..5568b40d1 100644 --- a/src/memory/memory.cpp +++ b/src/memory/memory.cpp @@ -13,6 +13,7 @@ #include "kernel.h" #include "kernel_elf.hpp" #include "kernel_log.hpp" +#include "spinlock.hpp" #include "virtual_memory.hpp" namespace { @@ -25,7 +26,37 @@ struct BmallocLogger { } }; -bmalloc::Bmalloc* allocator = nullptr; +/** + * @brief bmalloc 线程安全锁(基于 SpinLock) + * @details 在 Lock() 时关中断 + 原子自旋,保证 malloc/free + * 在 timer 抢占和多核环境下的安全性。 + */ +class BmallocLock : public bmalloc::LockBase { + public: + void Lock() override { + lock_.Lock().or_else([](auto&&) -> Expected { + // 不应触发:bmalloc 内部不会递归加锁 + while (true) { + cpu_io::Pause(); + } + return {}; + }); + } + + void Unlock() override { + lock_.UnLock().or_else([](auto&&) -> Expected { + while (true) { + cpu_io::Pause(); + } + return {}; + }); + } + + private: + SpinLock lock_{"bmalloc"}; +}; + +bmalloc::Bmalloc* allocator = nullptr; } // namespace extern "C" auto malloc(size_t size) -> void* { @@ -81,8 +112,8 @@ auto MemoryInit() -> void { static_cast(reinterpret_cast(allocator_addr)), static_cast(allocator_size)); - static bmalloc::Bmalloc bmallocator(allocator_addr, - allocator_size); + static bmalloc::Bmalloc bmallocator( + allocator_addr, allocator_size); allocator = &bmallocator; // 初始化当前核心的虚拟内存 diff --git a/src/syscall.cpp b/src/syscall.cpp index ee8dcbb7b..58a8e80d4 100644 --- a/src/syscall.cpp +++ b/src/syscall.cpp @@ -52,6 +52,28 @@ auto syscall_dispatcher(int64_t syscall_id, uint64_t args[6]) -> int { case kSyscallSleep: ret = sys_sleep(args[0]); break; + case kSyscallKill: + ret = sys_kill(static_cast(args[0]), static_cast(args[1])); + break; + case kSyscallSigaction: + ret = sys_sigaction(static_cast(args[0]), + reinterpret_cast(args[1])); + break; + case kSyscallSigprocmask: + ret = sys_sigprocmask(static_cast(args[0]), + static_cast(args[1]), + reinterpret_cast(args[2])); + break; + case kSyscallSchedGetaffinity: + ret = sys_sched_getaffinity(static_cast(args[0]), + static_cast(args[1]), + reinterpret_cast(args[2])); + break; + case kSyscallSchedSetaffinity: + ret = sys_sched_setaffinity(static_cast(args[0]), + static_cast(args[1]), + reinterpret_cast(args[2])); + break; default: klog::Err("[Syscall] Unknown syscall id: {}", syscall_id); ret = -1; @@ -306,3 +328,36 @@ auto sys_exit(int code) -> int { return 0; } + +[[nodiscard]] auto sys_kill(int pid, int sig) -> int { + auto result = + TaskManagerSingleton::instance().SendSignal(static_cast(pid), sig); + if (!result.has_value()) { + klog::Err("[Syscall] sys_kill failed: {}", result.error().message()); + return -1; + } + return 0; +} + +[[nodiscard]] auto sys_sigaction(int signum, SignalHandler handler) -> int { + SignalAction action; + action.handler = handler; + auto result = + TaskManagerSingleton::instance().SetSignalAction(signum, action, nullptr); + if (!result.has_value()) { + klog::Err("[Syscall] sys_sigaction failed: {}", result.error().message()); + return -1; + } + return 0; +} + +[[nodiscard]] auto sys_sigprocmask(int how, uint32_t set, uint32_t* oldset) + -> int { + auto result = + TaskManagerSingleton::instance().SetSignalMask(how, set, oldset); + if (!result.has_value()) { + klog::Err("[Syscall] sys_sigprocmask failed: {}", result.error().message()); + return -1; + } + return 0; +} diff --git a/src/task/CMakeLists.txt b/src/task/CMakeLists.txt index 08bf06006..a616a23c0 100644 --- a/src/task/CMakeLists.txt +++ b/src/task/CMakeLists.txt @@ -16,4 +16,5 @@ TARGET_SOURCES ( clone.cpp wait.cpp task_manager.cpp - mutex.cpp) + mutex.cpp + signal.cpp) diff --git a/src/task/block.cpp b/src/task/block.cpp index dc9d02e64..7eed5f2f8 100644 --- a/src/task/block.cpp +++ b/src/task/block.cpp @@ -9,40 +9,35 @@ #include "task_manager.hpp" #include "task_messages.hpp" -auto TaskManager::Block(ResourceId resource_id) -> void { - auto& cpu_sched = GetCurrentCpuSched(); - +auto TaskManager::Block(CpuSchedData& cpu_sched, ResourceId resource_id) + -> void { auto* current = GetCurrentTask(); assert(current != nullptr && "Block: No current task to block"); assert(current->GetStatus() == TaskStatus::kRunning && "Block: current task status must be kRunning"); - { - LockGuard lock_guard(cpu_sched.lock); + auto& list = cpu_sched.blocked_tasks[resource_id]; + if (list.full()) { + klog::Err( + "Block: blocked_tasks list full for resource, cannot block task {}", + current->pid); + return; + } - // Check capacity before transitioning FSM - auto& list = cpu_sched.blocked_tasks[resource_id]; - if (list.full()) { - klog::Err( - "Block: blocked_tasks list full for resource, cannot block task {}", - current->pid); - // Rollback: task stays kRunning, do not transition FSM - return; - } + current->fsm.Receive(MsgBlock{resource_id}); + current->aux->blocked_on = resource_id; + list.push_back(current); - // Transition: kRunning -> kBlocked - current->fsm.Receive(MsgBlock{resource_id}); - // Record blocked resource - current->aux->blocked_on = resource_id; - list.push_back(current); + klog::Debug("Block: pid={} blocked on resource={}, data={:#x}", current->pid, + resource_id.GetTypeName(), + static_cast(resource_id.GetData())); +} - klog::Debug("Block: pid={} blocked on resource={}, data={:#x}", - current->pid, resource_id.GetTypeName(), - static_cast(resource_id.GetData())); +auto TaskManager::Block(ResourceId resource_id) -> void { + auto& cpu_sched = GetCurrentCpuSched(); + { + LockGuard lock_guard(cpu_sched.lock); + Block(cpu_sched, resource_id); } - - // 调度到其他任务 Schedule(); - - // 任务被唤醒后会从这里继续执行 } diff --git a/src/task/exit.cpp b/src/task/exit.cpp index dacc262a9..a876ef24c 100644 --- a/src/task/exit.cpp +++ b/src/task/exit.cpp @@ -16,16 +16,15 @@ auto TaskManager::Exit(int exit_code) -> void { assert(current->GetStatus() == TaskStatus::kRunning && "Exit: current task status must be kRunning"); + ResourceId wait_resource_id{}; + bool should_wake_parent = false; + { LockGuard lock_guard(cpu_sched.lock); - // 设置退出码 current->aux->exit_code = exit_code; - - // 检查是否是线程组的主线程 bool is_group_leader = current->IsThreadGroupLeader(); - // 如果是线程组的主线程,需要检查是否还有其他线程 if (is_group_leader && current->GetThreadGroupSize() > 1) { klog::Warn( "Exit: Thread group leader (pid={}, tgid={}) exiting, but group " @@ -33,43 +32,31 @@ auto TaskManager::Exit(int exit_code) -> void { current->pid, current->aux->tgid, current->GetThreadGroupSize()); } - // 从线程组中移除 current->LeaveThreadGroup(); - // 将子进程过继给 init 进程 (仅当是进程时) - if (is_group_leader) { - ReparentChildren(current); - } - if (current->aux->parent_pid != 0) { - // 有父进程,进入僵尸状态等待回收 - // Transition: kRunning -> kZombie current->fsm.Receive(MsgExit{exit_code, true}); - - // 唤醒等待此进程退出的父进程 - // 父进程会阻塞在 ChildExit 类型的资源上,数据是父进程自己的 PID - auto wait_resource_id = + wait_resource_id = ResourceId(ResourceType::kChildExit, current->aux->parent_pid); - Wakeup(wait_resource_id); - - /// @todo 通知父进程 (发送 SIGCHLD) + should_wake_parent = true; - klog::Debug("Exit: pid={} waking up parent={} on resource={}", - current->pid, current->aux->parent_pid, - wait_resource_id.GetTypeName()); + klog::Debug("Exit: pid={} entering zombie, will wake parent={}", + current->pid, current->aux->parent_pid); } else { - // 没有父进程,直接退出并释放资源 - // Transition: kRunning -> kExited current->fsm.Receive(MsgExit{exit_code, false}); - // No parent to call wait(), reap immediately to free TCB + stack - ReapTask(current); + } + + if (is_group_leader) { + ReparentChildren(current); } } - Schedule(); + if (should_wake_parent) { + Wakeup(wait_resource_id); + } - klog::Err("Exit: Task {} should not return from Schedule()", current->pid); + Schedule(); - // UNREACHABLE: 任务退出后 Schedule() 不应返回 + // UNREACHABLE __builtin_unreachable(); } diff --git a/src/task/include/scheduler_base.hpp b/src/task/include/scheduler_base.hpp index 97eab8fd9..366d22935 100644 --- a/src/task/include/scheduler_base.hpp +++ b/src/task/include/scheduler_base.hpp @@ -9,6 +9,18 @@ #include "task_control_block.hpp" +/** + * @brief 新任务首次执行时的引导函数 (由 kernel_thread_entry 汇编调用) + * + * @param entry 任务入口函数 + * @param arg 传递给入口函数的参数 + * + * @pre 由 kernel_thread_entry 汇编以 C 调用约定调用 + * @post 中断已开启,任务入口函数已执行,进程已退出 + */ +extern "C" [[noreturn]] void kernel_thread_bootstrap(void (*entry)(void*), + void* arg); + /** * @brief 调度器基类接口 * diff --git a/src/task/include/task_control_block.hpp b/src/task/include/task_control_block.hpp index 022b1fa3c..a5051f48a 100644 --- a/src/task/include/task_control_block.hpp +++ b/src/task/include/task_control_block.hpp @@ -14,6 +14,7 @@ #include "file_descriptor.hpp" #include "resource_id.hpp" +#include "signal.hpp" #include "task_fsm.hpp" /// 进程 ID 类型 @@ -101,6 +102,9 @@ struct TaskAuxData { /// 等待的资源 ID ResourceId blocked_on{}; + /// 信号状态 + SignalState signals{}; + /// 是否为中断线程 bool is_interrupt_thread{false}; /// 关联的中断号 diff --git a/src/task/include/task_fsm.hpp b/src/task/include/task_fsm.hpp index bb02363f8..9f0677dfd 100644 --- a/src/task/include/task_fsm.hpp +++ b/src/task/include/task_fsm.hpp @@ -6,6 +6,8 @@ #include +#include + #include "kernel_log.hpp" #include "task_messages.hpp" @@ -154,20 +156,30 @@ class TaskFsm { /** * @brief 启动 FSM(在 TCB 完全构造后调用) */ - auto Start() -> void { fsm_.start(); } + auto Start() -> void { + fsm_.start(); + cached_state_.store(fsm_.get_state_id(), std::memory_order_release); + } /** * @brief 向 FSM 发送消息 * @param msg 要发送的消息 + * @pre 调用者持有保护此 TCB 的锁(如 cpu_sched.lock) + * @post cached_state_ 以 release 语义更新,跨核可见 */ - auto Receive(const etl::imessage& msg) -> void { fsm_.receive(msg); } + auto Receive(const etl::imessage& msg) -> void { + fsm_.receive(msg); + cached_state_.store(fsm_.get_state_id(), std::memory_order_release); + } /** - * @brief 获取当前状态 ID + * @brief 获取当前状态 ID(线程安全) * @return etl::fsm_state_id_t 当前状态 ID + * @note 使用 acquire 语义从 atomic 缓存读取, + * 无需持有与 Receive() 相同的锁即可安全调用 */ [[nodiscard]] auto GetStateId() const -> etl::fsm_state_id_t { - return fsm_.get_state_id(); + return cached_state_.load(std::memory_order_acquire); } /// @name 构造/析构函数 @@ -202,4 +214,7 @@ class TaskFsm { etl::ifsm_state* state_list_[7]; etl::fsm fsm_; + + /// 跨核可见的状态缓存,解决 etl::fsm 非线程安全的问题 + std::atomic cached_state_{TaskStatusId::kUnInit}; }; diff --git a/src/task/include/task_manager.hpp b/src/task/include/task_manager.hpp index d84d043e4..e47e86af9 100644 --- a/src/task/include/task_manager.hpp +++ b/src/task/include/task_manager.hpp @@ -24,6 +24,7 @@ #include "per_cpu.hpp" #include "resource_id.hpp" #include "scheduler_base.hpp" +#include "signal.hpp" #include "spinlock.hpp" #include "task_control_block.hpp" @@ -140,6 +141,16 @@ class TaskManager { */ auto Block(ResourceId resource_id) -> void; + /** + * @brief 阻塞当前任务(调用者已持有 cpu_sched.lock,不调用 Schedule) + * @param cpu_sched 当前核心的调度数据 + * @param resource_id 等待的资源 ID + * @pre cpu_sched.lock 已被调用者持有 + * @post 当前任务已转移到 blocked_tasks,FSM 状态为 kBlocked + * @note 调用者必须在释放锁后调用 Schedule() + */ + auto Block(CpuSchedData& cpu_sched, ResourceId resource_id) -> void; + /** * @brief 唤醒等待指定资源的所有任务 * @param resource_id 资源 ID @@ -147,6 +158,21 @@ class TaskManager { */ auto Wakeup(ResourceId resource_id) -> void; + /** + * @brief 唤醒等待指定资源的第一个任务(跨核搜索) + * @param resource_id 资源 ID + * @note 只唤醒一个任务,用于互斥锁等只需唤醒一个等待者的场景 + */ + auto WakeupOne(ResourceId resource_id) -> void; + + /** + * @brief 唤醒等待指定资源的所有任务(调用者已持有 cpu_sched.lock) + * @param cpu_sched 当前核心的调度数据 + * @param resource_id 资源 ID + * @pre cpu_sched.lock 已被调用者持有 + */ + auto Wakeup(CpuSchedData& cpu_sched, ResourceId resource_id) -> void; + /** * @brief 克隆当前任务 (fork/clone 系统调用) * @param flags 克隆标志位 @@ -185,6 +211,54 @@ class TaskManager { */ [[nodiscard]] auto FindTask(Pid pid) -> TaskControlBlock*; + /** + * @brief 获取当前核心的调度数据 + * @return CpuSchedData& 当前核心的调度数据引用 + */ + [[nodiscard]] auto GetCurrentCpuSched() -> CpuSchedData& { + return cpu_schedulers_[cpu_io::GetCurrentCoreId()]; + } + + /// @name 信号机制 + /// @{ + + /** + * @brief 向指定任务发送信号 + * @param pid 目标任务 PID + * @param signum 信号编号 + * @return Expected 成功返回空值,失败返回错误 + */ + [[nodiscard]] auto SendSignal(Pid pid, int signum) -> Expected; + + /** + * @brief 检查并处理当前任务的待处理信号 + * @return int 处理的信号编号,无信号返回 0 + */ + [[nodiscard]] auto CheckPendingSignals() -> int; + + /** + * @brief 设置信号处理函数 + * @param signum 信号编号 + * @param action 新的信号动作 + * @param old_action 旧的信号动作(输出参数,可为 nullptr) + * @return Expected 成功返回空值,失败返回错误 + */ + [[nodiscard]] auto SetSignalAction(int signum, const SignalAction& action, + SignalAction* old_action) + -> Expected; + + /** + * @brief 修改当前任务的信号掩码 + * @param how 操作方式 (kSigBlock, kSigUnblock, kSigSetmask) + * @param set 要操作的信号集 + * @param oldset 旧信号集输出(可为 nullptr) + * @return Expected 成功返回空值,失败返回错误 + */ + [[nodiscard]] auto SetSignalMask(int how, uint32_t set, uint32_t* oldset) + -> Expected; + + /// @} + /// @name 构造/析构函数 /// @{ TaskManager() = default; @@ -257,14 +331,6 @@ class TaskManager { */ auto Balance() -> void; - /** - * @brief 获取当前核心的调度数据 - * @return CpuSchedData& 当前核心的调度数据引用 - */ - [[nodiscard]] auto GetCurrentCpuSched() -> CpuSchedData& { - return cpu_schedulers_[cpu_io::GetCurrentCoreId()]; - } - /** * @brief 获取线程组的所有线程 * @param tgid 线程组 ID diff --git a/src/task/mutex.cpp b/src/task/mutex.cpp index 3db552037..9dc2356cc 100644 --- a/src/task/mutex.cpp +++ b/src/task/mutex.cpp @@ -9,7 +9,8 @@ #include "task_manager.hpp" auto Mutex::Lock() -> Expected { - auto current_task = TaskManagerSingleton::instance().GetCurrentTask(); + auto& tm = TaskManagerSingleton::instance(); + auto* current_task = tm.GetCurrentTask(); if (current_task == nullptr) { klog::Err("Mutex::Lock: Cannot lock mutex '{}' outside task context", name); return std::unexpected(Error{ErrorCode::kMutexNoTaskContext}); @@ -23,19 +24,51 @@ auto Mutex::Lock() -> Expected { return std::unexpected(Error{ErrorCode::kMutexRecursiveLock}); } + // 快速路径:尝试立即获取 bool expected = false; - while (!locked_.compare_exchange_weak( - expected, true, std::memory_order_acquire, std::memory_order_relaxed)) { - klog::Debug("Mutex::Lock: Task {} blocking on mutex '{}'", current_pid, - name); - TaskManagerSingleton::instance().Block(resource_id_); + 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::Lock: Task {} acquired mutex '{}'", current_pid, name); + return {}; + } + + // 慢路径:在调度器锁保护下进行 re-check + block,防止丢失唤醒 + // @todo: 当前实现在高竞争下可能不公平——被唤醒的任务的 CAS 可能被快速路径 + // 的新到达者抢占,导致被唤醒任务重新 Block。考虑实现 handoff 模式: + // UnLock 直接将 owner_ 转移给被唤醒任务,避免竞争。 + while (true) { + { + auto& cpu_sched = tm.GetCurrentCpuSched(); + LockGuard lock_guard(cpu_sched.lock); + + 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::Lock: Task {} acquired mutex '{}' (re-check)", + current_pid, name); + return {}; + } + + klog::Debug("Mutex::Lock: Task {} blocking on mutex '{}'", current_pid, + name); + tm.Block(cpu_sched, resource_id_); + } + + tm.Schedule(); 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::Lock: Task {} acquired mutex '{}' (after wake)", + current_pid, name); + return {}; + } } - - owner_.store(current_pid, std::memory_order_release); - klog::Debug("Mutex::Lock: Task {} acquired mutex '{}'", current_pid, name); - return {}; } auto Mutex::UnLock() -> Expected { @@ -60,7 +93,7 @@ auto Mutex::UnLock() -> Expected { klog::Debug("Mutex::UnLock: Task {} released mutex '{}'", current_pid, name); - TaskManagerSingleton::instance().Wakeup(resource_id_); + TaskManagerSingleton::instance().WakeupOne(resource_id_); return {}; } diff --git a/src/task/schedule.cpp b/src/task/schedule.cpp index 52319bfde..fd3ca83e5 100644 --- a/src/task/schedule.cpp +++ b/src/task/schedule.cpp @@ -23,9 +23,18 @@ #include "task_messages.hpp" #include "virtual_memory.hpp" +extern "C" [[noreturn]] void kernel_thread_bootstrap(void (*entry)(void*), + void* arg) { + cpu_io::EnableInterrupt(); + entry(arg); + assert(false && "kernel thread returned without calling sys_exit()"); + while (true) { + cpu_io::Pause(); + } +} + auto TaskManager::Schedule() -> void { auto& cpu_sched = GetCurrentCpuSched(); - cpu_sched.scheduler_started = true; cpu_sched.lock.Lock().or_else([](auto&& err) { klog::Err("Schedule: Failed to acquire lock: {}", err.message()); while (true) { @@ -34,6 +43,8 @@ auto TaskManager::Schedule() -> void { return Expected{}; }); + cpu_sched.scheduler_started = true; + auto* current = GetCurrentTask(); assert(current != nullptr && "Schedule: No current task to schedule"); diff --git a/src/task/signal.cpp b/src/task/signal.cpp new file mode 100644 index 000000000..40184d0fc --- /dev/null +++ b/src/task/signal.cpp @@ -0,0 +1,228 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "signal.hpp" + +#include + +#include "kernel_log.hpp" +#include "task_manager.hpp" +#include "task_messages.hpp" + +auto TaskManager::SendSignal(Pid pid, int signum) -> Expected { + if (!SignalState::IsValid(signum)) { + return std::unexpected(Error{ErrorCode::kSignalInvalidNumber}); + } + + // 查找目标任务 + TaskControlBlock* target = nullptr; + { + LockGuard lock_guard(task_table_lock_); + auto it = task_table_.find(pid); + if (it == task_table_.end()) { + return std::unexpected(Error{ErrorCode::kSignalTaskNotFound}); + } + target = it->second.get(); + } + + if (!target || !target->aux) { + return std::unexpected(Error{ErrorCode::kSignalTaskNotFound}); + } + + target->aux->signals.SetPending(signum); + + klog::Debug("SendSignal: signal {} ({}) sent to pid={}", + SignalState::GetSignalName(signum), signum, pid); + + // SIGKILL 和 SIGCONT 需要立即唤醒阻塞/睡眠的任务 + bool needs_wake = + (signum == signal_number::kSigKill || signum == signal_number::kSigCont); + + // 如果信号未被屏蔽,也需要唤醒 + if (!needs_wake && + !(target->aux->signals.blocked.load(std::memory_order_acquire) & + (1U << signum))) { + auto status = target->GetStatus(); + if (status == TaskStatus::kBlocked || status == TaskStatus::kSleeping) { + needs_wake = true; + } + } + + if (needs_wake) { + auto status = target->GetStatus(); + + if (status == TaskStatus::kBlocked) { + auto resource_id = target->aux->blocked_on; + if (static_cast(resource_id)) { + Wakeup(resource_id); + klog::Debug("SendSignal: woke blocked task pid={} for signal {}", pid, + SignalState::GetSignalName(signum)); + } + } else if (status == TaskStatus::kSleeping) { + // Expedite wake: set wake_tick to 0 so next TickUpdate wakes it. + // Note: single aligned 64-bit store is atomic on 64-bit platforms. + target->sched_info.wake_tick = 0; + klog::Debug("SendSignal: expedited sleep wakeup for pid={}", pid); + } + } + + return {}; +} + +auto TaskManager::CheckPendingSignals() -> int { + auto* current = GetCurrentTask(); + if (!current || !current->aux) { + return 0; + } + + auto& signals = current->aux->signals; + if (!signals.HasDeliverableSignal()) { + return 0; + } + + int signum = signals.GetNextDeliverableSignal(); + if (signum == signal_number::kSigNone) { + return 0; + } + + signals.ClearPending(signum); + + klog::Debug("CheckPendingSignals: pid={} delivering signal {} ({})", + current->pid, signum, SignalState::GetSignalName(signum)); + + auto& action = signals.actions[signum]; + + // SIGKILL: 强制终止,无法捕获 + if (signum == signal_number::kSigKill) { + klog::Info("CheckPendingSignals: pid={} killed by SIGKILL", current->pid); + Exit(128 + signum); + __builtin_unreachable(); + } + + // SIGSTOP: 强制停止,无法捕获 + /// @todo 实现进程停止状态 (需要新的 FSM 状态 kStopped) + if (signum == signal_number::kSigStop || signum == signal_number::kSigTstp) { + klog::Info( + "CheckPendingSignals: pid={} received stop signal {} (not implemented)", + current->pid, SignalState::GetSignalName(signum)); + return signum; + } + + // SIGCONT: 继续(如果已停止) + if (signum == signal_number::kSigCont) { + klog::Debug("CheckPendingSignals: pid={} received SIGCONT", current->pid); + return signum; + } + + if (action.handler == kSigIgn) { + klog::Debug("CheckPendingSignals: pid={} ignored signal {}", current->pid, + SignalState::GetSignalName(signum)); + return signum; + } + + if (action.handler == kSigDfl) { + char default_action = GetDefaultSignalAction(signum); + switch (default_action) { + case 'T': + case 'C': + klog::Info( + "CheckPendingSignals: pid={} terminated by signal {} (default)", + current->pid, SignalState::GetSignalName(signum)); + Exit(128 + signum); + __builtin_unreachable(); + case 'I': + break; + case 'S': + klog::Debug("CheckPendingSignals: pid={} stop by {} (not implemented)", + current->pid, SignalState::GetSignalName(signum)); + break; + case 'K': + break; + default: + break; + } + return signum; + } + + /// @todo 实现用户空间信号投递: + /// 1. 保存当前 trap 上下文 + /// 2. 修改 trap 上下文的 PC 指向 handler + /// 3. 在用户栈上构造 sigreturn 蹦床 + /// 4. 返回用户空间执行 handler + /// 5. handler 返回时通过 sigreturn 恢复原始上下文 + klog::Debug( + "CheckPendingSignals: pid={} user handler for signal {} (delivery " + "not yet implemented)", + current->pid, SignalState::GetSignalName(signum)); + return signum; +} + +auto TaskManager::SetSignalAction(int signum, const SignalAction& action, + SignalAction* old_action) -> Expected { + auto* current = GetCurrentTask(); + if (!current || !current->aux) { + return std::unexpected(Error{ErrorCode::kTaskNoCurrentTask}); + } + + if (!SignalState::IsValid(signum)) { + return std::unexpected(Error{ErrorCode::kSignalInvalidNumber}); + } + + if (SignalState::IsUncatchable(signum)) { + return std::unexpected(Error{ErrorCode::kSignalUncatchable}); + } + + auto& signals = current->aux->signals; + + if (old_action) { + *old_action = signals.actions[signum]; + } + + signals.actions[signum] = action; + + klog::Debug("SetSignalAction: pid={} set handler for signal {} ({})", + current->pid, signum, SignalState::GetSignalName(signum)); + + return {}; +} + +auto TaskManager::SetSignalMask(int how, uint32_t set, uint32_t* oldset) + -> Expected { + auto* current = GetCurrentTask(); + if (!current || !current->aux) { + return std::unexpected(Error{ErrorCode::kTaskNoCurrentTask}); + } + + auto& signals = current->aux->signals; + + if (oldset) { + *oldset = signals.blocked.load(std::memory_order_acquire); + } + + // SIGKILL 和 SIGSTOP 不能被屏蔽 + uint32_t uncatchable_mask = + (1U << signal_number::kSigKill) | (1U << signal_number::kSigStop); + set &= ~uncatchable_mask; + + switch (how) { + case signal_mask_op::kSigBlock: + signals.blocked.fetch_or(set, std::memory_order_release); + break; + case signal_mask_op::kSigUnblock: + signals.blocked.fetch_and(~set, std::memory_order_release); + break; + case signal_mask_op::kSigSetmask: + signals.blocked.store(set, std::memory_order_release); + break; + default: + return std::unexpected(Error{ErrorCode::kInvalidArgument}); + } + + signals.blocked.fetch_and(~uncatchable_mask, std::memory_order_release); + + klog::Debug("SetSignalMask: pid={} mask={:#x}", current->pid, + signals.blocked.load(std::memory_order_acquire)); + + return {}; +} diff --git a/src/task/sleep.cpp b/src/task/sleep.cpp index b03a5fd4b..5cc574a6f 100644 --- a/src/task/sleep.cpp +++ b/src/task/sleep.cpp @@ -48,4 +48,6 @@ auto TaskManager::Sleep(uint64_t ms) -> void { Schedule(); // 任务被唤醒后会从这里继续执行 + // 检查是否有待处理的信号(可能是信号导致了提前唤醒) + (void)CheckPendingSignals(); } diff --git a/src/task/wait.cpp b/src/task/wait.cpp index 498589e8d..cb5f8996a 100644 --- a/src/task/wait.cpp +++ b/src/task/wait.cpp @@ -19,28 +19,26 @@ auto TaskManager::Wait(Pid pid, int* status, bool no_hang, bool untraced) while (true) { TaskControlBlock* target = nullptr; + bool did_block = false; + bool has_matching_child = false; { - LockGuard lock_guard(task_table_lock_); + // 锁顺序: cpu_sched.lock → task_table_lock_(与 Exit 一致) + auto& cpu_sched = GetCurrentCpuSched(); + LockGuard sched_guard(cpu_sched.lock); + LockGuard table_guard(task_table_lock_); - // 遍历任务表寻找符合条件的子进程 for (auto& [task_pid, task] : task_table_) { - // 检查是否是当前进程的子进程 bool is_child = (task->aux->parent_pid == current->pid); - // 检查 PID 匹配条件 bool pid_match = false; if (pid == static_cast(-1)) { - // 等待任意子进程 pid_match = is_child; } else if (pid == 0) { - // 等待同进程组的任意子进程 pid_match = is_child && (task->aux->pgid == current->aux->pgid); } else if (pid > 0) { - // 等待指定 PID 的子进程 pid_match = is_child && (task->pid == pid); } else { - // pid < -1: 等待进程组 ID 为 |pid| 的任意子进程 pid_match = is_child && (task->aux->pgid == static_cast(-pid)); } @@ -48,66 +46,60 @@ auto TaskManager::Wait(Pid pid, int* status, bool no_hang, bool untraced) continue; } - // 检查任务状态 + has_matching_child = true; + if (task->GetStatus() == TaskStatus::kZombie || task->GetStatus() == TaskStatus::kExited) { target = task.get(); break; } - // untraced: 报告已停止的子进程 if (untraced && task->GetStatus() == TaskStatus::kBlocked) { if (status) { - // 表示停止状态 *status = 0; } return task->pid; } } + + // 没有匹配的子进程,返回错误(类似 POSIX ECHILD) + if (!has_matching_child) { + return std::unexpected(Error{ErrorCode::kTaskNoChildFound}); + } + + // 原子性:在持有两把锁的情况下检查并阻塞,防止丢失唤醒 + if (!target && !no_hang) { + auto wait_resource_id = + ResourceId(ResourceType::kChildExit, current->pid); + Block(cpu_sched, wait_resource_id); + did_block = true; + } } - // 找到了退出的子进程 if (target) { assert((target->GetStatus() == TaskStatus::kZombie || target->GetStatus() == TaskStatus::kExited) && "Wait: target task must be kZombie or kExited"); - assert(target->aux->parent_pid == current->pid && - "Wait: target parent_pid must match current pid"); Pid result_pid = target->pid; - // 返回退出状态 if (status) { *status = target->aux->exit_code; } - // 清理僵尸进程 { - LockGuard lock_guard(task_table_lock_); - auto it = task_table_.find(target->pid); - assert(it != task_table_.end() && - "Wait: target must exist in task_table"); - task_table_.erase(it->first); + LockGuard lock_guard(task_table_lock_); + task_table_.erase(target->pid); } klog::Debug("Wait: pid={} reaped child={}", current->pid, result_pid); return result_pid; } - // 如果设置了 no_hang,立即返回 if (no_hang) { return 0; } - // 没有找到符合条件的子进程,阻塞等待 - // 使用 ChildExit 类型的资源 ID,数据部分是当前进程的 PID - auto wait_resource_id = ResourceId(ResourceType::kChildExit, current->pid); - - Block(wait_resource_id); - - klog::Debug("Wait: pid={} blocked on resource={}, data={}", current->pid, - wait_resource_id.GetTypeName(), - static_cast(wait_resource_id.GetData())); - // 被唤醒后重新检查 + Schedule(); } } diff --git a/src/task/wakeup.cpp b/src/task/wakeup.cpp index 36348bd9d..ee9dc97ee 100644 --- a/src/task/wakeup.cpp +++ b/src/task/wakeup.cpp @@ -9,23 +9,17 @@ #include "task_manager.hpp" #include "task_messages.hpp" -auto TaskManager::Wakeup(ResourceId resource_id) -> void { - auto& cpu_sched = GetCurrentCpuSched(); - - LockGuard lock_guard(cpu_sched.lock); - - // 查找等待该资源的任务列表 +auto TaskManager::Wakeup(CpuSchedData& cpu_sched, ResourceId resource_id) + -> void { auto it = cpu_sched.blocked_tasks.find(resource_id); if (it == cpu_sched.blocked_tasks.end()) { - // 没有任务等待该资源 klog::Debug("Wakeup: No tasks waiting on resource={}, data={:#x}", resource_id.GetTypeName(), static_cast(resource_id.GetData())); return; } - // 唤醒所有等待该资源的任务 auto& waiting_tasks = it->second; size_t wakeup_count = 0; @@ -38,11 +32,9 @@ auto TaskManager::Wakeup(ResourceId resource_id) -> void { assert(task->aux->blocked_on == resource_id && "Wakeup: task blocked_on must match resource_id"); - // 将任务标记为就绪 task->fsm.Receive(MsgWakeup{}); task->aux->blocked_on = ResourceId{}; - // 将任务重新加入对应调度器的就绪队列 auto* scheduler = cpu_sched.schedulers[static_cast(task->policy)].get(); assert(scheduler != nullptr && "Wakeup: scheduler must not be null"); @@ -50,10 +42,61 @@ auto TaskManager::Wakeup(ResourceId resource_id) -> void { wakeup_count++; } - // 移除空的资源队列 cpu_sched.blocked_tasks.erase(resource_id); klog::Debug("Wakeup: Woke up {} tasks from resource={}, data={:#x}", wakeup_count, resource_id.GetTypeName(), static_cast(resource_id.GetData())); } + +auto TaskManager::Wakeup(ResourceId resource_id) -> void { + for (size_t i = 0; i < SIMPLEKERNEL_MAX_CORE_COUNT; ++i) { + auto& cpu_sched = cpu_schedulers_[i]; + LockGuard lock_guard(cpu_sched.lock); + Wakeup(cpu_sched, resource_id); + } +} + +auto TaskManager::WakeupOne(ResourceId resource_id) -> void { + for (size_t i = 0; i < SIMPLEKERNEL_MAX_CORE_COUNT; ++i) { + auto& cpu_sched = cpu_schedulers_[i]; + LockGuard lock_guard(cpu_sched.lock); + + auto it = cpu_sched.blocked_tasks.find(resource_id); + if (it == cpu_sched.blocked_tasks.end()) { + continue; + } + + auto& waiting_tasks = it->second; + if (waiting_tasks.empty()) { + continue; + } + + auto* task = waiting_tasks.front(); + waiting_tasks.pop_front(); + + assert(task->GetStatus() == TaskStatus::kBlocked && + "WakeupOne: task status must be kBlocked"); + + task->fsm.Receive(MsgWakeup{}); + task->aux->blocked_on = ResourceId{}; + + auto* scheduler = + cpu_sched.schedulers[static_cast(task->policy)].get(); + assert(scheduler != nullptr && "WakeupOne: scheduler must not be null"); + scheduler->Enqueue(task); + + if (waiting_tasks.empty()) { + cpu_sched.blocked_tasks.erase(resource_id); + } + + klog::Debug("WakeupOne: Woke task pid={} from resource={}, data={:#x}", + task->pid, resource_id.GetTypeName(), + static_cast(resource_id.GetData())); + return; // Only wake one + } + + klog::Debug("WakeupOne: No tasks waiting on resource={}, data={:#x}", + resource_id.GetTypeName(), + static_cast(resource_id.GetData())); +} diff --git a/tests/AGENTS.md b/tests/AGENTS.md index ca55e30ba..7cd921a19 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -33,7 +33,7 @@ integration_test/ - **Adding system tests** → Create `tests/system_test/{module}_test.cpp`, register in main.cpp - **Mock data** → `unit_test/mocks/` for stub implementations needed by host-only tests - **Test fixtures** → `unit_test/task/` for `TaskTestHarness` scheduler test setup -- **Run unit tests** → `cd build_x86_64 && make unit-test` +- **Run unit tests** → `cd build_{arch} && make unit-test` (host architecture only) - **Run system tests** → `cd build_{arch} && make run` (system tests embedded in kernel) ## CONVENTIONS diff --git a/tests/integration_test/CMakeLists.txt b/tests/integration_test/CMakeLists.txt index fb7714070..322c18367 100644 --- a/tests/integration_test/CMakeLists.txt +++ b/tests/integration_test/CMakeLists.txt @@ -4,11 +4,7 @@ LIST (APPEND ${PROJECT_NAME}_QEMU_BOOT_FLAGS "-D;${CMAKE_BINARY_DIR}/bin/${PROJECT_NAME}_qemu.log") # 目标平台参数 -IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - LIST (APPEND ${PROJECT_NAME}_QEMU_BOOT_FLAGS -drive - file=fat:rw:${CMAKE_BINARY_DIR}/bin,format=raw,media=disk -bios - ${u-boot_BINARY_DIR}/u-boot.rom) -ELSEIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") +IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") LIST (APPEND ${PROJECT_NAME}_QEMU_BOOT_FLAGS -bios ${u-boot_BINARY_DIR}/spl/u-boot-spl.bin -device loader,file=${u-boot_BINARY_DIR}/u-boot.itb,addr=0x80200000) @@ -28,10 +24,6 @@ ELSEIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") $) ENDIF() -IF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") - -ELSEIF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "riscv64") +IF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "riscv64") ADD_SUBDIRECTORY (opensbi_test) -ELSEIF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64") - ENDIF() diff --git a/tests/integration_test/aarch64_minimal/main.cpp b/tests/integration_test/aarch64_minimal/main.cpp index ba5ba2d8a..53ad12ed4 100644 --- a/tests/integration_test/aarch64_minimal/main.cpp +++ b/tests/integration_test/aarch64_minimal/main.cpp @@ -10,16 +10,6 @@ extern "C" { volatile uint8_t* uart = (uint8_t*)0x9000000; void putc(char character) { *uart = character; } -void SetupFpu() { - __asm__ volatile( - "mov X0, #0x00300000\n" - "msr CPACR_EL1, x0\n" - "isb\n" - : - : - :); -} - struct param { uint64_t a; uint64_t b; @@ -31,7 +21,6 @@ struct param { void callee([[maybe_unused]] param var) { putc('e'); } void main() { - SetupFpu(); putc('0'); callee({}); putc('1'); diff --git a/tests/system_test/CMakeLists.txt b/tests/system_test/CMakeLists.txt index 0d74da6d3..229eb22cd 100644 --- a/tests/system_test/CMakeLists.txt +++ b/tests/system_test/CMakeLists.txt @@ -18,14 +18,22 @@ ADD_EXECUTABLE ( rr_scheduler_test.cpp cfs_scheduler_test.cpp idle_scheduler_test.cpp - thread_group_system_test.cpp - wait_system_test.cpp - clone_system_test.cpp - exit_system_test.cpp - ramfs_system_test.cpp - fatfs_system_test.cpp + thread_group_test.cpp + wait_test.cpp + clone_test.cpp + exit_test.cpp + ramfs_test.cpp + fatfs_test.cpp kernel_task_test.cpp user_task_test.cpp + cross_core_test.cpp + yield_test.cpp + fork_test.cpp + signal_test.cpp + affinity_test.cpp + tick_test.cpp + zombie_reap_test.cpp + stress_test.cpp ${CMAKE_SOURCE_DIR}/src/syscall.cpp ${CMAKE_SOURCE_DIR}/src/io_buffer.cpp) @@ -49,17 +57,7 @@ TARGET_LINK_LIBRARIES ( LIST (APPEND ${PROJECT_NAME}_QEMU_BOOT_FLAGS "-D;${CMAKE_BINARY_DIR}/bin/${PROJECT_NAME}_qemu.log") # 目标平台参数 -IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - LIST ( - APPEND - ${PROJECT_NAME}_QEMU_BOOT_FLAGS - -drive - file=fat:rw:${CMAKE_BINARY_DIR}/bin,format=raw,media=disk - -bios - ${u-boot_BINARY_DIR}/u-boot.rom - -device - isa-debug-exit,iobase=0xf4,iosize=0x04) -ELSEIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") +IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") LIST (APPEND ${PROJECT_NAME}_QEMU_BOOT_FLAGS -bios ${u-boot_BINARY_DIR}/spl/u-boot-spl.bin -device loader,file=${u-boot_BINARY_DIR}/u-boot.itb,addr=0x80200000) diff --git a/tests/system_test/affinity_test.cpp b/tests/system_test/affinity_test.cpp new file mode 100644 index 000000000..cbe09c969 --- /dev/null +++ b/tests/system_test/affinity_test.cpp @@ -0,0 +1,263 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include +#include + +#include "kernel.h" +#include "kernel_log.hpp" +#include "kstd_libcxx.h" +#include "kstd_memory" +#include "syscall.hpp" +#include "system_test.h" +#include "task_control_block.hpp" +#include "task_manager.hpp" + +namespace { + +std::atomic g_tests_completed{0}; +std::atomic g_tests_failed{0}; + +// =========================================================================== +// test_affinity_get_self +// +// Verify that the default affinity for the current task is UINT64_MAX. +// =========================================================================== + +void test_affinity_get_self(void* /*arg*/) { + klog::Info("=== Affinity Get Self Test ==="); + + bool passed = true; + uint64_t mask = 0; + + int ret = sys_sched_getaffinity(0, sizeof(uint64_t), &mask); + if (ret != 0) { + klog::Err("test_affinity_get_self: getaffinity returned {}, expected 0", + ret); + passed = false; + } + + if (passed && mask != UINT64_MAX) { + klog::Err("test_affinity_get_self: mask={:#x}, expected {:#x}", mask, + static_cast(UINT64_MAX)); + passed = false; + } + + if (passed) { + klog::Info("Affinity Get Self Test: PASSED"); + } else { + klog::Err("Affinity Get Self Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(passed ? 0 : 1); +} + +// =========================================================================== +// test_affinity_set_get +// +// Set affinity to 1UL, read back and verify, then restore to UINT64_MAX. +// =========================================================================== + +void test_affinity_set_get(void* /*arg*/) { + klog::Info("=== Affinity Set/Get Test ==="); + + bool passed = true; + + // Set affinity to CPU 0 only + uint64_t new_mask = 1UL; + int ret = sys_sched_setaffinity(0, sizeof(uint64_t), &new_mask); + if (ret != 0) { + klog::Err("test_affinity_set_get: setaffinity returned {}, expected 0", + ret); + passed = false; + } + + // Read back and verify + uint64_t read_mask = 0; + if (passed) { + ret = sys_sched_getaffinity(0, sizeof(uint64_t), &read_mask); + if (ret != 0) { + klog::Err("test_affinity_set_get: getaffinity returned {}, expected 0", + ret); + passed = false; + } + } + + if (passed && read_mask != 1UL) { + klog::Err("test_affinity_set_get: read_mask={:#x}, expected 0x1", + read_mask); + passed = false; + } + + // Restore default affinity + uint64_t all_mask = UINT64_MAX; + (void)sys_sched_setaffinity(0, sizeof(uint64_t), &all_mask); + + if (passed) { + klog::Info("Affinity Set/Get Test: PASSED"); + } else { + klog::Err("Affinity Set/Get Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(passed ? 0 : 1); +} + +// =========================================================================== +// test_affinity_other_task +// +// Spawn a sleeping task and read its affinity by PID — expect UINT64_MAX. +// =========================================================================== + +std::atomic g_sleeper_pid{0}; + +void sleeper_work(void* /*arg*/) { + auto* current = TaskManagerSingleton::instance().GetCurrentTask(); + g_sleeper_pid.store(current->pid); + (void)sys_sleep(5000); + sys_exit(0); +} + +void test_affinity_other_task(void* /*arg*/) { + klog::Info("=== Affinity Other Task Test ==="); + + bool passed = true; + g_sleeper_pid = 0; + + // Spawn a sleeping task + auto sleeper = kstd::make_unique("AffinitySleeper", 10, + sleeper_work, nullptr); + TaskManagerSingleton::instance().AddTask(std::move(sleeper)); + + // Wait for the sleeper to start and publish its PID + int timeout = 100; + while (timeout > 0 && g_sleeper_pid.load() == 0) { + (void)sys_sleep(10); + timeout--; + } + + Pid other_pid = g_sleeper_pid.load(); + if (other_pid == 0) { + klog::Err("test_affinity_other_task: sleeper did not start"); + passed = false; + } + + // Read the sleeper's affinity by PID + uint64_t mask = 0; + if (passed) { + int ret = sys_sched_getaffinity(static_cast(other_pid), + sizeof(uint64_t), &mask); + if (ret != 0) { + klog::Err( + "test_affinity_other_task: getaffinity(pid={}) returned {}, " + "expected 0", + other_pid, ret); + passed = false; + } + } + + if (passed && mask != UINT64_MAX) { + klog::Err("test_affinity_other_task: mask={:#x}, expected {:#x}", mask, + static_cast(UINT64_MAX)); + passed = false; + } + + if (passed) { + klog::Info("Affinity Other Task Test: PASSED"); + } else { + klog::Err("Affinity Other Task Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(passed ? 0 : 1); +} + +// =========================================================================== +// test_affinity_errors +// +// Verify error returns for invalid PID and too-small cpusetsize. +// =========================================================================== + +void test_affinity_errors(void* /*arg*/) { + klog::Info("=== Affinity Errors Test ==="); + + bool passed = true; + uint64_t mask = 0; + + // Invalid PID should return -1 + int ret = sys_sched_getaffinity(99999, sizeof(uint64_t), &mask); + if (ret != -1) { + klog::Err( + "test_affinity_errors: getaffinity(pid=99999) returned {}, " + "expected -1", + ret); + passed = false; + } + + // Too-small cpusetsize should return -1 + ret = sys_sched_getaffinity(0, 1, &mask); + if (ret != -1) { + klog::Err( + "test_affinity_errors: getaffinity(cpusetsize=1) returned {}, " + "expected -1", + ret); + passed = false; + } + + if (passed) { + klog::Info("Affinity Errors Test: PASSED"); + } else { + klog::Err("Affinity Errors Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(passed ? 0 : 1); +} + +} // namespace + +auto affinity_test() -> bool { + klog::Info("===== Affinity System Test Start ====="); + + g_tests_completed = 0; + g_tests_failed = 0; + + auto& task_mgr = TaskManagerSingleton::instance(); + + auto test1 = kstd::make_unique( + "TestAffinityGetSelf", 10, test_affinity_get_self, nullptr); + task_mgr.AddTask(std::move(test1)); + + auto test2 = kstd::make_unique( + "TestAffinitySetGet", 10, test_affinity_set_get, nullptr); + task_mgr.AddTask(std::move(test2)); + + auto test3 = kstd::make_unique( + "TestAffinityOther", 10, test_affinity_other_task, nullptr); + task_mgr.AddTask(std::move(test3)); + + auto test4 = kstd::make_unique( + "TestAffinityErrors", 10, test_affinity_errors, nullptr); + task_mgr.AddTask(std::move(test4)); + + int timeout = 200; + while (timeout > 0) { + (void)sys_sleep(50); + if (g_tests_completed >= 4) { + break; + } + timeout--; + } + + EXPECT_EQ(g_tests_completed, 4, "tests completed"); + EXPECT_EQ(g_tests_failed, 0, "tests failed"); + + klog::Info("Affinity System Test Suite: COMPLETED"); + return true; +} diff --git a/tests/system_test/clone_system_test.cpp b/tests/system_test/clone_test.cpp similarity index 95% rename from tests/system_test/clone_system_test.cpp rename to tests/system_test/clone_test.cpp index 000bb76c1..9f9cd2458 100644 --- a/tests/system_test/clone_system_test.cpp +++ b/tests/system_test/clone_test.cpp @@ -106,18 +106,23 @@ void test_clone_process(void* /*arg*/) { g_child2_tgid = 0; g_child2_parent_pid = 0; - auto* self = TaskManagerSingleton::instance().GetCurrentTask(); + auto& task_mgr = TaskManagerSingleton::instance(); + auto* self = task_mgr.GetCurrentTask(); Pid my_pid = self->pid; auto child1 = kstd::make_unique( "CloneChild1", 10, child_process_work, reinterpret_cast(1)); child1->aux->parent_pid = my_pid; - TaskManagerSingleton::instance().AddTask(std::move(child1)); + auto* child1_raw = child1.get(); + task_mgr.AddTask(std::move(child1)); + Pid child1_pid = child1_raw->pid; auto child2 = kstd::make_unique( "CloneChild2", 10, child_process_work, reinterpret_cast(2)); child2->aux->parent_pid = my_pid; - TaskManagerSingleton::instance().AddTask(std::move(child2)); + auto* child2_raw = child2.get(); + task_mgr.AddTask(std::move(child2)); + Pid child2_pid = child2_raw->pid; (void)sys_sleep(200); @@ -148,6 +153,11 @@ void test_clone_process(void* /*arg*/) { passed = false; } + // 回收子进程,避免僵尸被过继给 runner 导致 collected 计数错误 + int status = 0; + (void)task_mgr.Wait(child1_pid, &status, false, false); + (void)task_mgr.Wait(child2_pid, &status, false, false); + if (passed) { klog::Info("Clone Process Test: PASSED"); } else { @@ -422,7 +432,7 @@ void test_clone_flags_auto_completion(void* /*arg*/) { } // namespace -auto clone_system_test() -> bool { +auto clone_test() -> bool { klog::Info("===== Clone System Test Start ====="); g_tests_completed = 0; diff --git a/tests/system_test/cross_core_test.cpp b/tests/system_test/cross_core_test.cpp new file mode 100644 index 000000000..f239dd6d3 --- /dev/null +++ b/tests/system_test/cross_core_test.cpp @@ -0,0 +1,354 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include +#include +#include + +#include "basic_info.hpp" +#include "kernel.h" +#include "kstd_memory" +#include "mutex.hpp" +#include "per_cpu.hpp" +#include "syscall.hpp" +#include "system_test.h" +#include "task_control_block.hpp" +#include "task_manager.hpp" + +namespace { + +std::atomic g_tests_completed{0}; +std::atomic g_tests_failed{0}; + +// =========================================================================== +// test_cross_core_wakeup +// +// Two tasks pinned to different cores share a Mutex. +// Task A (core 0) acquires it, sleeps 200ms, releases. +// Task B (core 1) blocks on Lock(), wakes up when A releases. +// Verify B acquired the mutex after A released it. +// =========================================================================== + +struct WakeupArgs { + Mutex* mtx; + std::atomic* phase; // 0:init, 1:A locked, 2:A unlocked, 3:B locked +}; + +void wakeup_task_a(void* arg) { + auto* ctx = reinterpret_cast(arg); + + (void)ctx->mtx->Lock(); + ctx->phase->store(1, std::memory_order_release); + klog::Info("[CrossCore Wakeup] A: locked mutex, sleeping 200ms"); + + (void)sys_sleep(200); + + (void)ctx->mtx->UnLock(); + ctx->phase->store(2, std::memory_order_release); + klog::Info("[CrossCore Wakeup] A: unlocked mutex"); + + sys_exit(0); +} + +void wakeup_task_b(void* arg) { + auto* ctx = reinterpret_cast(arg); + + // Wait until A has locked the mutex before trying + int timeout = 100; + while (timeout > 0 && ctx->phase->load(std::memory_order_acquire) < 1) { + (void)sys_sleep(10); + timeout--; + } + + klog::Info("[CrossCore Wakeup] B: attempting Lock() (should block)"); + (void)ctx->mtx->Lock(); + + // By the time B gets here, A must have released (phase >= 2) + int phase_val = ctx->phase->load(std::memory_order_acquire); + if (phase_val < 2) { + klog::Err( + "[CrossCore Wakeup] B: acquired mutex before A released! " + "phase={}", + phase_val); + } + ctx->phase->store(3, std::memory_order_release); + klog::Info("[CrossCore Wakeup] B: locked mutex after A released"); + + (void)ctx->mtx->UnLock(); + sys_exit(0); +} + +void test_cross_core_wakeup(void* /*arg*/) { + klog::Info("=== Cross-Core Wakeup Test ==="); + + Mutex mtx("cc_wakeup"); + std::atomic phase{0}; + bool passed = true; + + WakeupArgs ctx; + ctx.mtx = &mtx; + ctx.phase = &phase; + + auto task_a = kstd::make_unique( + "CCWakeupA", 10, wakeup_task_a, reinterpret_cast(&ctx)); + task_a->aux->cpu_affinity = (1UL << 0); + + auto task_b = kstd::make_unique( + "CCWakeupB", 10, wakeup_task_b, reinterpret_cast(&ctx)); + task_b->aux->cpu_affinity = (1UL << 1); + + TaskManagerSingleton::instance().AddTask(std::move(task_a)); + TaskManagerSingleton::instance().AddTask(std::move(task_b)); + + // Wait for B to finish (phase == 3) with timeout + int timeout = 200; + while (timeout > 0 && phase.load(std::memory_order_acquire) < 3) { + (void)sys_sleep(50); + timeout--; + } + + if (phase.load(std::memory_order_acquire) != 3) { + klog::Err("test_cross_core_wakeup: FAIL — phase={}, expected 3", + phase.load()); + passed = false; + } + + if (passed) { + klog::Info("Cross-Core Wakeup Test: PASSED"); + } else { + klog::Err("Cross-Core Wakeup Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(0); +} + +// =========================================================================== +// test_cross_core_exit_wait +// +// Parent on core 0 calls Wait(-1, &status). +// Child on core 1 does work, then sys_exit(42). +// Verify parent collects correct exit code. +// =========================================================================== + +struct ExitWaitArgs { + std::atomic* result_code; + std::atomic* result_ready; +}; + +void exit_wait_child(void* /*arg*/) { + klog::Info("[CrossCore ExitWait] Child: doing work on core 1"); + (void)sys_sleep(100); + klog::Info("[CrossCore ExitWait] Child: calling sys_exit(42)"); + sys_exit(42); +} + +void exit_wait_parent(void* arg) { + auto* ctx = reinterpret_cast(arg); + + auto& task_mgr = TaskManagerSingleton::instance(); + auto* current = task_mgr.GetCurrentTask(); + + // Create child pinned to core 1 + auto child = kstd::make_unique("CCExitChild", 10, + exit_wait_child, nullptr); + child->aux->parent_pid = current->pid; + child->aux->pgid = current->aux->pgid; + child->aux->cpu_affinity = (1UL << 1); + + // Save raw pointer: pid is assigned inside AddTask() by AllocatePid(), + // so we must read it *after* the call (unique_ptr is moved). + auto* child_ptr = child.get(); + task_mgr.AddTask(std::move(child)); + Pid child_pid = child_ptr->pid; + + klog::Info( + "[CrossCore ExitWait] Parent: created child pid={}, calling " + "Wait()", + child_pid); + + int status = 0; + auto wait_result = task_mgr.Wait(static_cast(-1), &status, false, false); + + if (wait_result.has_value() && wait_result.value() == child_pid) { + ctx->result_code->store(status, std::memory_order_release); + klog::Info("[CrossCore ExitWait] Parent: child exited with code {}", + status); + } else { + ctx->result_code->store(-1, std::memory_order_release); + klog::Err("[CrossCore ExitWait] Parent: Wait() failed or wrong pid"); + } + + ctx->result_ready->store(1, std::memory_order_release); + sys_exit(0); +} + +void test_cross_core_exit_wait(void* /*arg*/) { + klog::Info("=== Cross-Core Exit/Wait Test ==="); + + std::atomic result_code{-1}; + std::atomic result_ready{0}; + bool passed = true; + + ExitWaitArgs ctx; + ctx.result_code = &result_code; + ctx.result_ready = &result_ready; + + auto parent_task = kstd::make_unique( + "CCExitParent", 10, exit_wait_parent, reinterpret_cast(&ctx)); + parent_task->aux->cpu_affinity = (1UL << 0); + + TaskManagerSingleton::instance().AddTask(std::move(parent_task)); + + // Wait for parent to finish + int timeout = 200; + while (timeout > 0 && result_ready.load(std::memory_order_acquire) == 0) { + (void)sys_sleep(50); + timeout--; + } + + if (result_ready.load(std::memory_order_acquire) != 1) { + klog::Err("test_cross_core_exit_wait: FAIL — timed out"); + passed = false; + } else if (result_code.load(std::memory_order_acquire) != 42) { + klog::Err("test_cross_core_exit_wait: FAIL — exit_code={}, expected 42", + result_code.load()); + passed = false; + } + + if (passed) { + klog::Info("Cross-Core Exit/Wait Test: PASSED"); + } else { + klog::Err("Cross-Core Exit/Wait Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(0); +} + +// =========================================================================== +// test_cross_core_mutex_counter +// +// 4 tasks (2 on core 0, 2 on core 1) each increment a shared counter +// 100 times under mutex protection. Final counter must be 400. +// =========================================================================== + +struct MutexCounterArgs { + Mutex* mtx; + std::atomic* counter; + int iterations; +}; + +void mutex_counter_worker(void* arg) { + auto* ctx = reinterpret_cast(arg); + + for (int i = 0; i < ctx->iterations; ++i) { + (void)ctx->mtx->Lock(); + int val = ctx->counter->load(std::memory_order_relaxed); + ctx->counter->store(val + 1, std::memory_order_relaxed); + (void)ctx->mtx->UnLock(); + } + + sys_exit(0); +} + +void test_cross_core_mutex_counter(void* /*arg*/) { + klog::Info("=== Cross-Core Mutex Counter Test ==="); + + Mutex mtx("cc_counter"); + std::atomic counter{0}; + bool passed = true; + + constexpr int kIterations = 100; + constexpr int kWorkers = 4; + constexpr int kExpectedTotal = kWorkers * kIterations; + + MutexCounterArgs ctx; + ctx.mtx = &mtx; + ctx.counter = &counter; + ctx.iterations = kIterations; + + // 2 workers on core 0, 2 on core 1 + for (int i = 0; i < kWorkers; ++i) { + uint64_t core = (i < 2) ? 0 : 1; + auto task = kstd::make_unique( + "CCCounterW", 10, mutex_counter_worker, reinterpret_cast(&ctx)); + task->aux->cpu_affinity = (1UL << core); + TaskManagerSingleton::instance().AddTask(std::move(task)); + } + + // Wait for counter to reach expected value + int timeout = 400; + while (timeout > 0 && + counter.load(std::memory_order_acquire) < kExpectedTotal) { + (void)sys_sleep(50); + timeout--; + } + + int final_count = counter.load(std::memory_order_acquire); + if (final_count != kExpectedTotal) { + klog::Err("test_cross_core_mutex_counter: FAIL — counter={}, expected {}", + final_count, kExpectedTotal); + passed = false; + } + + if (passed) { + klog::Info("Cross-Core Mutex Counter Test: PASSED"); + } else { + klog::Err("Cross-Core Mutex Counter Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(0); +} + +} // namespace + +auto cross_core_test() -> bool { + klog::Info("=== Cross-Core System Test Suite ==="); + + auto core_count = BasicInfoSingleton::instance().core_count; + if (core_count < 2) { + klog::Info("Skipping cross-core tests: need >= 2 cores, have {}", + core_count); + return true; + } + + g_tests_completed = 0; + g_tests_failed = 0; + + auto& task_mgr = TaskManagerSingleton::instance(); + + auto test1 = kstd::make_unique( + "TestCCWakeup", 10, test_cross_core_wakeup, nullptr); + task_mgr.AddTask(std::move(test1)); + + auto test2 = kstd::make_unique( + "TestCCExitWait", 10, test_cross_core_exit_wait, nullptr); + task_mgr.AddTask(std::move(test2)); + + auto test3 = kstd::make_unique( + "TestCCMutexCnt", 10, test_cross_core_mutex_counter, nullptr); + task_mgr.AddTask(std::move(test3)); + + constexpr int kExpectedTests = 3; + int timeout = 600; + while (timeout > 0) { + (void)sys_sleep(50); + if (g_tests_completed >= kExpectedTests) { + break; + } + timeout--; + } + + EXPECT_EQ(g_tests_completed.load(), kExpectedTests, + "All cross-core tests should complete"); + EXPECT_EQ(g_tests_failed.load(), 0, "No cross-core tests should fail"); + + klog::Info("Cross-Core System Test Suite: COMPLETED"); + return true; +} diff --git a/tests/system_test/ctor_dtor_test.cpp b/tests/system_test/ctor_dtor_test.cpp index 60421d2a9..1b6b97f66 100644 --- a/tests/system_test/ctor_dtor_test.cpp +++ b/tests/system_test/ctor_dtor_test.cpp @@ -61,10 +61,6 @@ class InsClass : public AbsClass { }; auto ctor_dtor_test() -> bool { -#ifdef __aarch64__ - cpu_io::SetupFpu(); -#endif - klog::Info("Running C++ Runtime Tests..."); // 1. Verify Global Initialization diff --git a/tests/system_test/exit_system_test.cpp b/tests/system_test/exit_test.cpp similarity index 93% rename from tests/system_test/exit_system_test.cpp rename to tests/system_test/exit_test.cpp index 52c4c0645..8aa562908 100644 --- a/tests/system_test/exit_system_test.cpp +++ b/tests/system_test/exit_test.cpp @@ -28,6 +28,10 @@ std::atomic g_exit_test_counter{0}; std::atomic g_tests_completed{0}; std::atomic g_tests_failed{0}; +/// Counter for locally-constructed TCBs not managed by TaskManager. +/// Starts at SIZE_MAX/2 to avoid collision with the global PID allocator. +std::atomic local_pid_counter{SIZE_MAX / 2}; + // --------------------------------------------------------------------------- // test_exit_normal // 测试: 创建带工作函数的任务,让其运行完毕后,验证 TCB 状态字段的语义正确性。 @@ -50,8 +54,8 @@ void test_exit_normal(void* /*arg*/) { // 1. 创建 TCB 并检查初始状态不是终止态 auto* task = new TaskControlBlock("ExitNormal", 10, nullptr, nullptr); - task->pid = 5000; - task->aux->tgid = 5000; + task->pid = local_pid_counter.fetch_add(1); + task->aux->tgid = task->pid; task->aux->parent_pid = 1; if (task->GetStatus() == TaskStatus::kExited || @@ -130,8 +134,8 @@ void test_exit_with_error(void* /*arg*/) { // 1. 创建 TCB,确认 exit_code 默认为 0 auto* task = new TaskControlBlock("ExitError", 10, nullptr, nullptr); - task->pid = 5001; - task->aux->tgid = 5001; + task->pid = local_pid_counter.fetch_add(1); + task->aux->tgid = task->pid; task->aux->parent_pid = 1; if (task->aux->exit_code != 0) { @@ -212,29 +216,25 @@ void test_thread_exit(void* /*arg*/) { g_exit_test_counter = 0; - // 创建线程组主线程 + // 创建线程组主线程(不加入调度,entry 为 nullptr) auto leader_uptr = kstd::make_unique("ThreadLeader", 10, nullptr, nullptr); - leader_uptr->pid = 5100; - leader_uptr->aux->tgid = 5100; - leader_uptr->aux->parent_pid = 1; auto* leader = leader_uptr.get(); - - TaskManagerSingleton::instance().AddTask(std::move(leader_uptr)); + leader->pid = local_pid_counter.fetch_add(1); + leader->aux->tgid = leader->pid; + leader->aux->parent_pid = 1; // 创建子线程 auto thread1 = kstd::make_unique( "Thread1", 10, child_thread_exit_work, reinterpret_cast(1)); - thread1->pid = 5101; - thread1->aux->tgid = 5100; + thread1->aux->tgid = leader->pid; thread1->JoinThreadGroup(leader); TaskManagerSingleton::instance().AddTask(std::move(thread1)); auto thread2 = kstd::make_unique( "Thread2", 10, child_thread_exit_work, reinterpret_cast(2)); - thread2->pid = 5102; - thread2->aux->tgid = 5100; + thread2->aux->tgid = leader->pid; thread2->JoinThreadGroup(leader); TaskManagerSingleton::instance().AddTask(std::move(thread2)); @@ -285,8 +285,8 @@ void test_orphan_exit(void* /*arg*/) { // 1. 创建孤儿 TCB,验证 parent_pid == 0 被正确存储 auto* orphan = new TaskControlBlock("Orphan", 10, nullptr, nullptr); - orphan->pid = 5200; - orphan->aux->tgid = 5200; + orphan->pid = local_pid_counter.fetch_add(1); + orphan->aux->tgid = orphan->pid; orphan->aux->parent_pid = 0; // 孤儿进程 if (orphan->aux->parent_pid != 0) { @@ -365,20 +365,18 @@ void test_zombie_process(void* /*arg*/) { bool passed = true; - // 1. 创建父子 TCB,验证 parent_pid 字段正确关联 + // 1. 创建父 TCB(不加入调度,entry 为 nullptr),验证 parent_pid 字段正确关联 auto parent_uptr = kstd::make_unique("Parent", 10, nullptr, nullptr); - parent_uptr->pid = 5300; - parent_uptr->aux->tgid = 5300; - parent_uptr->aux->parent_pid = 1; auto* parent = parent_uptr.get(); - - TaskManagerSingleton::instance().AddTask(std::move(parent_uptr)); + parent->pid = local_pid_counter.fetch_add(1); + parent->aux->tgid = parent->pid; + parent->aux->parent_pid = 1; auto* local_child = new TaskControlBlock("ZombieFsmTest", 10, nullptr, nullptr); - local_child->pid = 5301; - local_child->aux->tgid = 5301; + local_child->pid = local_pid_counter.fetch_add(1); + local_child->aux->tgid = local_child->pid; local_child->aux->parent_pid = parent->pid; if (local_child->aux->parent_pid != parent->pid) { @@ -417,7 +415,7 @@ void test_zombie_process(void* /*arg*/) { std::atomic work_flag{0}; auto real_child = kstd::make_unique( "RealChild", 10, child_work, reinterpret_cast(&work_flag)); - real_child->aux->parent_pid = 5300; // 指向 parent + real_child->aux->parent_pid = parent->pid; TaskManagerSingleton::instance().AddTask(std::move(real_child)); int timeout = 10; @@ -449,7 +447,7 @@ void test_zombie_process(void* /*arg*/) { /** * @brief Exit 系统测试入口 */ -auto exit_system_test() -> bool { +auto exit_test() -> bool { klog::Info("===== Exit System Test Start ====="); // 重置全局计数器 diff --git a/tests/system_test/fatfs_system_test.cpp b/tests/system_test/fatfs_test.cpp similarity index 60% rename from tests/system_test/fatfs_system_test.cpp rename to tests/system_test/fatfs_test.cpp index 3ab95af0d..f3a0b2141 100644 --- a/tests/system_test/fatfs_system_test.cpp +++ b/tests/system_test/fatfs_test.cpp @@ -2,19 +2,20 @@ * @copyright Copyright The SimpleKernel Contributors */ +#include "fatfs.hpp" + #include #include #include "device_manager.hpp" #include "device_node.hpp" -#include "fatfs.hpp" #include "kstd_cstring" #include "mount.hpp" #include "system_test.h" #include "vfs.hpp" -auto fatfs_system_test() -> bool { - klog::Info("fatfs_system_test: start"); +auto fatfs_test() -> bool { + klog::Info("fatfs_test: start"); // T1: Get virtio-blk device via DeviceManager DeviceNode* blk_nodes[4]{}; @@ -22,36 +23,38 @@ auto fatfs_system_test() -> bool { DeviceType::kBlock, blk_nodes, 4); if (blk_count == 0 || blk_nodes[0]->block_device == nullptr) { klog::Info( - "fatfs_system_test: SKIP — no virtio-blk device " + "fatfs_test: SKIP — no virtio-blk device " "available"); return true; // Graceful skip, not a failure } vfs::BlockDevice* blk = blk_nodes[0]->block_device; - klog::Info("fatfs_system_test: virtio-blk device: {}", blk->GetName()); + klog::Info("fatfs_test: virtio-blk device: {}", blk->GetName()); EXPECT_GT(blk->GetSectorCount(), static_cast(0), - "fatfs_system_test: virtio-blk has zero sectors"); + "fatfs_test: virtio-blk has zero sectors"); // T2: Mount FatFS at /mnt/fat // vfs::Init() and ramfs mount at "/" should already have been done by - // ramfs_system_test, but call Init() again (it's idempotent). + // ramfs_test, but call Init() again (it's idempotent). auto init_result = vfs::Init(); - EXPECT_TRUE(init_result.has_value(), "fatfs_system_test: vfs init failed"); + EXPECT_TRUE(init_result.has_value(), "fatfs_test: vfs init failed"); + + // FileSystemInit() may have already mounted FatFS at /mnt/fat — tear it + // down so the test owns the full mount lifecycle. + if (vfs::GetMountTable().IsMountPoint("/mnt/fat")) { + (void)vfs::GetMountTable().Unmount("/mnt/fat"); + } // Create /mnt and /mnt/fat directories in the VFS tree (in ramfs at /) // before mounting. MkDir is idempotent-ish — ignore errors if exists. (void)vfs::MkDir("/mnt"); (void)vfs::MkDir("/mnt/fat"); + // MountTable::Mount calls fs->Mount() internally — do not call it manually. static fatfs::FatFsFileSystem fat_fs(0); - auto fat_mount = fat_fs.Mount(blk); - EXPECT_TRUE(fat_mount.has_value(), - "fatfs_system_test: FatFsFileSystem::Mount failed"); - klog::Info("fatfs_system_test: FatFsFileSystem::Mount ok"); - auto vfs_mount = vfs::GetMountTable().Mount("/mnt/fat", &fat_fs, blk); EXPECT_TRUE(vfs_mount.has_value(), - "fatfs_system_test: vfs mount at /mnt/fat failed"); - klog::Info("fatfs_system_test: vfs mount at /mnt/fat ok"); + "fatfs_test: vfs mount at /mnt/fat failed"); + klog::Info("fatfs_test: vfs mount at /mnt/fat ok"); // T3: Write a file on the FAT volume { @@ -59,16 +62,16 @@ auto fatfs_system_test() -> bool { vfs::Open("/mnt/fat/test.txt", vfs::OpenFlags::kOCreate | vfs::OpenFlags::kOReadWrite); EXPECT_TRUE(file_result.has_value(), - "fatfs_system_test: open /mnt/fat/test.txt failed"); + "fatfs_test: open /mnt/fat/test.txt failed"); vfs::File* file = file_result.value(); const char kMsg[] = "Hello, FatFS!"; auto write_result = vfs::Write(file, kMsg, sizeof(kMsg) - 1); EXPECT_TRUE(write_result.has_value(), - "fatfs_system_test: write to /mnt/fat/test.txt failed"); + "fatfs_test: write to /mnt/fat/test.txt failed"); EXPECT_EQ(write_result.value(), sizeof(kMsg) - 1, - "fatfs_system_test: write byte count mismatch"); - klog::Info("fatfs_system_test: wrote {} bytes to /mnt/fat/test.txt", + "fatfs_test: write byte count mismatch"); + klog::Info("fatfs_test: wrote {} bytes to /mnt/fat/test.txt", write_result.value()); (void)vfs::Close(file); @@ -79,19 +82,19 @@ auto fatfs_system_test() -> bool { auto file_result = vfs::Open("/mnt/fat/test.txt", vfs::OpenFlags::kOReadOnly); EXPECT_TRUE(file_result.has_value(), - "fatfs_system_test: re-open /mnt/fat/test.txt failed"); + "fatfs_test: re-open /mnt/fat/test.txt failed"); vfs::File* file = file_result.value(); char buf[64] = {}; const char kMsg[] = "Hello, FatFS!"; auto read_result = vfs::Read(file, buf, sizeof(buf) - 1); EXPECT_TRUE(read_result.has_value(), - "fatfs_system_test: read from /mnt/fat/test.txt failed"); + "fatfs_test: read from /mnt/fat/test.txt failed"); EXPECT_EQ(read_result.value(), sizeof(kMsg) - 1, - "fatfs_system_test: read byte count mismatch"); + "fatfs_test: read byte count mismatch"); EXPECT_EQ(memcmp(buf, kMsg, sizeof(kMsg) - 1), 0, - "fatfs_system_test: read content mismatch"); - klog::Info("fatfs_system_test: verified read: {}", buf); + "fatfs_test: read content mismatch"); + klog::Info("fatfs_test: verified read: {}", buf); (void)vfs::Close(file); } @@ -100,15 +103,15 @@ auto fatfs_system_test() -> bool { { auto mkdir_result = vfs::MkDir("/mnt/fat/subdir"); EXPECT_TRUE(mkdir_result.has_value(), - "fatfs_system_test: mkdir /mnt/fat/subdir failed"); - klog::Info("fatfs_system_test: mkdir /mnt/fat/subdir ok"); + "fatfs_test: mkdir /mnt/fat/subdir failed"); + klog::Info("fatfs_test: mkdir /mnt/fat/subdir ok"); // Create a file inside subdir auto inner = vfs::Open("/mnt/fat/subdir/inner.txt", vfs::OpenFlags::kOCreate | vfs::OpenFlags::kOWriteOnly); EXPECT_TRUE(inner.has_value(), - "fatfs_system_test: create /mnt/fat/subdir/inner.txt failed"); + "fatfs_test: create /mnt/fat/subdir/inner.txt failed"); if (inner.has_value()) { (void)vfs::Close(inner.value()); } @@ -117,16 +120,16 @@ auto fatfs_system_test() -> bool { auto dir_file = vfs::Open( "/mnt/fat", vfs::OpenFlags::kOReadOnly | vfs::OpenFlags::kODirectory); EXPECT_TRUE(dir_file.has_value(), - "fatfs_system_test: open /mnt/fat as dir failed"); + "fatfs_test: open /mnt/fat as dir failed"); if (dir_file.has_value()) { vfs::DirEntry entries[16] = {}; auto readdir_result = vfs::ReadDir(dir_file.value(), entries, 16); EXPECT_TRUE(readdir_result.has_value(), - "fatfs_system_test: readdir /mnt/fat failed"); + "fatfs_test: readdir /mnt/fat failed"); // Should see at least test.txt and subdir EXPECT_GT(readdir_result.value(), static_cast(1), - "fatfs_system_test: readdir /mnt/fat should return > 1 entry"); - klog::Info("fatfs_system_test: readdir /mnt/fat returned {} entries", + "fatfs_test: readdir /mnt/fat should return > 1 entry"); + klog::Info("fatfs_test: readdir /mnt/fat returned {} entries", readdir_result.value()); (void)vfs::Close(dir_file.value()); } @@ -134,40 +137,33 @@ auto fatfs_system_test() -> bool { // T6: Unmount and remount — verify persistence { - auto unmount_result = fat_fs.Unmount(); + auto unmount_result = vfs::GetMountTable().Unmount("/mnt/fat"); EXPECT_TRUE(unmount_result.has_value(), - "fatfs_system_test: FatFsFileSystem::Unmount failed"); - klog::Info("fatfs_system_test: unmounted ok"); - - // Remount - auto remount_result = fat_fs.Mount(blk); - EXPECT_TRUE(remount_result.has_value(), - "fatfs_system_test: remount failed"); - klog::Info("fatfs_system_test: remounted ok"); + "fatfs_test: vfs unmount /mnt/fat failed"); + klog::Info("fatfs_test: unmounted ok"); - // Re-wire VFS mount auto vfs_remount = vfs::GetMountTable().Mount("/mnt/fat", &fat_fs, blk); - EXPECT_TRUE(vfs_remount.has_value(), - "fatfs_system_test: vfs remount failed"); + EXPECT_TRUE(vfs_remount.has_value(), "fatfs_test: vfs remount failed"); + klog::Info("fatfs_test: remounted ok"); // Verify test.txt persisted auto file_result = vfs::Open("/mnt/fat/test.txt", vfs::OpenFlags::kOReadOnly); EXPECT_TRUE(file_result.has_value(), - "fatfs_system_test: test.txt not found after remount"); + "fatfs_test: test.txt not found after remount"); if (file_result.has_value()) { char buf[64] = {}; const char kMsg[] = "Hello, FatFS!"; auto read_result = vfs::Read(file_result.value(), buf, sizeof(buf) - 1); EXPECT_TRUE(read_result.has_value(), - "fatfs_system_test: read after remount failed"); + "fatfs_test: read after remount failed"); EXPECT_EQ(memcmp(buf, kMsg, sizeof(kMsg) - 1), 0, - "fatfs_system_test: data corrupted after remount"); - klog::Info("fatfs_system_test: persistence verified: {}", buf); + "fatfs_test: data corrupted after remount"); + klog::Info("fatfs_test: persistence verified: {}", buf); (void)vfs::Close(file_result.value()); } } - klog::Info("fatfs_system_test: all tests passed"); + klog::Info("fatfs_test: all tests passed"); return true; } diff --git a/tests/system_test/fork_test.cpp b/tests/system_test/fork_test.cpp new file mode 100644 index 000000000..2cee6c0bb --- /dev/null +++ b/tests/system_test/fork_test.cpp @@ -0,0 +1,182 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include +#include + +#include "kernel.h" +#include "kernel_log.hpp" +#include "kstd_libcxx.h" +#include "kstd_memory" +#include "syscall.hpp" +#include "system_test.h" +#include "task_control_block.hpp" +#include "task_manager.hpp" + +namespace { + +std::atomic g_tests_completed{0}; +std::atomic g_tests_failed{0}; + +/// Test: fork creates independent child process +std::atomic g_fork_child_pid{0}; +std::atomic g_fork_child_tgid{0}; +std::atomic g_fork_child_parent{0}; + +void fork_child_work(void* /*arg*/) { + auto* current = TaskManagerSingleton::instance().GetCurrentTask(); + g_fork_child_pid.store(current->pid); + g_fork_child_tgid.store(current->aux->tgid); + g_fork_child_parent.store(current->aux->parent_pid); + sys_exit(42); +} + +void test_fork_basic(void* /*arg*/) { + klog::Info("=== Fork Basic Test ==="); + bool passed = true; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + Pid my_pid = self->pid; + + g_fork_child_pid = 0; + g_fork_child_tgid = 0; + g_fork_child_parent = 0; + + auto child = kstd::make_unique("ForkChild", 10, + fork_child_work, nullptr); + child->aux->parent_pid = my_pid; + auto* child_raw = child.get(); + tm.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; + + int timeout = 100; + while (timeout-- > 0 && g_fork_child_pid.load() == 0) { + (void)sys_sleep(50); + } + + if (g_fork_child_pid.load() == 0) { + klog::Err("Fork child did not start"); + passed = false; + } + + if (passed) { + if (g_fork_child_pid.load() == my_pid) { + klog::Err("Child PID == parent PID"); + passed = false; + } + if (g_fork_child_tgid.load() != g_fork_child_pid.load()) { + klog::Err("Child tgid ({}) != child pid ({})", g_fork_child_tgid.load(), + g_fork_child_pid.load()); + passed = false; + } + if (g_fork_child_parent.load() != my_pid) { + klog::Err("Child parent_pid ({}) != my pid ({})", + g_fork_child_parent.load(), my_pid); + passed = false; + } + } + + int status = 0; + auto wait_result = tm.Wait(child_pid, &status, false, false); + if (!wait_result.has_value() || wait_result.value() != child_pid) { + klog::Err("Wait for fork child failed"); + passed = false; + } else if (status != 42) { + klog::Err("Fork child exit code: {} (expected 42)", status); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Fork Basic Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +/// Test: multiple forks — each child independent +std::atomic g_multi_fork_done{0}; + +void multi_fork_child(void* arg) { + g_multi_fork_done++; + int code = static_cast(reinterpret_cast(arg)); + sys_exit(code); +} + +void test_fork_multiple(void* /*arg*/) { + klog::Info("=== Fork Multiple Test ==="); + bool passed = true; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + g_multi_fork_done = 0; + + constexpr int kNumChildren = 4; + Pid child_pids[kNumChildren]; + + for (int i = 0; i < kNumChildren; ++i) { + auto child = kstd::make_unique( + "ForkMultiChild", 10, multi_fork_child, + reinterpret_cast(static_cast(i + 10))); + child->aux->parent_pid = self->pid; + auto* raw = child.get(); + tm.AddTask(std::move(child)); + child_pids[i] = raw->pid; + } + + for (int i = 0; i < kNumChildren; ++i) { + int status = 0; + auto result = tm.Wait(child_pids[i], &status, false, false); + if (!result.has_value()) { + klog::Err("Wait for child {} failed", i); + passed = false; + } else if (status != i + 10) { + klog::Err("Child {} exit code: {} (expected {})", i, status, i + 10); + passed = false; + } + } + + if (g_multi_fork_done.load() != kNumChildren) { + klog::Err("Only {} of {} children ran", g_multi_fork_done.load(), + kNumChildren); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Fork Multiple Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +} // namespace + +auto fork_test() -> bool { + klog::Info("===== Fork System Test Start ====="); + g_tests_completed = 0; + g_tests_failed = 0; + + auto& tm = TaskManagerSingleton::instance(); + + auto t1 = kstd::make_unique("TestForkBasic", 10, + test_fork_basic, nullptr); + tm.AddTask(std::move(t1)); + + auto t2 = kstd::make_unique("TestForkMultiple", 10, + test_fork_multiple, nullptr); + tm.AddTask(std::move(t2)); + + int timeout = 200; + while (timeout-- > 0 && g_tests_completed.load() < 2) { + (void)sys_sleep(50); + } + + EXPECT_EQ(g_tests_completed.load(), 2, "All fork tests completed"); + EXPECT_EQ(g_tests_failed.load(), 0, "No fork tests failed"); + + klog::Info("Fork System Test: COMPLETED"); + return true; +} diff --git a/tests/system_test/main.cpp b/tests/system_test/main.cpp index 9e0e42662..0f2c45b66 100644 --- a/tests/system_test/main.cpp +++ b/tests/system_test/main.cpp @@ -28,7 +28,7 @@ struct test_case { bool is_smp_test = false; }; -constexpr size_t kTestCount = 18; +constexpr size_t kTestCount = 26; std::array test_cases = { test_case{"ctor_dtor_test", ctor_dtor_test, false}, @@ -36,19 +36,28 @@ std::array test_cases = { test_case{"memory_test", memory_test, false}, test_case{"virtual_memory_test", virtual_memory_test, false}, test_case{"interrupt_test", interrupt_test, false}, + test_case{"kernel_task_test", kernel_task_test, false}, + test_case{"user_task_test", user_task_test, false}, test_case{"fifo_scheduler_test", fifo_scheduler_test, false}, test_case{"rr_scheduler_test", rr_scheduler_test, false}, test_case{"cfs_scheduler_test", cfs_scheduler_test, false}, test_case{"idle_scheduler_test", idle_scheduler_test, false}, - test_case{"thread_group_system_test", thread_group_system_test, false}, - test_case{"wait_system_test", wait_system_test, false}, - test_case{"clone_system_test", clone_system_test, false}, - test_case{"exit_system_test", exit_system_test, false}, - test_case{"ramfs_system_test", ramfs_system_test, false}, - test_case{"fatfs_system_test", fatfs_system_test, false}, + test_case{"thread_group_test", thread_group_test, false}, + test_case{"wait_test", wait_test, false}, + test_case{"clone_test", clone_test, false}, + test_case{"exit_test", exit_test, false}, + test_case{"cross_core_test", cross_core_test, false}, test_case{"mutex_test", mutex_test, false}, - test_case{"kernel_task_test", kernel_task_test, false}, - test_case{"user_task_test", user_task_test, false}}; + test_case{"yield_test", yield_test, false}, + test_case{"fork_test", fork_test, false}, + test_case{"signal_test", signal_test, false}, + test_case{"affinity_test", affinity_test, false}, + test_case{"tick_test", tick_test, false}, + test_case{"zombie_reap_test", zombie_reap_test, false}, + test_case{"stress_test", stress_test, false}, + test_case{"ramfs_test", ramfs_test, false}, + test_case{"fatfs_test", fatfs_test, false}, +}; std::array test_results{}; @@ -137,10 +146,13 @@ void test_runner_entry(void* /*arg*/) { task->aux->parent_pid = runner->pid; task->aux->pgid = runner->aux->pgid; - test_results[i].pid = task->pid; + // Save raw pointer: pid is assigned inside AddTask() by AllocatePid(), + // so we must read it *after* the call (unique_ptr is moved). + auto* task_ptr = task.get(); test_results[i].status = TestThreadStatus::kRunning; task_mgr.AddTask(std::move(task)); + test_results[i].pid = task_ptr->pid; thread_test_count++; } @@ -160,6 +172,7 @@ void test_runner_entry(void* /*arg*/) { if (wait_result.has_value() && wait_result.value() > 0) { Pid exited_pid = wait_result.value(); + bool is_test_thread = false; for (size_t i = 0; i < kTestCount; ++i) { if (test_results[i].pid == static_cast(exited_pid)) { test_results[i].exit_code = status; @@ -169,11 +182,17 @@ void test_runner_entry(void* /*arg*/) { klog::Info("[RUNNER] Collected: {} (pid={}, exit_code={}) — {}", test_cases[i].name, exited_pid, status, (status == 0) ? "PASS" : "FAIL"); + is_test_thread = true; break; } } - collected++; + if (is_test_thread) { + collected++; + } else { + klog::Debug("[RUNNER] Reaped orphan pid={}, not a test thread", + exited_pid); + } } else { (void)sys_sleep(50); retries++; @@ -234,8 +253,10 @@ auto main_smp(int argc, const char** argv) -> int { } // namespace +std::atomic_flag primary_booted_ = ATOMIC_FLAG_INIT; + auto _start(int argc, const char** argv) -> void { - if (argv != nullptr) { + if (!primary_booted_.test_and_set(std::memory_order_acquire)) { CppInit(); main(argc, argv); } else { diff --git a/tests/system_test/ramfs_system_test.cpp b/tests/system_test/ramfs_test.cpp similarity index 58% rename from tests/system_test/ramfs_system_test.cpp rename to tests/system_test/ramfs_test.cpp index 692701c22..60e29bc43 100644 --- a/tests/system_test/ramfs_system_test.cpp +++ b/tests/system_test/ramfs_test.cpp @@ -9,8 +9,8 @@ #include "system_test.h" #include "vfs.hpp" -auto ramfs_system_test() -> bool { - klog::Info("ramfs_system_test: start"); +auto ramfs_test() -> bool { + klog::Info("ramfs_test: start"); // FileSystemInit() has already been called in main.cpp. // ramfs is mounted at "/" — use VFS directly. @@ -19,33 +19,31 @@ auto ramfs_system_test() -> bool { { auto file_result = vfs::Open( "/hello.txt", vfs::OpenFlags::kOCreate | vfs::OpenFlags::kOReadWrite); - EXPECT_TRUE(file_result.has_value(), - "ramfs_system_test: open /hello.txt failed"); + EXPECT_TRUE(file_result.has_value(), "ramfs_test: open /hello.txt failed"); vfs::File* file = file_result.value(); const char kMsg[] = "Hello, ramfs!"; auto write_result = vfs::Write(file, kMsg, sizeof(kMsg) - 1); - EXPECT_TRUE(write_result.has_value(), "ramfs_system_test: write failed"); + EXPECT_TRUE(write_result.has_value(), "ramfs_test: write failed"); EXPECT_EQ(write_result.value(), sizeof(kMsg) - 1, - "ramfs_system_test: write byte count mismatch"); - klog::Info("ramfs_system_test: wrote {} bytes", write_result.value()); + "ramfs_test: write byte count mismatch"); + klog::Info("ramfs_test: wrote {} bytes", write_result.value()); // Seek back to start auto seek_result = vfs::Seek(file, 0, vfs::SeekWhence::kSet); - EXPECT_TRUE(seek_result.has_value(), - "ramfs_system_test: seek to start failed"); + EXPECT_TRUE(seek_result.has_value(), "ramfs_test: seek to start failed"); EXPECT_EQ(seek_result.value(), static_cast(0), - "ramfs_system_test: seek position mismatch"); + "ramfs_test: seek position mismatch"); // Read back char buf[64] = {}; auto read_result = vfs::Read(file, buf, sizeof(buf) - 1); - EXPECT_TRUE(read_result.has_value(), "ramfs_system_test: read failed"); + EXPECT_TRUE(read_result.has_value(), "ramfs_test: read failed"); EXPECT_EQ(read_result.value(), sizeof(kMsg) - 1, - "ramfs_system_test: read byte count mismatch"); + "ramfs_test: read byte count mismatch"); EXPECT_EQ(memcmp(buf, kMsg, sizeof(kMsg) - 1), 0, - "ramfs_system_test: read content mismatch"); - klog::Info("ramfs_system_test: read back: {}", buf); + "ramfs_test: read content mismatch"); + klog::Info("ramfs_test: read back: {}", buf); (void)vfs::Close(file); } @@ -54,26 +52,24 @@ auto ramfs_system_test() -> bool { { auto file_result = vfs::Open("/hello.txt", vfs::OpenFlags::kOReadOnly); EXPECT_TRUE(file_result.has_value(), - "ramfs_system_test: re-open for seek test failed"); + "ramfs_test: re-open for seek test failed"); vfs::File* file = file_result.value(); // Seek to offset 7 auto seek_result = vfs::Seek(file, 7, vfs::SeekWhence::kSet); - EXPECT_TRUE(seek_result.has_value(), - "ramfs_system_test: seek to offset 7 failed"); + EXPECT_TRUE(seek_result.has_value(), "ramfs_test: seek to offset 7 failed"); EXPECT_EQ(seek_result.value(), static_cast(7), - "ramfs_system_test: seek offset 7 mismatch"); + "ramfs_test: seek offset 7 mismatch"); char buf[32] = {}; auto read_result = vfs::Read(file, buf, 5); - EXPECT_TRUE(read_result.has_value(), - "ramfs_system_test: partial read failed"); + EXPECT_TRUE(read_result.has_value(), "ramfs_test: partial read failed"); // "Hello, ramfs!" -> offset 7 = "ramfs" EXPECT_EQ(read_result.value(), static_cast(5), - "ramfs_system_test: partial read count mismatch"); + "ramfs_test: partial read count mismatch"); EXPECT_EQ(memcmp(buf, "ramfs", 5), 0, - "ramfs_system_test: partial read content mismatch"); - klog::Info("ramfs_system_test: partial read from offset 7: {}", buf); + "ramfs_test: partial read content mismatch"); + klog::Info("ramfs_test: partial read from offset 7: {}", buf); (void)vfs::Close(file); } @@ -81,33 +77,31 @@ auto ramfs_system_test() -> bool { // T4: MkDir + ReadDir { auto mkdir_result = vfs::MkDir("/testdir"); - EXPECT_TRUE(mkdir_result.has_value(), - "ramfs_system_test: mkdir /testdir failed"); - klog::Info("ramfs_system_test: mkdir /testdir ok"); + EXPECT_TRUE(mkdir_result.has_value(), "ramfs_test: mkdir /testdir failed"); + klog::Info("ramfs_test: mkdir /testdir ok"); // Create a file inside auto inner = vfs::Open("/testdir/inner.txt", vfs::OpenFlags::kOCreate | vfs::OpenFlags::kOWriteOnly); EXPECT_TRUE(inner.has_value(), - "ramfs_system_test: open /testdir/inner.txt failed"); + "ramfs_test: open /testdir/inner.txt failed"); (void)vfs::Close(inner.value()); // ReadDir on /testdir auto dir_file_result = vfs::Open( "/testdir", vfs::OpenFlags::kOReadOnly | vfs::OpenFlags::kODirectory); EXPECT_TRUE(dir_file_result.has_value(), - "ramfs_system_test: open /testdir as dir failed"); + "ramfs_test: open /testdir as dir failed"); vfs::File* dir_file = dir_file_result.value(); vfs::DirEntry entries[8] = {}; auto readdir_result = vfs::ReadDir(dir_file, entries, 8); - EXPECT_TRUE(readdir_result.has_value(), - "ramfs_system_test: readdir failed"); + EXPECT_TRUE(readdir_result.has_value(), "ramfs_test: readdir failed"); // Expect at least "." + ".." + "inner.txt" = 3 entries EXPECT_GT(readdir_result.value(), static_cast(2), - "ramfs_system_test: readdir should return > 2 entries"); - klog::Info("ramfs_system_test: readdir returned {} entries", + "ramfs_test: readdir should return > 2 entries"); + klog::Info("ramfs_test: readdir returned {} entries", readdir_result.value()); (void)vfs::Close(dir_file); @@ -117,13 +111,13 @@ auto ramfs_system_test() -> bool { { auto unlink_result = vfs::Unlink("/hello.txt"); EXPECT_TRUE(unlink_result.has_value(), - "ramfs_system_test: unlink /hello.txt failed"); - klog::Info("ramfs_system_test: unlink /hello.txt ok"); + "ramfs_test: unlink /hello.txt failed"); + klog::Info("ramfs_test: unlink /hello.txt ok"); auto reopen = vfs::Open("/hello.txt", vfs::OpenFlags::kOReadOnly); EXPECT_FALSE(reopen.has_value(), - "ramfs_system_test: /hello.txt should be gone after unlink"); - klog::Info("ramfs_system_test: confirmed /hello.txt no longer exists"); + "ramfs_test: /hello.txt should be gone after unlink"); + klog::Info("ramfs_test: confirmed /hello.txt no longer exists"); } // T6: RmDir @@ -131,12 +125,11 @@ auto ramfs_system_test() -> bool { // Remove inner file first auto unlink_result = vfs::Unlink("/testdir/inner.txt"); EXPECT_TRUE(unlink_result.has_value(), - "ramfs_system_test: unlink /testdir/inner.txt failed"); + "ramfs_test: unlink /testdir/inner.txt failed"); auto rmdir_result = vfs::RmDir("/testdir"); - EXPECT_TRUE(rmdir_result.has_value(), - "ramfs_system_test: rmdir /testdir failed"); - klog::Info("ramfs_system_test: rmdir /testdir ok"); + EXPECT_TRUE(rmdir_result.has_value(), "ramfs_test: rmdir /testdir failed"); + klog::Info("ramfs_test: rmdir /testdir ok"); } // T7: Two independent files do not share data @@ -145,8 +138,8 @@ auto ramfs_system_test() -> bool { vfs::OpenFlags::kOCreate | vfs::OpenFlags::kOReadWrite); auto f2 = vfs::Open("/fileB.txt", vfs::OpenFlags::kOCreate | vfs::OpenFlags::kOReadWrite); - EXPECT_TRUE(f1.has_value(), "ramfs_system_test: open fileA failed"); - EXPECT_TRUE(f2.has_value(), "ramfs_system_test: open fileB failed"); + EXPECT_TRUE(f1.has_value(), "ramfs_test: open fileA failed"); + EXPECT_TRUE(f2.has_value(), "ramfs_test: open fileB failed"); const char kDataA[] = "AAAA"; const char kDataB[] = "BBBB"; @@ -162,10 +155,10 @@ auto ramfs_system_test() -> bool { (void)vfs::Read(f2.value(), buf2, 4); EXPECT_EQ(memcmp(buf1, kDataA, 4), 0, - "ramfs_system_test: fileA data corrupted by fileB"); + "ramfs_test: fileA data corrupted by fileB"); EXPECT_EQ(memcmp(buf2, kDataB, 4), 0, - "ramfs_system_test: fileB data corrupted by fileA"); - klog::Info("ramfs_system_test: two files are independent"); + "ramfs_test: fileB data corrupted by fileA"); + klog::Info("ramfs_test: two files are independent"); (void)vfs::Close(f1.value()); (void)vfs::Close(f2.value()); @@ -173,6 +166,6 @@ auto ramfs_system_test() -> bool { (void)vfs::Unlink("/fileB.txt"); } - klog::Info("ramfs_system_test: all tests passed"); + klog::Info("ramfs_test: all tests passed"); return true; } diff --git a/tests/system_test/signal_test.cpp b/tests/system_test/signal_test.cpp new file mode 100644 index 000000000..a12f79f14 --- /dev/null +++ b/tests/system_test/signal_test.cpp @@ -0,0 +1,432 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include "signal.hpp" + +#include +#include + +#include "kernel.h" +#include "kernel_log.hpp" +#include "kstd_libcxx.h" +#include "kstd_memory" +#include "syscall.hpp" +#include "system_test.h" +#include "task_control_block.hpp" +#include "task_manager.hpp" + +namespace { + +std::atomic g_tests_completed{0}; +std::atomic g_tests_failed{0}; + +// --------------------------------------------------------------------------- +// test_sigterm_default +// Spawn a sleeping task, send SIGTERM, Wait -- expect exit code 128+15=143 +// --------------------------------------------------------------------------- + +void sigterm_target(void* /*arg*/) { + // Sleep long enough for the parent to send SIGTERM + (void)sys_sleep(5000); + // Should never reach here -- SIGTERM default action terminates + sys_exit(0); +} + +void test_sigterm_default(void* /*arg*/) { + klog::Info("=== Signal: SIGTERM Default Test ==="); + bool passed = true; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + + auto child = kstd::make_unique("SigtermTarget", 10, + sigterm_target, nullptr); + child->aux->parent_pid = self->pid; + auto* child_raw = child.get(); + tm.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; + + // Give the child time to start sleeping + (void)sys_sleep(100); + + // Send SIGTERM + int ret = sys_kill(static_cast(child_pid), signal_number::kSigTerm); + if (ret != 0) { + klog::Err("test_sigterm_default: sys_kill returned {}", ret); + passed = false; + } + + // Wait for the child to be terminated + int status = 0; + auto wait_result = tm.Wait(child_pid, &status, false, false); + if (!wait_result.has_value() || wait_result.value() != child_pid) { + klog::Err("test_sigterm_default: Wait failed"); + passed = false; + } else if (status != 128 + signal_number::kSigTerm) { + klog::Err("test_sigterm_default: exit code {} (expected {})", status, + 128 + signal_number::kSigTerm); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Signal SIGTERM Default Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_sigkill +// Spawn a task that tries to ignore SIGKILL (should fail), send SIGKILL -- +// expect exit code 128+9=137 +// --------------------------------------------------------------------------- + +void sigkill_target(void* /*arg*/) { + // Attempt to ignore SIGKILL -- this should fail + (void)sys_sigaction(signal_number::kSigKill, kSigIgn); + + // Sleep long enough for the parent to send SIGKILL + (void)sys_sleep(5000); + // Should never reach here + sys_exit(0); +} + +void test_sigkill(void* /*arg*/) { + klog::Info("=== Signal: SIGKILL Test ==="); + bool passed = true; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + + auto child = kstd::make_unique("SigkillTarget", 10, + sigkill_target, nullptr); + child->aux->parent_pid = self->pid; + auto* child_raw = child.get(); + tm.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; + + // Give the child time to start + (void)sys_sleep(100); + + // Send SIGKILL + int ret = sys_kill(static_cast(child_pid), signal_number::kSigKill); + if (ret != 0) { + klog::Err("test_sigkill: sys_kill returned {}", ret); + passed = false; + } + + // Wait for the child + int status = 0; + auto wait_result = tm.Wait(child_pid, &status, false, false); + if (!wait_result.has_value() || wait_result.value() != child_pid) { + klog::Err("test_sigkill: Wait failed"); + passed = false; + } else if (status != 128 + signal_number::kSigKill) { + klog::Err("test_sigkill: exit code {} (expected {})", status, + 128 + signal_number::kSigKill); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Signal SIGKILL Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_sigaction_ignore +// Task sets SIG_IGN for SIGTERM, parent sends SIGTERM, task survives and +// exits normally with 0 +// --------------------------------------------------------------------------- + +std::atomic g_ignore_handler_set{false}; + +void sigaction_ignore_target(void* /*arg*/) { + // Ignore SIGTERM + int ret = sys_sigaction(signal_number::kSigTerm, kSigIgn); + if (ret != 0) { + sys_exit(99); + } + g_ignore_handler_set.store(true); + + // Sleep -- parent will send SIGTERM during this time + (void)sys_sleep(500); + + // If we survive (SIGTERM was ignored), exit normally + sys_exit(0); +} + +void test_sigaction_ignore(void* /*arg*/) { + klog::Info("=== Signal: Sigaction Ignore Test ==="); + bool passed = true; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + + g_ignore_handler_set.store(false); + + auto child = kstd::make_unique( + "SigIgnTarget", 10, sigaction_ignore_target, nullptr); + child->aux->parent_pid = self->pid; + auto* child_raw = child.get(); + tm.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; + + // Wait for the child to set the ignore handler + int timeout = 100; + while (timeout-- > 0 && !g_ignore_handler_set.load()) { + (void)sys_sleep(50); + } + + if (!g_ignore_handler_set.load()) { + klog::Err("test_sigaction_ignore: child did not set handler"); + passed = false; + } + + // Send SIGTERM -- should be ignored + int ret = sys_kill(static_cast(child_pid), signal_number::kSigTerm); + if (ret != 0) { + klog::Err("test_sigaction_ignore: sys_kill returned {}", ret); + passed = false; + } + + // Wait for the child -- should exit normally with 0 + int status = 0; + auto wait_result = tm.Wait(child_pid, &status, false, false); + if (!wait_result.has_value() || wait_result.value() != child_pid) { + klog::Err("test_sigaction_ignore: Wait failed"); + passed = false; + } else if (status != 0) { + klog::Err("test_sigaction_ignore: exit code {} (expected 0)", status); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Signal Sigaction Ignore Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_sigaction_uncatchable +// Directly call sys_sigaction(kSigKill, kSigIgn) -- expect return -1 +// Same for kSigStop +// --------------------------------------------------------------------------- + +void test_sigaction_uncatchable(void* /*arg*/) { + klog::Info("=== Signal: Sigaction Uncatchable Test ==="); + bool passed = true; + + // Attempt to set handler for SIGKILL -- must fail + int ret = sys_sigaction(signal_number::kSigKill, kSigIgn); + if (ret != -1) { + klog::Err( + "test_sigaction_uncatchable: sigaction(SIGKILL) returned {} " + "(expected -1)", + ret); + passed = false; + } + + // Attempt to set handler for SIGSTOP -- must fail + ret = sys_sigaction(signal_number::kSigStop, kSigIgn); + if (ret != -1) { + klog::Err( + "test_sigaction_uncatchable: sigaction(SIGSTOP) returned {} " + "(expected -1)", + ret); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Signal Sigaction Uncatchable Test: {}", + passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_sigprocmask +// Task blocks SIGTERM, parent sends SIGTERM while blocked, task survives +// the blocked period (sets atomic flag), then unblocks +// --------------------------------------------------------------------------- + +std::atomic g_mask_ready{false}; +std::atomic g_survived_blocked{false}; + +void sigprocmask_target(void* /*arg*/) { + // Block SIGTERM + uint32_t sigterm_set = 1U << signal_number::kSigTerm; + int ret = sys_sigprocmask(signal_mask_op::kSigBlock, sigterm_set, nullptr); + if (ret != 0) { + sys_exit(99); + } + g_mask_ready.store(true); + + // Sleep while SIGTERM is blocked -- parent will send SIGTERM now + (void)sys_sleep(300); + + // If we get here, we survived the blocked period + g_survived_blocked.store(true); + + // Unblock SIGTERM -- pending signal should now be delivered (terminate) + (void)sys_sigprocmask(signal_mask_op::kSigUnblock, sigterm_set, nullptr); + + // Give the kernel a chance to deliver the pending signal + (void)sys_sleep(100); + + // If we reach here the pending signal was not delivered (unexpected). + // Exit with a distinguishable code so the parent can detect it. + sys_exit(0); +} + +void test_sigprocmask(void* /*arg*/) { + klog::Info("=== Signal: Sigprocmask Test ==="); + bool passed = true; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + + g_mask_ready.store(false); + g_survived_blocked.store(false); + + auto child = kstd::make_unique("SigmaskTarget", 10, + sigprocmask_target, nullptr); + child->aux->parent_pid = self->pid; + auto* child_raw = child.get(); + tm.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; + + // Wait for the child to block SIGTERM + int timeout = 100; + while (timeout-- > 0 && !g_mask_ready.load()) { + (void)sys_sleep(50); + } + + if (!g_mask_ready.load()) { + klog::Err("test_sigprocmask: child did not set mask"); + passed = false; + } + + // Send SIGTERM while it is blocked + int ret = sys_kill(static_cast(child_pid), signal_number::kSigTerm); + if (ret != 0) { + klog::Err("test_sigprocmask: sys_kill returned {}", ret); + passed = false; + } + + // Wait for the child to finish + int status = 0; + (void)tm.Wait(child_pid, &status, false, false); + + // The child must have survived the blocked period + if (!g_survived_blocked.load()) { + klog::Err("test_sigprocmask: child did not survive blocked period"); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Signal Sigprocmask Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_kill_invalid_pid +// Call sys_kill(99999, kSigTerm) -- expect return -1 +// --------------------------------------------------------------------------- + +void test_kill_invalid_pid(void* /*arg*/) { + klog::Info("=== Signal: Kill Invalid PID Test ==="); + bool passed = true; + + int ret = sys_kill(99999, signal_number::kSigTerm); + if (ret != -1) { + klog::Err( + "test_kill_invalid_pid: sys_kill(99999) returned {} (expected -1)", + ret); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Signal Kill Invalid PID Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +} // namespace + +/** + * @brief Signal system test entry point + */ +auto signal_test() -> bool { + klog::Info("===== Signal System Test Start ====="); + + g_tests_completed = 0; + g_tests_failed = 0; + + auto& tm = TaskManagerSingleton::instance(); + + // Sub-test 1: SIGTERM default action + auto t1 = kstd::make_unique("TestSigtermDefault", 10, + test_sigterm_default, nullptr); + tm.AddTask(std::move(t1)); + + // Sub-test 2: SIGKILL (uncatchable) + auto t2 = kstd::make_unique("TestSigkill", 10, test_sigkill, + nullptr); + tm.AddTask(std::move(t2)); + + // Sub-test 3: Sigaction ignore + auto t3 = kstd::make_unique("TestSigactionIgnore", 10, + test_sigaction_ignore, nullptr); + tm.AddTask(std::move(t3)); + + // Sub-test 4: Sigaction uncatchable + auto t4 = kstd::make_unique( + "TestSigactionUncatchable", 10, test_sigaction_uncatchable, nullptr); + tm.AddTask(std::move(t4)); + + // Sub-test 5: Sigprocmask + auto t5 = kstd::make_unique("TestSigprocmask", 10, + test_sigprocmask, nullptr); + tm.AddTask(std::move(t5)); + + // Sub-test 6: Kill invalid PID + auto t6 = kstd::make_unique("TestKillInvalidPid", 10, + test_kill_invalid_pid, nullptr); + tm.AddTask(std::move(t6)); + + klog::Info("Waiting for all 6 signal sub-tests to complete..."); + + // Wait for all sub-tests (timeout: 200 * 50ms = 10s) + constexpr int kExpectedTests = 6; + int timeout = 200; + while (timeout-- > 0) { + (void)sys_sleep(50); + if (g_tests_completed.load() >= kExpectedTests) { + break; + } + } + + klog::Info("Signal System Test: completed={}, failed={}", + g_tests_completed.load(), g_tests_failed.load()); + + EXPECT_EQ(g_tests_completed.load(), kExpectedTests, + "All 6 signal sub-tests completed"); + EXPECT_EQ(g_tests_failed.load(), 0, "No signal sub-tests failed"); + + klog::Info("===== Signal System Test End ====="); + return true; +} diff --git a/tests/system_test/spinlock_test.cpp b/tests/system_test/spinlock_test.cpp index 46cb241bb..395b85957 100644 --- a/tests/system_test/spinlock_test.cpp +++ b/tests/system_test/spinlock_test.cpp @@ -216,14 +216,16 @@ auto spinlock_smp_string_test() -> bool { constexpr int kBarrierSpinLimit = 100000000; int spins = 0; - while (str_test_start_barrier.load() < (int)core_count) { + while (str_test_start_barrier.load(std::memory_order_acquire) < + (int)core_count) { + cpu_io::Pause(); if (++spins > kBarrierSpinLimit) { if (core_id == 0) { klog::Err( "SMP string test barrier timeout: {}/{} cores arrived, skipping", str_test_start_barrier.load(), (int)core_count); } - return true; + return false; } } diff --git a/tests/system_test/stress_test.cpp b/tests/system_test/stress_test.cpp new file mode 100644 index 000000000..5d3ae92ef --- /dev/null +++ b/tests/system_test/stress_test.cpp @@ -0,0 +1,253 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include +#include + +#include "kernel.h" +#include "kernel_config.hpp" +#include "kernel_log.hpp" +#include "kstd_libcxx.h" +#include "kstd_memory" +#include "syscall.hpp" +#include "system_test.h" +#include "task_control_block.hpp" +#include "task_manager.hpp" + +namespace { + +std::atomic g_tests_completed{0}; +std::atomic g_tests_failed{0}; + +// --------------------------------------------------------------------------- +// test_many_tasks +// Spawn 20 tasks simultaneously, each increments an atomic counter and exits. +// Parent waits for all by PID, then verifies counter == 20. +// --------------------------------------------------------------------------- + +std::atomic g_many_tasks_counter{0}; + +void many_tasks_work(void* /*arg*/) { + g_many_tasks_counter++; + sys_exit(0); +} + +void test_many_tasks(void* /*arg*/) { + klog::Info("=== Stress: Many Tasks Test ==="); + + g_many_tasks_counter = 0; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + if (!self) { + klog::Err("test_many_tasks: Cannot get current task"); + g_tests_failed++; + g_tests_completed++; + sys_exit(1); + } + + constexpr int kTaskCount = 20; + Pid pids[kTaskCount]; + + // Spawn all 20 tasks + for (int i = 0; i < kTaskCount; ++i) { + auto task = kstd::make_unique("StressWorker", 10, + many_tasks_work, nullptr); + task->aux->parent_pid = self->pid; + task->aux->pgid = self->aux->pgid; + auto* raw = task.get(); + tm.AddTask(std::move(task)); + pids[i] = raw->pid; + } + + // Wait for each task by PID + bool passed = true; + for (int i = 0; i < kTaskCount; ++i) { + int status = 0; + int timeout = 100; + while (timeout-- > 0) { + auto result = tm.Wait(pids[i], &status, false, false); + if (result.has_value() && result.value() == pids[i]) { + break; + } + (void)sys_sleep(10); + } + if (timeout <= 0) { + klog::Err("test_many_tasks: timed out waiting for task {}", pids[i]); + passed = false; + } + } + + if (g_many_tasks_counter.load() != kTaskCount) { + klog::Err("test_many_tasks: FAIL - counter={}, expected {}", + g_many_tasks_counter.load(), kTaskCount); + passed = false; + } + + if (passed) { + klog::Info("Stress: Many Tasks Test: PASSED"); + } else { + klog::Err("Stress: Many Tasks Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_wait_non_child +// Call Wait with a bogus PID that is not a child of the current task. +// Expect the call to fail (no value, or value <= 0). +// --------------------------------------------------------------------------- + +void test_wait_non_child(void* /*arg*/) { + klog::Info("=== Stress: Wait Non-Child Test ==="); + + auto& tm = TaskManagerSingleton::instance(); + + int status = 0; + auto result = tm.Wait(99999, &status, false, false); + + bool passed = true; + if (result.has_value() && result.value() > 0) { + klog::Err( + "test_wait_non_child: FAIL - Wait(99999) should fail but got pid={}", + result.value()); + passed = false; + } else { + klog::Info("test_wait_non_child: Wait(99999) correctly failed"); + } + + if (passed) { + klog::Info("Stress: Wait Non-Child Test: PASSED"); + } else { + klog::Err("Stress: Wait Non-Child Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_rapid_create_exit +// Loop 10 times: create a task that exits with code i, Wait for it, +// verify exit code == i. Tests rapid lifecycle churn. +// --------------------------------------------------------------------------- + +void rapid_exit_work(void* arg) { + int code = static_cast(reinterpret_cast(arg)); + sys_exit(code); +} + +void test_rapid_create_exit(void* /*arg*/) { + klog::Info("=== Stress: Rapid Create/Exit Test ==="); + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + if (!self) { + klog::Err("test_rapid_create_exit: Cannot get current task"); + g_tests_failed++; + g_tests_completed++; + sys_exit(1); + } + + bool passed = true; + constexpr int kIterations = 10; + + for (int i = 0; i < kIterations; ++i) { + auto task = kstd::make_unique( + "RapidExit", 10, rapid_exit_work, reinterpret_cast(i)); + task->aux->parent_pid = self->pid; + task->aux->pgid = self->aux->pgid; + auto* raw = task.get(); + tm.AddTask(std::move(task)); + Pid pid = raw->pid; + + int status = 0; + int timeout = 100; + while (timeout-- > 0) { + auto result = tm.Wait(pid, &status, false, false); + if (result.has_value() && result.value() == pid) { + break; + } + (void)sys_sleep(10); + } + + if (timeout <= 0) { + klog::Err("test_rapid_create_exit: timed out waiting for task {}", pid); + passed = false; + break; + } + + if (status != i) { + klog::Err( + "test_rapid_create_exit: FAIL - iteration {}: exit code={}, " + "expected {}", + i, status, i); + passed = false; + } + } + + if (passed) { + klog::Info("Stress: Rapid Create/Exit Test: PASSED"); + } else { + klog::Err("Stress: Rapid Create/Exit Test: FAILED"); + g_tests_failed++; + } + + g_tests_completed++; + sys_exit(passed ? 0 : 1); +} + +} // namespace + +/** + * @brief Stress system test entry point + */ +auto stress_test() -> bool { + klog::Info("===== Stress System Test Start ====="); + + g_tests_completed = 0; + g_tests_failed = 0; + + auto& tm = TaskManagerSingleton::instance(); + + // Sub-test 1: Many tasks + auto t1 = kstd::make_unique("TestManyTasks", 10, + test_many_tasks, nullptr); + tm.AddTask(std::move(t1)); + + // Sub-test 2: Wait non-child + auto t2 = kstd::make_unique("TestWaitNonChild", 10, + test_wait_non_child, nullptr); + tm.AddTask(std::move(t2)); + + // Sub-test 3: Rapid create/exit + auto t3 = kstd::make_unique( + "TestRapidCreateExit", 10, test_rapid_create_exit, nullptr); + tm.AddTask(std::move(t3)); + + // Wait for all 3 sub-tests to complete (timeout: 400 * 50ms = 20s) + constexpr int kExpectedTests = 3; + int timeout = 400; + while (timeout > 0) { + (void)sys_sleep(50); + if (g_tests_completed.load() >= kExpectedTests) { + break; + } + timeout--; + } + + klog::Info("Stress System Test: completed={}, failed={}", + g_tests_completed.load(), g_tests_failed.load()); + + EXPECT_EQ(g_tests_completed.load(), kExpectedTests, + "All 3 stress sub-tests completed"); + EXPECT_EQ(g_tests_failed.load(), 0, "No stress sub-tests failed"); + + klog::Info("===== Stress System Test End ====="); + return true; +} diff --git a/tests/system_test/system_test.h b/tests/system_test/system_test.h index 842677e7c..6817a1ee5 100644 --- a/tests/system_test/system_test.h +++ b/tests/system_test/system_test.h @@ -4,12 +4,15 @@ #pragma once +#include #include #include #include +#include "kernel.h" #include "kernel_log.hpp" +#include "virtual_memory.hpp" // =========================================================================== // Assertion helpers @@ -177,16 +180,24 @@ auto fifo_scheduler_test() -> bool; auto rr_scheduler_test() -> bool; auto cfs_scheduler_test() -> bool; auto idle_scheduler_test() -> bool; -auto thread_group_system_test() -> bool; -auto wait_system_test() -> bool; -auto clone_system_test() -> bool; -auto exit_system_test() -> bool; -auto ramfs_system_test() -> bool; -auto fatfs_system_test() -> bool; +auto thread_group_test() -> bool; +auto wait_test() -> bool; +auto clone_test() -> bool; +auto exit_test() -> bool; +auto ramfs_test() -> bool; +auto fatfs_test() -> bool; auto memory_test() -> bool; auto kernel_task_test() -> bool; auto user_task_test() -> bool; auto mutex_test() -> bool; +auto cross_core_test() -> bool; +auto yield_test() -> bool; +auto fork_test() -> bool; +auto signal_test() -> bool; +auto affinity_test() -> bool; +auto tick_test() -> bool; +auto zombie_reap_test() -> bool; +auto stress_test() -> bool; // =========================================================================== // QEMU exit @@ -197,19 +208,16 @@ inline void QemuExit([[maybe_unused]] bool success) { #if defined(__riscv) // sifive_test device (virt machine 默认存在,地址 0x100000) // 0x5555 = FINISHER_PASS, 0x3333 = FINISHER_FAIL - volatile auto* finisher = reinterpret_cast(0x100000); + constexpr uint64_t kSifiveTestAddr = 0x100000; + constexpr size_t kSifiveTestSize = 0x1000; + (void)VirtualMemorySingleton::instance().MapMMIO(kSifiveTestAddr, + kSifiveTestSize); + volatile auto* finisher = + reinterpret_cast(kSifiveTestAddr); *finisher = success ? 0x5555 : 0x3333; -#elif defined(__x86_64__) - // isa-debug-exit device (需要 QEMU 参数: - // -device isa-debug-exit,iobase=0xf4,iosize=0x04) - // QEMU 退出码 = (val << 1) | 1,所以 0 -> 1(pass), 1 -> 3(fail) - uint32_t code = success ? 0 : 1; - asm volatile("outl %0, %1" ::"a"(code), "Nd"(static_cast(0xf4))); #elif defined(__aarch64__) - // PSCI SYSTEM_OFF (function id = 0x84000008) - // 需要 EL2/EL3 支持(ATF 已提供) - register uint64_t x0 asm("x0") = 0x84000008; - asm volatile("hvc #0" : "+r"(x0)); + // PSCI SYSTEM_OFF,通过 SMC 调用 ATF (EL3) + cpu_io::SecureMonitorCall(cpu_io::psci::kSYSTEM_OFF, 0, 0, 0, 0, 0, 0, 0); #endif __builtin_unreachable(); } diff --git a/tests/system_test/thread_group_system_test.cpp b/tests/system_test/thread_group_test.cpp similarity index 84% rename from tests/system_test/thread_group_system_test.cpp rename to tests/system_test/thread_group_test.cpp index 234a20f9f..3f7fdccd3 100644 --- a/tests/system_test/thread_group_system_test.cpp +++ b/tests/system_test/thread_group_test.cpp @@ -27,6 +27,7 @@ std::atomic g_thread_completed{0}; std::atomic g_tests_completed{0}; std::atomic g_tests_failed{0}; +std::atomic g_local_pid_counter{SIZE_MAX / 2}; /** * @brief 线程函数,增加计数器 */ @@ -57,23 +58,23 @@ void test_thread_group_basic(void* /*arg*/) { auto leader_holder = kstd::make_unique( "ThreadGroupLeader", 10, nullptr, nullptr); auto* leader = leader_holder.get(); - leader->pid = 90000; - leader->aux->tgid = 90000; + leader->pid = g_local_pid_counter.fetch_add(1); + leader->aux->tgid = leader->pid; // 创建并加入线程组的线程 auto thread1 = kstd::make_unique( "Thread1", 10, thread_increment, reinterpret_cast(1)); - thread1->pid = 90001; + auto* thread1_raw = thread1.get(); thread1->JoinThreadGroup(leader); auto thread2 = kstd::make_unique( "Thread2", 10, thread_increment, reinterpret_cast(2)); - thread2->pid = 90002; + auto* thread2_raw = thread2.get(); thread2->JoinThreadGroup(leader); auto thread3 = kstd::make_unique( "Thread3", 10, thread_increment, reinterpret_cast(3)); - thread3->pid = 90003; + auto* thread3_raw = thread3.get(); thread3->JoinThreadGroup(leader); // 验证线程组大小 @@ -125,8 +126,8 @@ void test_thread_group_dynamic(void* /*arg*/) { auto leader_holder = kstd::make_unique("DynamicLeader", 10, nullptr, nullptr); auto* leader = leader_holder.get(); - leader->pid = 91000; - leader->aux->tgid = 91000; + leader->pid = g_local_pid_counter.fetch_add(1); + leader->aux->tgid = leader->pid; constexpr int kThreadCount = 5; etl::unique_ptr thread_holders[kThreadCount]; @@ -136,7 +137,7 @@ void test_thread_group_dynamic(void* /*arg*/) { thread_holders[i] = kstd::make_unique("DynamicThread", 10, nullptr, nullptr); threads[i] = thread_holders[i].get(); - threads[i]->pid = 91001 + i; + threads[i]->pid = g_local_pid_counter.fetch_add(1); } // 动态加入 @@ -199,8 +200,8 @@ void test_thread_group_concurrent_exit(void* /*arg*/) { auto leader_holder = kstd::make_unique( "ConcurrentLeader", 10, nullptr, nullptr); auto* leader = leader_holder.get(); - leader->pid = 92000; - leader->aux->tgid = 92000; + leader->pid = g_local_pid_counter.fetch_add(1); + leader->aux->tgid = leader->pid; // 创建多个工作线程 constexpr int kWorkerCount = 4; @@ -208,7 +209,6 @@ void test_thread_group_concurrent_exit(void* /*arg*/) { auto worker = kstd::make_unique( "ConcurrentWorker", 10, concurrent_exit_worker, reinterpret_cast(i)); - worker->pid = 92001 + i; worker->JoinThreadGroup(leader); TaskManagerSingleton::instance().AddTask(std::move(worker)); } @@ -240,37 +240,37 @@ void test_thread_group_concurrent_exit(void* /*arg*/) { /** * @brief 线程组系统测试入口 */ -auto thread_group_system_test() -> bool { +auto thread_group_test() -> bool { klog::Info("=== Thread Group System Test Suite ==="); g_tests_completed = 0; g_tests_failed = 0; - // 测试 1: 基本线程组功能 - auto test1 = kstd::make_unique( - "TestThreadGroupBasic", 10, test_thread_group_basic, nullptr); - TaskManagerSingleton::instance().AddTask(std::move(test1)); + struct SubTest { + const char* name; + void (*func)(void*); + }; - // 测试 2: 动态加入和离开 - auto test2 = kstd::make_unique( - "TestThreadGroupDynamic", 10, test_thread_group_dynamic, nullptr); - TaskManagerSingleton::instance().AddTask(std::move(test2)); + SubTest sub_tests[] = { + {"TestThreadGroupBasic", test_thread_group_basic}, + {"TestThreadGroupDynamic", test_thread_group_dynamic}, + {"TestThreadGroupConcurrentExit", test_thread_group_concurrent_exit}, + }; - // 测试 3: 并发退出 - auto test3 = kstd::make_unique( - "TestThreadGroupConcurrentExit", 10, test_thread_group_concurrent_exit, - nullptr); - TaskManagerSingleton::instance().AddTask(std::move(test3)); - - // 同步等待所有测试完成 constexpr int kExpectedTests = 3; - int timeout = 400; - while (timeout > 0) { - (void)sys_sleep(50); - if (g_tests_completed >= kExpectedTests) { - break; + + // 顺序执行:子测试共享 g_thread_completed,并行会导致交叉计数 + for (int i = 0; i < kExpectedTests; ++i) { + auto task = kstd::make_unique(sub_tests[i].name, 10, + sub_tests[i].func, nullptr); + TaskManagerSingleton::instance().AddTask(std::move(task)); + + // 等待当前子测试完成后再启动下一个 + int timeout = 400; + while (timeout > 0 && g_tests_completed < i + 1) { + (void)sys_sleep(50); + timeout--; } - timeout--; } EXPECT_EQ(g_tests_completed.load(), kExpectedTests, diff --git a/tests/system_test/tick_test.cpp b/tests/system_test/tick_test.cpp new file mode 100644 index 000000000..c634421af --- /dev/null +++ b/tests/system_test/tick_test.cpp @@ -0,0 +1,145 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include +#include + +#include "kernel.h" +#include "kernel_log.hpp" +#include "kstd_libcxx.h" +#include "kstd_memory" +#include "per_cpu.hpp" +#include "syscall.hpp" +#include "system_test.h" +#include "task_control_block.hpp" +#include "task_manager.hpp" + +namespace { + +std::atomic g_tests_completed{0}; +std::atomic g_tests_failed{0}; + +void test_tick_increments(void*) { + klog::Info("=== Tick Increments Test ==="); + + auto* sched_data = per_cpu::GetCurrentCore().sched_data; + uint64_t tick_before = sched_data->local_tick; + + (void)sys_sleep(200); + + uint64_t tick_after = sched_data->local_tick; + + if (tick_after <= tick_before) { + klog::Err( + "test_tick_increments: FAIL -- local_tick did not increase " + "(before={}, after={})", + tick_before, tick_after); + g_tests_failed++; + } else { + klog::Info("Tick Increments Test: PASSED (before={}, after={}, delta={})", + tick_before, tick_after, tick_after - tick_before); + } + + g_tests_completed++; + sys_exit(0); +} + +void test_sleep_timing(void*) { + klog::Info("=== Sleep Timing Test ==="); + + auto* sched_data = per_cpu::GetCurrentCore().sched_data; + uint64_t tick_before = sched_data->local_tick; + + (void)sys_sleep(500); + + uint64_t tick_after = sched_data->local_tick; + uint64_t delta = tick_after - tick_before; + + if (delta == 0) { + klog::Err( + "test_sleep_timing: FAIL -- tick delta is 0 after 500ms sleep " + "(before={}, after={})", + tick_before, tick_after); + g_tests_failed++; + } else { + klog::Info("Sleep Timing Test: PASSED (before={}, after={}, delta={})", + tick_before, tick_after, delta); + } + + g_tests_completed++; + sys_exit(0); +} + +void test_runtime_tracking(void*) { + klog::Info("=== Runtime Tracking Test ==="); + + auto* self = TaskManagerSingleton::instance().GetCurrentTask(); + uint64_t runtime_before = self->sched_info.total_runtime; + + // Busy-wait for at least 5 tick intervals to ensure total_runtime + // is incremented (1ms per tick at SIMPLEKERNEL_TICK=1000) + auto* sched_data = per_cpu::GetCurrentCore().sched_data; + uint64_t start_tick = sched_data->local_tick; + volatile uint64_t sink = 0; + while (sched_data->local_tick - start_tick < 5) { + sink = sink + 1; + } + (void)sink; + + uint64_t runtime_after = self->sched_info.total_runtime; + + if (runtime_after <= runtime_before) { + klog::Err( + "test_runtime_tracking: FAIL -- total_runtime did not increase " + "(before={}, after={})", + runtime_before, runtime_after); + g_tests_failed++; + } else { + klog::Info("Runtime Tracking Test: PASSED (before={}, after={}, delta={})", + runtime_before, runtime_after, runtime_after - runtime_before); + } + + g_tests_completed++; + sys_exit(0); +} + +} // namespace + +auto tick_test() -> bool { + klog::Info("=== Tick System Test Suite ==="); + + g_tests_completed = 0; + g_tests_failed = 0; + + auto& task_mgr = TaskManagerSingleton::instance(); + + auto test1 = kstd::make_unique( + "TestTickIncrements", 10, test_tick_increments, nullptr); + task_mgr.AddTask(std::move(test1)); + + auto test2 = kstd::make_unique("TestSleepTiming", 10, + test_sleep_timing, nullptr); + task_mgr.AddTask(std::move(test2)); + + auto test3 = kstd::make_unique( + "TestRuntimeTracking", 10, test_runtime_tracking, nullptr); + task_mgr.AddTask(std::move(test3)); + + constexpr int kExpectedTests = 3; + int timeout = 400; + while (timeout > 0) { + (void)sys_sleep(50); + if (g_tests_completed >= kExpectedTests) { + break; + } + timeout--; + } + + EXPECT_EQ(g_tests_completed.load(), kExpectedTests, + "All tick tests should complete"); + EXPECT_EQ(g_tests_failed.load(), 0, "No tick tests should fail"); + + klog::Info("Tick System Test Suite: COMPLETED"); + return true; +} diff --git a/tests/system_test/wait_system_test.cpp b/tests/system_test/wait_test.cpp similarity index 95% rename from tests/system_test/wait_system_test.cpp rename to tests/system_test/wait_test.cpp index b5c64fa37..03c57914c 100644 --- a/tests/system_test/wait_system_test.cpp +++ b/tests/system_test/wait_test.cpp @@ -64,9 +64,10 @@ void test_wait_basic(void* /*arg*/) { reinterpret_cast(42)); child->aux->parent_pid = current->pid; child->aux->pgid = current->aux->pgid; - Pid child_pid = child->pid; + auto* child_raw = child.get(); task_mgr.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; klog::Info("Parent: created child with PID={}", child_pid); @@ -121,8 +122,9 @@ void test_wait_any_child(void* /*arg*/) { "AnyChild", 10, child_work, reinterpret_cast(10 + i)); child->aux->parent_pid = current->pid; child->aux->pgid = current->aux->pgid; - Pid child_pid = child->pid; + auto* child_raw = child.get(); task_mgr.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; klog::Info("Parent: created child {} with PID={}", i, child_pid); } @@ -187,8 +189,9 @@ void test_wait_no_hang(void* /*arg*/) { "SlowChild", 10, slow_child_work, reinterpret_cast(1)); child->aux->parent_pid = current->pid; child->aux->pgid = current->aux->pgid; - Pid child_pid = child->pid; + auto* child_raw = child.get(); task_mgr.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; klog::Info("Parent: created slow child with PID={}", child_pid); @@ -233,17 +236,19 @@ void test_wait_process_group(void* /*arg*/) { reinterpret_cast(1)); child1->aux->parent_pid = current->pid; child1->aux->pgid = current->aux->pgid; // 同一进程组 - Pid child1_pid = child1->pid; Pid child1_pgid = child1->aux->pgid; + auto* child1_raw = child1.get(); task_mgr.AddTask(std::move(child1)); + Pid child1_pid = child1_raw->pid; auto child2 = kstd::make_unique("PgChild2", 10, child_work, reinterpret_cast(2)); child2->aux->parent_pid = current->pid; child2->aux->pgid = 9999; // 不同进程组 - Pid child2_pid = child2->pid; Pid child2_pgid = child2->aux->pgid; + auto* child2_raw = child2.get(); task_mgr.AddTask(std::move(child2)); + Pid child2_pid = child2_raw->pid; klog::Info("Parent: created child1 (pgid={}) and child2 (pgid={})", child1_pgid, child2_pgid); @@ -295,8 +300,9 @@ void test_wait_zombie_reap(void* /*arg*/) { "ZombieChild", 10, zombie_child_work, reinterpret_cast(1)); child->aux->parent_pid = current->pid; child->aux->pgid = current->aux->pgid; - Pid child_pid = child->pid; + auto* child_raw = child.get(); task_mgr.AddTask(std::move(child)); + Pid child_pid = child_raw->pid; klog::Info("Parent: created zombie child with PID={}", child_pid); @@ -323,7 +329,7 @@ void test_wait_zombie_reap(void* /*arg*/) { /** * @brief Wait 系统测试入口 */ -auto wait_system_test() -> bool { +auto wait_test() -> bool { klog::Info("=== Wait System Test Suite ==="); g_tests_completed = 0; diff --git a/tests/system_test/yield_test.cpp b/tests/system_test/yield_test.cpp new file mode 100644 index 000000000..3ece46cac --- /dev/null +++ b/tests/system_test/yield_test.cpp @@ -0,0 +1,89 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include +#include + +#include "kernel.h" +#include "kernel_log.hpp" +#include "kstd_libcxx.h" +#include "kstd_memory" +#include "syscall.hpp" +#include "system_test.h" +#include "task_control_block.hpp" +#include "task_manager.hpp" + +namespace { + +/// Test 1: sys_yield returns 0 +std::atomic g_yield_ret{-1}; + +void yield_basic_work(void* /*arg*/) { + int ret = sys_yield(); + g_yield_ret.store(ret); + sys_exit(0); +} + +/// Test 2: yield preserves execution order fairness +std::atomic g_a_count{0}; +std::atomic g_b_count{0}; +std::atomic g_fairness_done{false}; + +void yield_task_a(void* /*arg*/) { + for (int i = 0; i < 10; ++i) { + g_a_count++; + (void)sys_yield(); + } + sys_exit(0); +} + +void yield_task_b(void* /*arg*/) { + for (int i = 0; i < 10; ++i) { + g_b_count++; + (void)sys_yield(); + } + g_fairness_done.store(true); + sys_exit(0); +} + +} // namespace + +auto yield_test() -> bool { + klog::Info("yield_test: start"); + auto& tm = TaskManagerSingleton::instance(); + + // Test 1: basic yield returns 0 + g_yield_ret = -1; + auto t1 = kstd::make_unique("YieldBasic", 10, + yield_basic_work, nullptr); + tm.AddTask(std::move(t1)); + + int timeout = 100; + while (timeout-- > 0 && g_yield_ret.load() == -1) { + (void)sys_sleep(50); + } + EXPECT_EQ(g_yield_ret.load(), 0, "sys_yield should return 0"); + + // Test 2: yield fairness — both tasks make progress + g_a_count = 0; + g_b_count = 0; + g_fairness_done = false; + + auto ta = + kstd::make_unique("YieldA", 10, yield_task_a, nullptr); + auto tb = + kstd::make_unique("YieldB", 10, yield_task_b, nullptr); + tm.AddTask(std::move(ta)); + tm.AddTask(std::move(tb)); + + timeout = 100; + while (timeout-- > 0 && !g_fairness_done.load()) { + (void)sys_sleep(50); + } + EXPECT_EQ(g_a_count.load(), 10, "Task A should complete 10 iterations"); + EXPECT_EQ(g_b_count.load(), 10, "Task B should complete 10 iterations"); + + klog::Info("yield_test: PASS"); + return true; +} diff --git a/tests/system_test/zombie_reap_test.cpp b/tests/system_test/zombie_reap_test.cpp new file mode 100644 index 000000000..7ab55be6c --- /dev/null +++ b/tests/system_test/zombie_reap_test.cpp @@ -0,0 +1,280 @@ +/** + * @copyright Copyright The SimpleKernel Contributors + */ + +#include +#include + +#include "kernel.h" +#include "kernel_log.hpp" +#include "kstd_libcxx.h" +#include "kstd_memory" +#include "syscall.hpp" +#include "system_test.h" +#include "task_control_block.hpp" +#include "task_manager.hpp" + +namespace { + +std::atomic g_tests_completed{0}; +std::atomic g_tests_failed{0}; + +// --------------------------------------------------------------------------- +// test_zombie_reap +// Spawn a child that exits with code 77, Wait for it, verify exit code and +// that FindTask returns nullptr after reap. +// --------------------------------------------------------------------------- + +void zombie_child_work(void* /*arg*/) { + klog::Info("zombie_child_work: exiting with code 77"); + sys_exit(77); +} + +void test_zombie_reap(void* /*arg*/) { + klog::Info("=== Zombie Reap Test ==="); + bool passed = true; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + + auto child = kstd::make_unique("ZRChild", 10, + zombie_child_work, nullptr); + child->aux->parent_pid = self->pid; + auto* raw = child.get(); + tm.AddTask(std::move(child)); + Pid child_pid = raw->pid; + + klog::Info("test_zombie_reap: spawned child pid={}", child_pid); + + // Wait for child to exit + int status = 0; + auto result = tm.Wait(child_pid, &status, false, false); + if (!result.has_value() || result.value() != child_pid) { + klog::Err("test_zombie_reap: Wait failed (result={})", + result.has_value() ? static_cast(result.value()) : -1); + passed = false; + } else if (status != 77) { + klog::Err("test_zombie_reap: exit code {} (expected 77)", status); + passed = false; + } + + // Sleep briefly then verify child is reaped from the task table + (void)sys_sleep(100); + + auto* found = tm.FindTask(child_pid); + if (found != nullptr) { + klog::Err("test_zombie_reap: FindTask({}) != nullptr after reap", + child_pid); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Zombie Reap Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_orphan_reparent +// Spawn a "parent" task that itself spawns a "grandchild" then exits quickly. +// Grandchild sleeps 500ms then reads its own parent_pid into an atomic. +// Outer test waits and checks that the grandchild's parent was reparented. +// --------------------------------------------------------------------------- + +std::atomic g_grandchild_parent{0}; +std::atomic g_mid_parent_pid{0}; + +void grandchild_work(void* /*arg*/) { + (void)sys_sleep(500); + auto* current = TaskManagerSingleton::instance().GetCurrentTask(); + g_grandchild_parent.store(current->aux->parent_pid); + klog::Info("grandchild_work: parent_pid={}", current->aux->parent_pid); + sys_exit(0); +} + +void mid_parent_work(void* /*arg*/) { + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + g_mid_parent_pid.store(self->pid); + + // Spawn grandchild + auto gc = kstd::make_unique("Grandchild", 10, + grandchild_work, nullptr); + gc->aux->parent_pid = self->pid; + tm.AddTask(std::move(gc)); + + klog::Info("mid_parent_work: spawned grandchild, exiting quickly"); + // Exit quickly so grandchild becomes orphan and gets reparented + (void)sys_sleep(50); + sys_exit(0); +} + +void test_orphan_reparent(void* /*arg*/) { + klog::Info("=== Orphan Reparent Test ==="); + bool passed = true; + + g_grandchild_parent = 0; + g_mid_parent_pid = 0; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + + auto mid = kstd::make_unique("MidParent", 10, + mid_parent_work, nullptr); + mid->aux->parent_pid = self->pid; + auto* mid_raw = mid.get(); + tm.AddTask(std::move(mid)); + Pid mid_pid = mid_raw->pid; + + // Wait for mid-parent to exit first + int status = 0; + (void)tm.Wait(mid_pid, &status, false, false); + + // Wait for mid-parent pid to be recorded + int timeout = 100; + while (timeout-- > 0 && g_mid_parent_pid.load() == 0) { + (void)sys_sleep(50); + } + + Pid original_parent = g_mid_parent_pid.load(); + + // Wait for grandchild to report its parent_pid + timeout = 200; + while (timeout-- > 0 && g_grandchild_parent.load() == 0) { + (void)sys_sleep(50); + } + + Pid reparented_parent = g_grandchild_parent.load(); + + if (reparented_parent == 0) { + klog::Err("test_orphan_reparent: grandchild did not report parent_pid"); + passed = false; + } else if (reparented_parent == original_parent) { + klog::Err( + "test_orphan_reparent: grandchild parent_pid unchanged (still {})", + original_parent); + passed = false; + } else { + klog::Info("test_orphan_reparent: grandchild reparented from {} to {}", + original_parent, reparented_parent); + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Orphan Reparent Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +// --------------------------------------------------------------------------- +// test_wait_multi_children +// Spawn 3 children (each exits with unique code after 50ms sleep). Parent +// uses Wait(-1, &status, true, false) in a loop to collect all 3. Verify +// collected count == 3. +// --------------------------------------------------------------------------- + +void multi_child_work(void* arg) { + int code = static_cast(reinterpret_cast(arg)); + (void)sys_sleep(50); + klog::Info("multi_child_work: exiting with code {}", code); + sys_exit(code); +} + +void test_wait_multi_children(void* /*arg*/) { + klog::Info("=== Wait Multi Children Test ==="); + bool passed = true; + + auto& tm = TaskManagerSingleton::instance(); + auto* self = tm.GetCurrentTask(); + + constexpr int kChildCount = 3; + int exit_codes[kChildCount] = {10, 20, 30}; + + for (int i = 0; i < kChildCount; ++i) { + auto child = kstd::make_unique( + "MultiChild", 10, multi_child_work, + reinterpret_cast(static_cast(exit_codes[i]))); + child->aux->parent_pid = self->pid; + auto* raw = child.get(); + tm.AddTask(std::move(child)); + klog::Info("test_wait_multi_children: spawned child {} pid={}", i, + raw->pid); + } + + // Collect all children via Wait(-1, ..., nohang=true, ...) + int collected = 0; + int retries = 400; // 400 * 50ms = 20s timeout + + while (collected < kChildCount && retries > 0) { + int status = 0; + auto result = tm.Wait(static_cast(-1), &status, true, false); + + if (result.has_value() && result.value() > 0) { + klog::Info("test_wait_multi_children: reaped pid={} status={}", + result.value(), status); + collected++; + } else { + (void)sys_sleep(50); + retries--; + } + } + + if (collected != kChildCount) { + klog::Err("test_wait_multi_children: collected {} of {} children", + collected, kChildCount); + passed = false; + } + + if (!passed) { + g_tests_failed++; + } + g_tests_completed++; + klog::Info("Wait Multi Children Test: {}", passed ? "PASSED" : "FAILED"); + sys_exit(passed ? 0 : 1); +} + +} // namespace + +/** + * @brief Zombie reap system test entry + */ +auto zombie_reap_test() -> bool { + klog::Info("===== Zombie Reap System Test Start ====="); + + g_tests_completed = 0; + g_tests_failed = 0; + + auto& tm = TaskManagerSingleton::instance(); + + auto t1 = kstd::make_unique("TestZombieReap", 10, + test_zombie_reap, nullptr); + tm.AddTask(std::move(t1)); + + auto t2 = kstd::make_unique("TestOrphanReparent", 10, + test_orphan_reparent, nullptr); + tm.AddTask(std::move(t2)); + + auto t3 = kstd::make_unique( + "TestWaitMultiChildren", 10, test_wait_multi_children, nullptr); + tm.AddTask(std::move(t3)); + + constexpr int kExpectedTests = 3; + int timeout = 400; // 400 * 50ms = 20s + while (timeout > 0) { + (void)sys_sleep(50); + if (g_tests_completed.load() >= kExpectedTests) { + break; + } + timeout--; + } + + EXPECT_EQ(g_tests_completed.load(), kExpectedTests, + "All zombie reap tests completed"); + EXPECT_EQ(g_tests_failed.load(), 0, "No zombie reap tests failed"); + + klog::Info("===== Zombie Reap System Test End ====="); + return true; +} diff --git a/tests/unit_test/README.md b/tests/unit_test/README.md index e0f99d809..0914f9e81 100644 --- a/tests/unit_test/README.md +++ b/tests/unit_test/README.md @@ -22,10 +22,6 @@ 测试 riscv64/cpu.hpp 相关内容 -- x86_64_cpu_test.cpp - - 测试 x86_64/cpu.hpp 相关内容 - - aarch64_cpu_test.cpp 测试 aarch64/cpu.hpp 相关内容 diff --git a/tools/.pre-commit-config.yaml.in b/tools/.pre-commit-config.yaml.in index aff684096..210cccf9f 100644 --- a/tools/.pre-commit-config.yaml.in +++ b/tools/.pre-commit-config.yaml.in @@ -29,15 +29,7 @@ repos: # - -extra-arg=--std=c++2b # - --fix # - --header-filter=^(@CMAKE_BINARY_DIR@/src/).* - # - --exclude-header-filter=^(?@CMAKE_BINARY_DIR@/src/arch/x86_64|@CMAKE_BINARY_DIR@/src/arch/aarch64).* - # - id: clang-tidy - # args: - # - --checks=.clang-tidy - # - -p=@CMAKE_BINARY_DIR@/build_x86_64/compiler_commands.json - # - -extra-arg=--std=c++2b - # - --fix - # - --header-filter=^(?@CMAKE_BINARY_DIR@/src/).* - # - --exclude-header-filter=^(?@CMAKE_BINARY_DIR@/src/arch/riscv_64|@CMAKE_BINARY_DIR@/src/arch/aarch64).* + # - --exclude-header-filter=^(?@CMAKE_BINARY_DIR@/src/arch/aarch64).* # - id: clang-tidy # args: # - --checks=.clang-tidy @@ -45,7 +37,7 @@ repos: # - -extra-arg=--std=c++2b # - --fix # - --header-filter=^(?@CMAKE_BINARY_DIR@/src/).* - # - --exclude-header-filter=^(?@CMAKE_BINARY_DIR@/src/arch/riscv64|@CMAKE_BINARY_DIR@/src/arch/x86_64).* + # - --exclude-header-filter=^(?@CMAKE_BINARY_DIR@/src/arch/riscv64).* - repo: https://github.com/koalaman/shellcheck-precommit rev: v0.11.0 diff --git a/tools/Dockerfile b/tools/Dockerfile deleted file mode 100644 index 46ea3c44a..000000000 --- a/tools/Dockerfile +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright The SimpleKernel Contributors - -FROM ubuntu:latest AS basic_os -RUN export DEBIAN_FRONTEND=noninteractive && apt update && apt upgrade -y -ENV LANG=C.UTF-8 -ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt - -FROM basic_os AS basic_tools -RUN apt install --no-install-recommends --fix-missing -y \ - locales \ - ca-certificates \ - git \ - git-email \ - curl \ - wget \ - openssh-server \ - rsync \ - sudo \ - zsh \ - zip \ - unzip \ - telnet \ - tar \ - tmux \ - ncat \ - socat \ - lsof \ - bc \ - cpio \ - tftpd-hpa \ - uuid-dev -RUN apt install --no-install-recommends --fix-missing -y \ - file \ - vim \ - doxygen \ - valgrind \ - graphviz \ - pre-commit \ - clang-format \ - clang-tidy \ - uncrustify \ - iwyu \ - cppcheck \ - cpplint \ - shellcheck \ - cmake-format \ - lcov -RUN locale-gen en_US.UTF-8 && locale-gen zh_CN.UTF-8 \ - && mkdir -p /var/run/sshd \ - && echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config \ - && echo 'PubkeyAuthentication yes' >> /etc/ssh/sshd_config \ - && echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config \ - && ssh-keygen -A \ - && service ssh restart \ - && git clone https://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh \ - && cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc \ - && cp -r ~/.oh-my-zsh /home/ubuntu/ \ - && cp ~/.oh-my-zsh/templates/zshrc.zsh-template /home/ubuntu/.zshrc \ - && chsh -s /bin/zsh root \ - && usermod -s /bin/zsh root \ - && chsh -s /bin/zsh ubuntu \ - && usermod -s /bin/zsh ubuntu \ - && chmod 777 /srv/tftp - -FROM basic_tools AS dev_tools -RUN apt install --no-install-recommends --fix-missing -y \ - build-essential \ - binutils \ - make \ - cmake \ - git \ - qemu-system \ - gdb-multiarch \ - grub-common \ - xorriso \ - libgtest-dev \ - pkg-config \ - flex \ - bison \ - device-tree-compiler \ - python3-dev \ - python3-cryptography \ - python3-pyelftools \ - python3-setuptools \ - libgnutls28-dev \ - gawk \ - u-boot-tools \ - swig \ - p7zip-full \ - mtools \ - dosfstools -RUN apt install --no-install-recommends --fix-missing -y \ - gcc \ - g++ \ - gcc-riscv64-linux-gnu \ - g++-riscv64-linux-gnu \ - gcc-aarch64-linux-gnu \ - g++-aarch64-linux-gnu \ - gcc-arm-linux-gnueabihf \ - g++-arm-linux-gnueabihf \ - gcc-x86-64-linux-gnu \ - g++-x86-64-linux-gnu - -FROM dev_tools AS simple_kernel -EXPOSE 22 -USER root -WORKDIR /root -RUN apt autoremove -y && apt clean -y && rm -rf /var/lib/apt/lists/* \ - && git config --global --add safe.directory /root/SimpleKernel -ENTRYPOINT ["/bin/zsh"] diff --git a/tools/README.md b/tools/README.md index 227fb0de8..630fd704d 100644 --- a/tools/README.md +++ b/tools/README.md @@ -27,11 +27,3 @@ - riscv64_qemu_virt.its.in riscv64 qemu FIT 模版 - -- x86_64_boot_scr.txt - - x86_64 uboot 启动命令 - -- x86_64_qemu_virt.its.in - - x86_64 qemu FIT 模版 diff --git a/tools/x86_64_boot_scr.txt b/tools/x86_64_boot_scr.txt deleted file mode 100644 index 373a17801..000000000 --- a/tools/x86_64_boot_scr.txt +++ /dev/null @@ -1,3 +0,0 @@ -bootp -tftp $kernel_addr_r bin/boot.fit -bootm $kernel_addr_r diff --git a/tools/x86_64_qemu_virt.its.in b/tools/x86_64_qemu_virt.its.in deleted file mode 100644 index ac543561f..000000000 --- a/tools/x86_64_qemu_virt.its.in +++ /dev/null @@ -1,39 +0,0 @@ -/dts-v1/; - -/ { - description = "SimpleKernel x86_64 U-Boot FIT Image"; - #address-cells = <1>; - - images { - kernel { - description = "@DESC@"; - // 内核路径 - data = /incbin/("@KERNEL_PATH@"); - type = "kernel"; - // 架构类型 - arch = "x86_64"; - os = "elf"; - // 压缩方式 - compression = "none"; - // 内核加载地址 - load = <0x400000>; - // 内核入口地址 - entry = <0x400000>; - hash-1 { - algo = "sha1"; - }; - }; - }; - - configurations { - default = "config"; - - config { - description = "Default Boot Configuration"; - kernel = "kernel"; - hash-1 { - algo = "sha1"; - }; - }; - }; -};