Skip to content

Commit 08abb73

Browse files
Merge branch 'master' into crs-write-unsafe
2 parents e237ed4 + 2628fe4 commit 08abb73

File tree

13 files changed

+421
-284
lines changed

13 files changed

+421
-284
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: "`riscv`: Nominate CSR to be `write` safe"
3+
about: Suggest to make writes of a given CSR safe
4+
title: "`riscv`: make [CSR] write-safe"
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
10+
**Which CSR do you want to nominate as `write` safe?**
11+
Indicate which CSR you want to be `write` safe. Ex. `mepc`
12+
13+
**Does a CSR write introduce potential memory safety issues in safe code? Please describe.**
14+
A clear and concise justification on why writing to this CSR will **never** introduce memory safety issues in safe code.
15+
16+
**Does a CSR write introduce potential undefined behavior in safe code? Please describe.**
17+
A clear and concise justification on why writing to this CSR will **never** lead to undefined behavior in safe code.
18+
19+
**Does a CSR write invalidate invariants in safe code?**
20+
A clear and concise justification on why writing to this CSR will **never** invalidate invariants in safe code.
21+
22+
**Additional context**
23+
Please feel free to add any other context or screenshots about your request here.

riscv-rt/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Changed
1111

12+
- Use `RISCV_MTVEC_ALIGN` to control the alignment constraint of the vector table.
13+
- Ensure the `.heap` section is 4-byte aligned.
1214
- Limit rustc cfg flags to `riscvi`, `riscvm`, `riscvf`, and `riscvd`.
1315
- Temporary use of `RISCV_RT_LLVM_ARCH_PATCH` environment variable to include the
1416
temporary patch required for avoid LLVM spurious errors.

riscv-rt/build.rs

