Skip to main content

libarch/riscv64/
system.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use bitrs::{bitfield_repr, layout};
6use regio::riscv64::{Csr, encoding};
7use zerocopy::{IntoBytes, TryFromBytes};
8
9/// [riscv/priv]: 3.1.6  Supervisor Status Register (sstatus)
10pub const SSTATUS: Csr<encoding::sstatus, SupervisorStatus> = Csr::new();
11
12#[bitfield_repr(u8)]
13#[derive(Clone, Copy)]
14pub enum Xlen {
15    Xlen32 = 1,
16    Xlen64 = 2,
17    Xlen128 = 3,
18}
19
20#[bitfield_repr(u8)]
21#[derive(Clone, Copy)]
22pub enum ExtensionStatus {
23    Off = 0,     // Off (FS, VS); All off (XS)
24    Initial = 1, // Initial (FS, VS); None dirty or clean, some on (XS)
25    Clean = 2,   // Clean (FS, VS); None dirty, some clean (XS)
26    Dirty = 3,   // Dirty (FS, VS); Some dirty (XS)
27}
28
29layout!({
30    /// The layout of [`SSTATUS`].
31    pub struct SupervisorStatus(u64);
32    {
33        let sd @ 63; // State Dirty
34        let __ @ 62..34;
35        let uxl @ 33..32: Xlen; // UXLEN
36        let __ @ 31..20;
37        let mxr @ 19; // Make eXecutable Readable
38        let sum @ 18; // Supervisor User Memory
39        let xs @ 16..15: ExtensionStatus; // other eXtension State
40        let fs @ 14..13: ExtensionStatus; // F extension State
41        let __ @ 12..11;
42        let vs @ 10..9: ExtensionStatus; // V extension State
43        let spp @ 8; // Supervisor Previous Privilege
44        let __ @ 7;
45        let ube @ 6; // User Big-Endian
46        let spie @ 5; // Supervisor Previous Interrupt Enable
47        let __ @ 4..2;
48        let sie @ 1; // Supervisor Interrupt Enable
49        let __ @ 0;
50    }
51});
52
53/// [riscv/priv]: 3.1.7  Supervisor Trap Vector Base Address Register (stvec)
54pub const STVEC: Csr<encoding::stvec, SupervisorTrapVector> = Csr::new();
55
56#[bitfield_repr(u8)]
57#[derive(Clone, Copy)]
58pub enum TrapVectorMode {
59    Direct = 0,
60    Vectored = 1,
61}
62
63layout!({
64    /// The layout of [`STVEC`].
65    pub struct SupervisorTrapVector(u64);
66    {
67        #[unshifted]
68        let base @ 63..2;
69        let mode @ 1..0: TrapVectorMode;
70    }
71});
72
73/// [riscv/priv]: 3.1.15  Supervisor Cause Register (scause)
74pub const SCAUSE: Csr<encoding::scause, SupervisorCause> = Csr::new();
75
76/// exception_code values when interrupt is set.
77#[bitfield_repr(u64)]
78#[derive(Clone, Copy)]
79pub enum InterruptCode {
80    SoftwareInterrupt = 1,
81    TimerInterrupt = 5,
82    ExternalInterrupt = 9,
83}
84
85/// exception_code values when interrupt is clear.
86#[bitfield_repr(u64)]
87#[derive(Clone, Copy)]
88pub enum ExceptionCode {
89    InstructionAddressMisaligned = 0,
90    InstructionAccessFault = 1,
91    IllegalInstruction = 2,
92    Breakpoint = 3,
93    LoadAddressMisaligned = 4,
94    LoadAccessFault = 5,
95    StoreAddressMisaligned = 6,
96    StoreAccessFault = 7,
97    EcallUmode = 8,
98    EcallSmode = 9,
99    InstructionPageFault = 12,
100    LoadPageFault = 13,
101    StorePageFault = 15,
102}
103
104layout!({
105    /// The layout of [`SCAUSE`].
106    pub struct SupervisorCause(u64);
107    {
108        let interrupt @ 63;
109        let code @ 62..0;
110    }
111});
112
113impl SupervisorCause {
114    #[inline]
115    pub fn exception_code(self) -> Option<ExceptionCode> {
116        if self.interrupt() {
117            return None;
118        }
119        ExceptionCode::try_read_from_bytes(self.code().as_bytes()).ok()
120    }
121
122    #[inline]
123    pub fn interrupt_code(self) -> Option<InterruptCode> {
124        if !self.interrupt() {
125            return None;
126        }
127        InterruptCode::try_read_from_bytes(self.code().as_bytes()).ok()
128    }
129}
130
131/// [riscv/priv]: 3.1.16  Supervisor Trap Value Register (stval)
132pub const STVAL: Csr<encoding::stval, u64> = Csr::new();
133
134/// [riscv/unpriv/v]: 3.4.1  Vector type register (vtype)
135pub const VTYPE: Csr<encoding::vtype, VectorType> = Csr::new();
136
137layout!({
138    /// The layout of [`VTYPE`].
139    ///
140    /// `vsew` and `vlmul` are left as raw values: most of their encodings are
141    /// reserved, so a `bitfield_repr` enum could not represent what the hardware
142    /// may actually report.
143    pub struct VectorType(u64);
144    {
145        let vill @ 63; // Illegal value
146        let __ @ 62..8;
147        let vma @ 7; // Vector mask agnostic
148        let vta @ 6; // Vector tail agnostic
149        let vsew @ 5..3; // Selected element width
150        let vlmul @ 2..0; // Vector register group multiplier
151    }
152});
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn vtype() {
160        // SEW = 32, LMUL = 1/2, tail and mask agnostic.
161        let vtype = VectorType::from((1 << 7) | (1 << 6) | (0b010 << 3) | 0b111);
162        assert!(!vtype.vill());
163        assert!(vtype.vma());
164        assert!(vtype.vta());
165        assert_eq!(vtype.vsew(), 0b010);
166        assert_eq!(vtype.vlmul(), 0b111);
167    }
168
169    #[test]
170    fn scause() {
171        let mut scause = SupervisorCause::new();
172        scause.set_interrupt(false).set_code(ExceptionCode::LoadPageFault as u64);
173        assert_eq!(scause.exception_code(), Some(ExceptionCode::LoadPageFault));
174        assert_eq!(scause.interrupt_code(), None);
175
176        scause.set_interrupt(true).set_code(InterruptCode::TimerInterrupt as u64);
177        assert_eq!(scause.exception_code(), None);
178        assert_eq!(scause.interrupt_code(), Some(InterruptCode::TimerInterrupt));
179    }
180}