rCore Documentation and Learning Notes
website·Indexed 29 days ago
https://rcore.jiangshengdev.comTechnical documentation and study notes for the rCore OS project. Covers RISC-V Privileged ISA v1.13 and RV32I Base Integer Instruction Set v2.1, Rust language reference points including inline assembly and Send/Sync concurrency, memory management via the Buddy System and virtual memory page tables, and environment setup for macOS.
What's inside rCore
- The rCore Buddy System uses intrusive linked lists to manage free memory blocks of various orders. Each order maintains a linked list of available blocks of the same size. In an intrusive list, the 'next' pointer is stored within the memory block itself rather than in a separate wrapper node, minimizing memory overhead.
Understand RISC-V atomic memory ordering bits
RISC-V atomic instructions include two bits,aq(acquire) andrl(release), which specify additional memory ordering constraints observed by other hardware threads (harts). These bits are used to prevent the reordering of memory operations across the atomic instruction, ensuring that critical sections (like those in a spinlock) are executed atomically and in the correct sequence relative to other threads.Understand rCore Kernel Page Table Structure
rCore uses a simple paging virtual memory management mechanism. The kernel page table is structured as a multi-level page table where thesatpregister holds the root physical page number (PPN). Most kernel virtual pages use identity mapping (VPN == PPN), while the trampoline mapping is non-identity and the kernel stack uses frame mapping.Use inline assembly macros in Rust
Rust provides three primary macros for embedding hand-written assembly code into the compiler's output. These are stable on x86, x86-64, ARM, AArch64, Arm64EC, RISC-V, LoongArch, and s390x architectures.
asm!: Used for inline assembly within a function.naked_asm!: Used to define the entire body of a function. It only supportssymandconstoperands because the compiler cannot generate additional prologue/epilogue code.global_asm!: Used for assembly outside of any function. It only supportssymandconstoperands.
Build and install QEMU 7.0.0 from source on macOS
The rCore experiment requires QEMU version 7.0.0. Since Homebrew no longer provides this version, it must be compiled from source. This process is specifically tested for Apple M1/M2 chips on macOS Sequoia 15.5 or later.# Install build dependencies brew install wget ninja pkgconf glib meson pixman # Download and extract source mkdir -p ~/Develop cd ~/Develop wget https://download.qemu.org/qemu-7.0.0.tar.xz tar -xf qemu-7.0.0.tar.xz # Configure and build cd ~/Develop/qemu-7.0.0 ./configure --target-list=riscv64-softmmu make -j$(sysctl -n hw.ncpu) # Rename the executable for compatibility with rCore scripts cd ~/Develop/qemu-7.0.0/build cp qemu-system-riscv64-unsigned qemu-system-riscv64Initialize Buddy System Heap
The Buddy System heap is initialized usingHeap::<ORDER>::new(). This creates an array of empty intrusive linked lists calledfree_listwith a length ofORDER. The statisticsuser,allocated, andtotalare all initialized to 0. Each index in thefree_listmanages free memory blocks of size $2^{index}$ bytes. For example, ifORDER = 32, index 0 manages 1B blocks and index 31 manages 2GiB blocks.Analyze the RISC-V Reset Vector boot sequence
Upon power-on, the QEMU RISC-V virt platform executes a set of instructions starting at the Reset Vector (
0x1000). These instructions initialize the system and jump to the next boot stage using thefw_dynamic_infostructure.Execution Flow:
- The CPU starts at
0x1000. - It calculates the address of the
fw_dyn(firmware dynamic info) structure and stores it in registera2. - It retrieves the
mhartidintoa0. - It loads the next stage address from the
fw_dynstructure intot0. - It jumps to the address in
t0usingjr t0.
=> 0x1000: auipc t0,0x0 # Calculate base address 0x1004: addi a2,t0,40 # a2 = address of fw_dyn 0x1008: csrr a0,mhartid # Get hart ID 0x100c: ld a1,32(t0) # Load from fw_dyn 0x1010: ld t0,24(t0) # Load next_addr into t0 0x1014: jr t0 # Jump to next boot stage- The CPU starts at
Isolate a single user program for debugging
Because multiple user programs may have overlapping memory addresses, it is recommended to debug only one program at a time to avoid breakpoint interference. This is achieved by modifying theAPPSdefinition in theuser/Makefileto target a specific file.# Example: Modify user/Makefile to only build ch2b_hello_world.rs else ifeq ($(BASE), 1) # Basic tests only APPS := $(foreach T, $(TESTS), $(wildcard $(APP_DIR)/ch2b_hello_world.rs))Debug user programs in rCore using CLion
To debug user-level programs in rCore, you must first disconnect the kernel debugger before the CPU switches to user mode. In GDB, executedisconnectbefore the__restorefunction finishes (before thesretinstruction). Then, configure a remote debug session in CLion to connect to the user program's symbols.# In GDB console before sret is executed disconnectInstall and configure RISC-V GDB on macOS
Install the RISC-V GDB toolchain via Homebrew. Because rCore scripts expect a specific binary name, the installed executable must be copied or symlinked toriscv64-unknown-elf-gdb.# Install GDB brew install riscv64-elf-gdb # Rename executable for script compatibility cd /opt/homebrew/bin/ cp riscv64-elf-gdb riscv64-unknown-elf-gdb # Verify installation riscv64-unknown-elf-gdbInstall GDB Dashboard for rCore debugging
GDB Dashboard provides a visual interface for GDB. It is installed by downloading the.gdbinitconfiguration file to the home directory.brew install wget wget -P ~ https://github.com/cyrus-and/gdb-dashboard/raw/master/.gdbinitAllocate memory using Buddy System
The allocation process follows these steps:
- Normalize the
Layoutto determine the minimum order (power-of-two size) that satisfies both the size and alignment requirements. - Search for an available block in the free list of that order:
- If a block exists, it is popped and returned.
- If no block exists, the allocator searches higher orders for a block to borrow. The borrowed block is recursively split into two halves until a block of the target order is created.
- If no blocks are available up to the maximum order, an
OutOfMemoryerror is returned.
Upon success, the
user(requested bytes) andallocated(actual block size) statistics are updated.- Normalize the