+7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ fn main() {
4747
// make sure that these env variables are not changed without notice.
4848
println!("cargo:rerun-if-env-changed=RISCV_RT_BASE_ISA");
4949
println!("cargo:rerun-if-env-changed=RISCV_RT_LLVM_ARCH_PATCH");
50+
if env::var_os("CARGO_FEATURE_V_TRAP").is_some()
51+
&& env::var_os("CARGO_FEATURE_NO_INTERRUPTS").is_none()
52+
{
53+
// This environment variable is used by the `#[riscv::pac_enum()]` call in
54+
// `src/interrupts.rs` (when `v-trap` is enabled and `no-interrupts` disabled).
55+
println!("cargo:rerun-if-env-changed=RISCV_MTVEC_ALIGN");
56+
}
5057

5158
for flag in target.rustc_flags() {
5259
// Required until target_feature risc-v is stable and in-use

riscv-rt/link.x.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ SECTIONS
168168
__ebss = .;
169169

170170
/* fictitious region that represents the memory available for the heap */
171-
.heap (NOLOAD) :
171+
.heap (NOLOAD) : ALIGN(4)
172172
{
173173
__sheap = .;
174174
. += _heap_size;

riscv-rt/src/interrupts.rs

+16-89
Original file line numberDiff line numberDiff line change
@@ -7,97 +7,24 @@
77
//!
88
//! In vectored mode (i.e., `v-trap` feature enabled), interrupt dispatching is handled by hardware.
99
//! To support this mode, we provide inline assembly code that defines the interrupt vector table.
10+
//! Since the alignment constraint of this vector table is implementation-specific, it can be
11+
//! changed by setting the `RISCV_MTVEC_ALIGN` environment variable (the default is 4).
1012
//!
1113
//! # Note
1214
//!
13-
//! If your target has custom core interrupt sources, the target PAC might provide equivalent
14-
//! code to adapt for the target needs. In this case, you may need to opt out this module.
15-
//! To do so, activate the `no-interrupts` feature of the `riscv-rt` crate.
16-
17-
#[cfg(not(feature = "v-trap"))]
18-
extern "C" {
19-
fn SupervisorSoft();
20-
fn MachineSoft();
21-
fn SupervisorTimer();
22-
fn MachineTimer();
23-
fn SupervisorExternal();
24-
fn MachineExternal();
25-
}
26-
27-
/// Array with all the core interrupt handlers sorted according to their interrupt source code.
28-
///
29-
/// # Note
30-
///
31-
/// This array is necessary only in direct mode (i.e., `v-trap` feature disabled).
32-
#[cfg(not(feature = "v-trap"))]
33-
#[no_mangle]
34-
pub static __CORE_INTERRUPTS: [Option<unsafe extern "C" fn()>; 12] = [
35-
None,
36-
Some(SupervisorSoft),
37-
None,
38-
Some(MachineSoft),
39-
None,
40-
Some(SupervisorTimer),
41-
None,
42-
Some(MachineTimer),
43-
None,
44-
Some(SupervisorExternal),
45-
None,
46-
Some(MachineExternal),
47-
];
48-
49-
/// It calls the corresponding interrupt handler depending on the interrupt source code.
50-
///
51-
/// # Note
52-
///
53-
/// This function is only required in direct mode (i.e., `v-trap` feature disabled).
54-
/// In vectored mode, interrupt handler dispatching is performed directly by hardware.
55-
///
56-
/// # Safety
57-
///
58-
/// This function must be called only from the [`crate::start_trap_rust`] function.
59-
/// Do **NOT** call this function directly.
60-
#[cfg(not(feature = "v-trap"))]
61-
#[inline]
62-
#[no_mangle]
63-
pub unsafe extern "C" fn _dispatch_core_interrupt(code: usize) {
64-
extern "C" {
65-
fn DefaultHandler();
66-
}
67-
match __CORE_INTERRUPTS.get(code) {
68-
Some(Some(handler)) => handler(),
69-
_ => DefaultHandler(),
70-
}
71-
}
15+
//! If your target has custom core interrupt sources, the target PAC might provide equivalent code
16+
//! to adapt for the target needs (and is responsible for any alignment constraint). In this case,
17+
//! you may need to opt out this module. To do so, activate the `no-interrupts` feature of the
18+
//! `riscv-rt` crate.
7219
7320
// In vectored mode, we also must provide a vector table
74-
#[cfg(all(
75-
any(target_arch = "riscv32", target_arch = "riscv64"),
76-
feature = "v-trap"
77-
))]
78-
core::arch::global_asm!(
79-
r#" .section .trap, "ax"
80-
.weak _vector_table
81-
.type _vector_table, @function
82-
83-
.option push
84-
.balign 0x4 // TODO check if this is the correct alignment
85-
.option norelax
86-
.option norvc
87-
88-
_vector_table:
89-
j _start_trap // Interrupt 0 is used for exceptions
90-
j _start_SupervisorSoft_trap
91-
j _start_DefaultHandler_trap // Interrupt 2 is reserved
92-
j _start_MachineSoft_trap
93-
j _start_DefaultHandler_trap // Interrupt 4 is reserved
94-
j _start_SupervisorTimer_trap
95-
j _start_DefaultHandler_trap // Interrupt 6 is reserved
96-
j _start_MachineTimer_trap
97-
j _start_DefaultHandler_trap // Interrupt 8 is reserved
98-
j _start_SupervisorExternal_trap
99-
j _start_DefaultHandler_trap // Interrupt 10 is reserved
100-
j _start_MachineExternal_trap
101-
102-
.option pop"#
103-
);
21+
#[riscv::pac_enum(unsafe CoreInterruptNumber)]
22+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23+
enum Interrupt {
24+
SupervisorSoft = 1,
25+
MachineSoft = 3,
26+
SupervisorTimer = 5,
27+
MachineTimer = 7,
28+
SupervisorExternal = 9,
29+
MachineExternal = 11,
30+
}

riscv/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1414
### Changed
1515

