rCore Documentation and Learning Notes

website·Indexed 29 days ago

https://rcore.jiangshengdev.com

Technical 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.

Tokens
11K
Snippets
34
Records
70
Agent score
49%

What's inside rCore

  1. Overview of Intrusive Linked List in rCore Buddy System

    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.
  2. Understand RISC-V atomic memory ordering bits

    RISC-V atomic instructions include two bits, aq (acquire) and rl (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.
  3. 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 the satp register 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.
  4. 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 supports sym and const operands because the compiler cannot generate additional prologue/epilogue code.
    • global_asm!: Used for assembly outside of any function. It only supports sym and const operands.
  5. 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-riscv64
  6. Initialize Buddy System Heap

    The Buddy System heap is initialized using Heap::<ORDER>::new(). This creates an array of empty intrusive linked lists called free_list with a length of ORDER. The statistics user, allocated, and total are all initialized to 0. Each index in the free_list manages free memory blocks of size $2^{index}$ bytes. For example, if ORDER = 32, index 0 manages 1B blocks and index 31 manages 2GiB blocks.
  7. 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 the fw_dynamic_info structure.

    Execution Flow:

    1. The CPU starts at 0x1000.
    2. It calculates the address of the fw_dyn (firmware dynamic info) structure and stores it in register a2.
    3. It retrieves the mhartid into a0.
    4. It loads the next stage address from the fw_dyn structure into t0.
    5. It jumps to the address in t0 using jr 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
  8. 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 the APPS definition in the user/Makefile to 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))
  9. 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, execute disconnect before the __restore function finishes (before the sret instruction). Then, configure a remote debug session in CLion to connect to the user program's symbols.
    # In GDB console before sret is executed
    disconnect
  10. Install 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 to riscv64-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-gdb
  11. Install GDB Dashboard for rCore debugging

    GDB Dashboard provides a visual interface for GDB. It is installed by downloading the .gdbinit configuration file to the home directory.
    brew install wget
    wget -P ~ https://github.com/cyrus-and/gdb-dashboard/raw/master/.gdbinit
  12. Allocate memory using Buddy System

    The allocation process follows these steps:

    1. Normalize the Layout to determine the minimum order (power-of-two size) that satisfies both the size and alignment requirements.
    2. 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.
    3. If no blocks are available up to the maximum order, an OutOfMemory error is returned.

    Upon success, the user (requested bytes) and allocated (actual block size) statistics are updated.