Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Riscv Privileged

ASecurity

Use when writing RISC-V M-mode or S-mode code: CSRs, trap handlers, PLIC and CLINT interrupts, OpenSBI payloads, Sv39 or Sv48 page tables, or QEMU virt boot. Not for user-mode asm: use assembly-riscv.

54 stars
0 votes
0 copies
1 views
Added 9/20/2026
ai-agentsgobashtestinggit

Works with

cli

Security Analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned 9/20/2026

$npx -y skills add OutlineDriven/outline-driven-development --skill riscv-privileged --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Riscv Privileged?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Riscv Privileged
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/outlinedriven-riscv-privileged-outline-driven-development/badge)](https://www.skillsdirectory.com/skills/outlinedriven-riscv-privileged-outline-driven-development)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: riscv-privileged
description: 'Use when writing RISC-V M-mode or S-mode code: CSRs, trap handlers, PLIC and CLINT interrupts, OpenSBI payloads, Sv39 or Sv48 page tables, or QEMU virt boot. Not for user-mode asm: use assembly-riscv.'
disable-model-invocation: true
---

# RISC-V privileged architecture

## Contract

| Field | Bound contract |
|---|---|
| Trigger | A kernel, hypervisor, or firmware for RISC-V needs privilege modes, CSR access, a trap entry, timer or external interrupts, OpenSBI integration, virtual memory, or a QEMU `virt` boot and GDB session. |
| Authority | Reversible local: writes only the kernel or firmware sources in the project directory the user names and the OpenSBI build output under its own `build/`; rollback is reverting the sources and deleting `build/`. No remote mutation. |
| Side effect | New or edited sources, an OpenSBI firmware image, and QEMU processes started for testing. |
| Done | The image boots on `qemu-system-riscv64 -machine virt`, an `ecall` from U-mode reaches the trap handler with `scause = 8`, the timer interrupt fires through SBI, a PLIC source is claimed and completed, and the kernel runs with `satp` pointing at a page table it built. |

## Inputs

- Which modes the target implements: M, S, and U (Linux-class), or M and U only (embedded).
- XLEN (RV32 or RV64) and the ISA string the toolchain targets (`-march=rv64gc`).
- Whether OpenSBI runs below the kernel (S-mode kernel) or the kernel owns M-mode.
- Toolchain: `riscv64-unknown-elf-gcc` or a Linux-target cross GCC, and a GDB that knows RISC-V (`gdb-multiarch`, or the toolchain's own).

## Procedure

1. Fix the mode layout. M-mode runs firmware (OpenSBI) and has every right; S-mode runs the kernel; U-mode runs applications. An embedded part with no S-mode runs M and U only, and the "kernel" is the M-mode firmware. Done when: the project states which mode each component runs in and which CSR set (`m*` or `s*`) it therefore touches.

2. Access CSRs through inline assembly wrappers. Done when: every CSR read and write goes through one wrapper per register, so a mode change is one edit.

   | CSR | Mode | Holds |
   |---|---|---|
   | `mstatus`, `sstatus` | M, S | Global interrupt enable, previous privilege, FS and XS state |
   | `mtvec`, `stvec` | M, S | Trap vector base and mode (direct or vectored) |
   | `mepc`, `sepc` | M, S | PC at the trap |
   | `mcause`, `scause` | M, S | Trap cause; top bit set means interrupt |
   | `mtval`, `stval` | M, S | Faulting address or instruction bits |
   | `mie`, `sie` | M, S | Per-source interrupt enable |
   | `mip`, `sip` | M, S | Per-source interrupt pending |
   | `satp` | S | Translation mode, ASID, root page table PPN |

   ```c
   static inline uint64_t read_satp(void) {
       uint64_t v; asm volatile("csrr %0, satp" : "=r"(v)); return v;
   }
   static inline void write_stvec(const void *handler) {
       asm volatile("csrw stvec, %0" :: "r"(handler));
   }
   ```

3. Decode traps. `scause` bit 63 (RV64) distinguishes interrupts from exceptions; the low bits give the code. Done when: the handler dispatches the codes below and panics on any other with `scause`, `sepc`, and `stval` printed.

   | `scause` | Kind | Meaning |
   |---|---|---|
   | interrupt, code 1 | Interrupt | Supervisor software interrupt |
   | interrupt, code 5 | Interrupt | Supervisor timer interrupt |
   | interrupt, code 9 | Interrupt | Supervisor external interrupt (PLIC) |
   | 8 | Exception | `ecall` from U-mode |
   | 12, 13, 15 | Exception | Instruction, load, store page fault |
   | 2 | Exception | Illegal instruction |

   ```c
   void handle_trap(uint64_t scause, uint64_t sepc, uint64_t stval) {
       if (scause >> 63) {
           switch (scause & 0xff) {
           case 5: timer_interrupt(); break;
           case 9: external_interrupt(); break;
           default: panic("interrupt %lu", scause & 0xff);
           }
           return;
       }
       switch (scause) {
       case 8:  handle_syscall(); break;                /* sepc += 4 before sret */
       case 12: case 13: case 15: handle_page_fault(stval, scause); break;
       default: panic("exception %lu sepc=%lx stval=%lx", scause, sepc, stval);
       }
   }
   ```

4. Write the trap entry. In direct mode every trap lands at `stvec`; the entry swaps to the kernel stack through `sscratch`, saves the registers, calls the C handler, restores, and executes `sret`. The spec requires `BASE` to be 4-byte aligned, and vectored mode (`MODE = 1`) jumps to `BASE + 4 * cause` for interrupts. Done when: an `ecall` from U-mode arrives in `handle_trap` with `scause = 8` and returns to the instruction after it.

   ```
   .section .text.trap
   .globl trap_entry
   .align 4
   trap_entry:
       csrrw sp, sscratch, sp      # switch to the kernel stack
       # push the caller-saved and callee-saved registers
       csrr  a0, scause
       csrr  a1, sepc
       csrr  a2, stval
       call  handle_trap
       # pop the registers
       csrrw sp, sscratch, sp
       sret
   ```

   ```c
   write_stvec(trap_entry);   /* low two bits 0: direct mode */
   ```

5. Wire the interrupt controllers. On QEMU `virt` the CLINT (or ACLINT with `-machine virt,aclint=on`) provides per-hart timer (`mtime`, `mtimecmp`) and software (`msip`) interrupts, and the PLIC routes device interrupts (UART, virtio) with per-source priority, per-context enable, and a claim and complete register pair. An S-mode kernel sets timers through SBI rather than touching `mtimecmp`, which is M-mode memory. Done when: a PLIC source (the UART) is claimed and completed once per interrupt and the timer fires at the programmed interval.

   ```c
   uint32_t irq = plic_claim(hart_context);   /* read claim register */
   handle_device_irq(irq);
   plic_complete(hart_context, irq);          /* write the same value back */
   ```

6. Build OpenSBI with the kernel as payload when the kernel is S-mode. `FW_PAYLOAD_PATH` takes the image file of the next stage (a flat binary, not the ELF); `FW_PAYLOAD_OFFSET` is the payload offset from the OpenSBI load address, and it must equal where the kernel is linked to run. Done when: `build/platform/generic/firmware/fw_payload.elf` exists and boots the kernel on QEMU.

   ```bash
   git clone https://github.com/riscv-software-src/opensbi
   make -C opensbi PLATFORM=generic FW_PAYLOAD=y \
        FW_PAYLOAD_PATH=../kernel.bin FW_PAYLOAD_OFFSET=0x200000
   # QEMU virt loads OpenSBI at 0x80000000, so 0x200000 puts the kernel at 0x80200000
   ```

   SBI calls are `ecall` from S-mode with the extension ID in `a7` and the function ID in `a6`; the TIME extension ID is `0x54494D45` and `sbi_set_timer` is function 0. Base, TIME, IPI, RFENCE, and HSM are the extensions a kernel uses first.

   ```c
   struct sbiret sbi_set_timer(uint64_t stime) {
       return sbi_ecall(0x54494D45, 0, stime, 0, 0, 0, 0, 0);
   }
   ```

7. Build page tables. Sv39 translates 39-bit virtual addresses through three levels; Sv48 uses four levels and 48 bits. On RV64 `satp` holds `MODE` in bits 63 to 60 (8 for Sv39, 9 for Sv48), a 16-bit ASID, and the 44-bit root PPN. Map the kernel's own code and data before writing `satp`, or the instruction after the write faults. Done when: the kernel runs with `satp` enabled and a user page mapped with `PTE_U` is readable from U-mode and faults from S-mode without `SUM` set.

   ```c
   #define PTE_V (1UL << 0)
   #define PTE_R (1UL << 1)
   #define PTE_W (1UL << 2)
   #define PTE_X (1UL << 3)
   #define PTE_U (1UL << 4)

   uint64_t *walk_create(uint64_t *root, uint64_t va, int alloc);
   void map_page(uint64_t *root, uint64_t va, uint64_t pa, int perm);
   ```

   Issue `sfence.vma` after changing a mapping the hart may have cached.

8. Boot and debug on QEMU `virt`. `-bios default` loads QEMU's bundled OpenSBI and jumps to `-kernel` in S-mode; `-bios none` runs the kernel in M-mode at `0x80000000`. `-s -S` opens a GDB stub on port 1234 and waits. Done when: GDB stops at the kernel entry after `target remote :1234`.

   ```bash
   qemu-system-riscv64 -machine virt -cpu rv64 -m 128M \
       -kernel kernel.elf -bios default \
       -serial mon:stdio -display none -no-reboot

   qemu-system-riscv64 -machine virt -m 128M \
       -kernel opensbi/build/platform/generic/firmware/fw_payload.elf \
       -bios none -nographic

   qemu-system-riscv64 -machine virt -m 128M -kernel kernel.elf -bios default -nographic -s -S &
   gdb-multiarch kernel.elf -ex "target remote :1234"
   ```

9. Read xv6-riscv when a reference is needed. `kernel/start.c` (M-mode to S-mode handoff), `kernel/trap.c`, `kernel/vm.c`, and `kernel/plic.c` are small and map onto steps 3 to 7. Done when: each step above has been compared to its xv6 counterpart.

   ```bash
   git clone https://github.com/mit-pdos/xv6-riscv
   make -C xv6-riscv qemu
   ```

## Failure and recovery

| Symptom | Cause | Fix |
|---|---|---|
| Trap loop right after `stvec` is set | `stvec` base misaligned or the handler itself faults | Align the entry to 4 bytes; check the handler's first instructions with GDB. |
| Fault on the instruction after `csrw satp` | Kernel code not mapped in the new table | Identity-map the kernel before enabling translation. |
| Timer never fires | S-mode kernel writes `mtimecmp` directly, or `sie.STIE` clear | Call `sbi_set_timer`; set `STIE` in `sie` and `SIE` in `sstatus`. |
| PLIC source never interrupts | Priority 0, enable bit clear, or threshold too high | Set priority above 0, set the enable bit for the hart context, set threshold 0. |
| OpenSBI prints its banner then hangs | `FW_PAYLOAD_OFFSET` does not match the kernel's link address | Make the offset plus `0x80000000` equal the linked entry. |
| Illegal instruction in the kernel | Compressed or other extension missing from the CPU model | Match `-march` to the QEMU CPU, or pass `-cpu rv64,c=true`. |

## Output

Kernel or firmware sources in the named directory that boot on QEMU `virt` with a working trap entry, timer and PLIC interrupt paths, and an enabled page table, plus the QEMU and GDB command lines used and, when OpenSBI is built, the payload offset that matched the link address.

Attribution

OutlineDrivenOutlineDriven
View sourceSee grades on GitHubMore from OutlineDriven →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Terse caveman voice: answer first, fluff gone, every technical fact kept. Use for /caveman, "caveman mode", "talk like caveman", "be brief", "less tokens". Stays on until "stop caveman" or "normal mode".

1100021 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

698621 votes

Writing Skills

Create and manage Claude Code skills in HASH repository following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns), UserPromptSubmit hook, and the 500-line rule. Includes validation and debugging with SKILL_DEBUG. Examples include rust-error-stack, cargo-dep...

3931 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3421 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Amp, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Grok Build, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

741 votes
View all in ai-agents →