1616
- Make all CSR writes `unsafe` by default (#209)
17+
- Use `RISCV_MTVEC_ALIGN` to control the alignment constraint of the vector table
1718
- Simplify register macros with `cfg` field
1819
- Align assembly functions with `cortex-m`
1920
- Use CSR helper macros to define `marchid` register
@@ -27,6 +28,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2728
- Use CSR helper macros to define `misa` register
2829
- Use CSR helper macros to define `mip` register
2930
- Use CSR helper macros to define `mstatus` register
31+
- Use CSR helper macros to define `mstatush` register
32+
- Use CSR helper macros to define `mtvec` register
33+
- Use CSR helper macros to define `mtvendorid` register
34+
- Use CSR helper macros to define `satp` register
3035

3136
## [v0.12.1] - 2024-10-20
3237

riscv/macros/src/lib.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,20 @@ impl PacEnumItem {
265265
}
266266

267267
fn vector_table(&self) -> TokenStream2 {
268-
let mut asm = String::from(
268+
let align = match std::env::var("RISCV_MTVEC_ALIGN") {
269+
Ok(x) => x.parse::<u32>().ok(),
270+
Err(std::env::VarError::NotPresent) => Some(4),
271+
Err(std::env::VarError::NotUnicode(_)) => None,
272+
};
273+
let align = match align {
274+
Some(x) if x.is_power_of_two() && 4 <= x => x,
275+
_ => {
276+
return quote!(compile_error!(
277+
"RISCV_MTVEC_ALIGN is not a power of 2 (minimum 4)"
278+
))
279+
}
280+
};
281+
let mut asm = format!(
269282
r#"
270283
#[cfg(all(feature = "v-trap", any(target_arch = "riscv32", target_arch = "riscv64")))]
271284
core::arch::global_asm!("
@@ -274,7 +287,7 @@ core::arch::global_asm!("
274287
.type _vector_table, @function
275288
276289
.option push
277-
.balign 0x4 // TODO check if this is the correct alignment
290+
.balign {align}
278291
.option norelax
279292
.option norvc
280293
@@ -315,6 +328,8 @@ core::arch::global_asm!("
315328
let max_discriminant = self.max_number;
316329
let valid_matches = self.valid_matches();
317330

331+
let is_core_interrupt = matches!(attr, PacTrait::Interrupt(InterruptType::Core));
332+
318333
// Push the trait implementation
319334
res.push(quote! {
320335
unsafe impl riscv::#trait_name for #name {
@@ -350,19 +365,26 @@ core::arch::global_asm!("
350365

351366
let handlers = self.handlers(&trap_config);
352367
let interrupt_array = self.handlers_array();
368+
let cfg_v_trap = match is_core_interrupt {
369+
true => Some(quote!(#[cfg(not(feature = "v-trap"))])),
370+
false => None,
371+
};
353372

354373
// Push the interrupt handler functions and the interrupt array
355374
res.push(quote! {
375+
#cfg_v_trap
356376
extern "C" {
357377
#(#handlers;)*
358378
}
359379

380+
#cfg_v_trap
360381
#[doc(hidden)]
361382
#[no_mangle]
362383
pub static #vector_table: [Option<unsafe extern "C" fn(#(#array_signature),*)>; #max_discriminant + 1] = [
363384
#(#interrupt_array),*
364385
];
365386

387+
#cfg_v_trap
366388
#[inline]
367389
#[no_mangle]
368390
unsafe extern "C" fn #dispatch_fn_name(#(#dispatch_fn_args),*) {
@@ -378,7 +400,7 @@ core::arch::global_asm!("
378400
});
379401
}
380402

381-
if let PacTrait::Interrupt(InterruptType::Core) = attr {
403+
if is_core_interrupt {
382404
res.push(self.vector_table());
383405
}
384406

riscv/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#![no_std]
3636
#![allow(clippy::missing_safety_doc)]
3737
#![allow(clippy::eq_op)]
38+
#![allow(clippy::identity_op)]
3839

3940
pub use paste::paste;
4041

riscv/src/register/macros.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ macro_rules! clear_pmp {
544544
macro_rules! csr {
545545
($(#[$doc:meta])*
546546
$ty:ident,
547-
$mask:literal) => {
547+
$mask:expr) => {
548548
#[repr(C)]
549549
$(#[$doc])*
550550
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -647,8 +647,8 @@ macro_rules! csr_field_enum {
647647
#[macro_export]
648648
macro_rules! read_write_csr {
649649
($(#[$doc:meta])+
650-
$ty:ident: $csr:tt,
651-
mask: $mask:tt$(,)?
650+
$ty:ident: $csr:expr,
651+
mask: $mask:expr$(,)?
652652
) => {
653653
$crate::csr!($(#[$doc])+ $ty, $mask);
654654

@@ -663,17 +663,17 @@ macro_rules! read_write_csr {
663663
#[macro_export]
664664
macro_rules! read_only_csr {
665665
($(#[$doc:meta])+
666-
$ty:ident: $csr:tt,
667-
mask: $mask:tt$(,)?
666+
$ty:ident: $csr:expr,
667+
mask: $mask:expr$(,)?
668668
) => {
669669
$crate::csr! { $(#[$doc])+ $ty, $mask }
670670

671671
$crate::read_csr_as!($ty, $csr);
672672
};
673673

674674
($(#[$doc:meta])+
675-
$ty:ident: $csr:tt,
676-
mask: $mask:tt,
675+
$ty:ident: $csr:expr,
676+
mask: $mask:expr,
677677
sentinel: $sentinel:tt$(,)?,
678678
) => {
679679
$crate::csr! { $(#[$doc])+ $ty, $mask }
@@ -688,8 +688,8 @@ macro_rules! read_only_csr {
688688
#[macro_export]
689689
macro_rules! write_only_csr {
690690
($(#[$doc:meta])+
691-
$ty:ident: $csr:literal,
692-
mask: $mask:literal$(,)?
691+
$ty:ident: $csr:expr,
692+
mask: $mask:expr$(,)?
693693
) => {
694694
$crate::csr! { $(#[$doc])+ $ty, $mask }
695695

riscv/src/register/mstatush.rs

+30-15
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,26 @@
22
33
pub use super::mstatus::Endianness;
44

5-
/// mstatus register
6-
#[derive(Clone, Copy, Debug)]
7-
pub struct Mstatush {
8-
bits: usize,
5+
read_write_csr! {
6+
/// mstatus register
7+
Mstatush: 0x310,
8+
mask: 0x30,
99
}
1010

11-
impl Mstatush {
11+
read_write_csr_field! {
12+
Mstatush,
1213
/// S-mode non-instruction-fetch memory endianness
13-
#[inline]
14-
pub fn sbe(&self) -> Endianness {
15-
Endianness::from(self.bits & (1 << 4) != 0)
16-
}
14+
sbe,
15+
Endianness: [4:4],
16+
}
1717

18+
read_write_csr_field! {
19+
Mstatush,
1820
/// M-mode non-instruction-fetch memory endianness
19-
#[inline]
20-
pub fn mbe(&self) -> Endianness {
21-
Endianness::from(self.bits & (1 << 5) != 0)
22-
}
21+
mbe,
22+
Endianness: [5:5],
2323
}
2424

25-
read_csr_as_rv32!(Mstatush, 0x310);
26-
write_csr_rv32!(0x310);
2725
set_rv32!(0x310);
2826
clear_rv32!(0x310);
2927

@@ -44,3 +42,20 @@ pub unsafe fn set_mbe(endianness: Endianness) {
4442
Endianness::LittleEndian => _clear(1 << 5),
4543
}
4644
}
45+
46+
#[cfg(test)]
47+
mod tests {
48+
use super::*;
49+
50+
#[test]
51+
fn test_mstatush() {
52+
let mut m = Mstatush::from_bits(0);
53+
54+
[Endianness::LittleEndian, Endianness::BigEndian]
55+
.into_iter()
56+
.for_each(|endianness| {
57+
test_csr_field!(m, sbe: endianness);
58+
test_csr_field!(m, mbe: endianness);
59+
});
60+
}
61+
}

0 commit comments

Comments
 (0)