Skip to content

Commit 3475646

Browse files
committed
riscv: vmm-reference first support to RISC-V
This commit implements a first support to the RISC-V architecture. Major changes are related to interrupts handling: - use of in-kernel IRQCHIP (APLIC) - interrupt injection from userspace through kvm ioctl (KVM_SET_IRQ_LINE) Signed-off-by: Samuele Paone <[email protected]>
1 parent faa6d1a commit 3475646

File tree

15 files changed

+746
-101
lines changed

15 files changed

+746
-101
lines changed

src/arch/src/riscv_regs.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#[cfg(target_arch = "riscv64")]
2+
#[macro_export]
3+
macro_rules! kvm_reg_riscv_core_reg {
4+
($name:ident) => {
5+
offset_of!(kvm_bindings::user_regs_struct, $name) / std::mem::size_of::<u64>()
6+
};
7+
}
8+
#[macro_export]
9+
macro_rules! riscv_core_reg {
10+
($name:ident) => {
11+
kvm_reg_id!(
12+
KVM_REG_RISCV_CORE as u64,
13+
0,
14+
kvm_reg_riscv_core_reg!($name) as u64,
15+
KVM_REG_SIZE_U64
16+
)
17+
};
18+
}
19+
20+
// Registers configuration macros
21+
#[macro_export]
22+
macro_rules! kvm_reg_riscv_config_reg {
23+
($name:ident) => {
24+
offset_of!(kvm_bindings::kvm_riscv_config, $name) / std::mem::size_of::<u64>()
25+
};
26+
}
27+
#[macro_export]
28+
macro_rules! riscv_config_reg {
29+
($name:ident) => {
30+
kvm_reg_id!(
31+
KVM_REG_RISCV_CONFIG as u64,
32+
0,
33+
kvm_reg_riscv_config_reg!($name) as u64,
34+
KVM_REG_SIZE_U64
35+
)
36+
};
37+
}
38+
39+
// Timer-related macros
40+
#[macro_export]
41+
macro_rules! kvm_reg_riscv_timer_reg {
42+
($name:ident) => {
43+
offset_of!(kvm_riscv_timer, $name) / std::mem::size_of::<u64>()
44+
};
45+
}
46+
#[macro_export]
47+
macro_rules! riscv_timer_reg {
48+
($name:ident) => {
49+
kvm_reg_id!(
50+
KVM_REG_RISCV_TIMER as u64,
51+
0,
52+
kvm_reg_riscv_timer_reg!($name) as u64,
53+
KVM_REG_SIZE_U64
54+
)
55+
};
56+
}
57+
58+
#[macro_export]
59+
macro_rules! riscv_isa_ext_reg {
60+
($id:expr) => {
61+
kvm_reg_id!(KVM_REG_RISCV_ISA_EXT as u64, 0, $id, KVM_REG_SIZE_U64)
62+
};
63+
}
64+
65+
#[macro_export]
66+
macro_rules! kvm_reg_id {
67+
($stype:expr, $subtype:expr, $idx:expr, $size:expr) => {
68+
(KVM_REG_RISCV as u64)
69+
| ($stype as u64)
70+
| ($subtype as u64)
71+
| ($idx as u64)
72+
| ($size as u64)
73+
};
74+
}

src/devices/src/intc/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use kvm_ioctls::VmFd;
2+
use std::io;
3+
use std::sync::Arc;
4+
use vm_superio::Trigger;
5+
6+
#[derive(Clone)]
7+
pub struct APlicTrigger {
8+
pub gsi: u32,
9+
pub vm: Arc<VmFd>,
10+
}
11+
12+
impl APlicTrigger {
13+
pub fn try_clone(&self) -> io::Result<Self> {
14+
Ok(APlicTrigger {
15+
gsi: self.gsi,
16+
vm: self.vm.clone(),
17+
})
18+
}
19+
}
20+
21+
impl Trigger for APlicTrigger {
22+
type E = io::Error;
23+
24+
fn trigger(&self) -> io::Result<()> {
25+
self.vm.set_irq_line(self.gsi, true)?;
26+
self.vm.set_irq_line(self.gsi, false)?;
27+
28+
Ok(())
29+
}
30+
}

src/devices/src/legacy/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
33
#[cfg(target_arch = "x86_64")]
44
mod i8042;
5-
#[cfg(target_arch = "aarch64")]
5+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
66
mod rtc;
77
mod serial;
88
#[cfg(target_arch = "x86_64")]
99
pub use i8042::I8042Wrapper;
10-
#[cfg(target_arch = "aarch64")]
10+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
1111
pub use rtc::RtcWrapper;
1212
pub use serial::Error as SerialError;
1313
pub use serial::SerialWrapper;

src/devices/src/legacy/serial.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::convert::TryInto;
55
use std::io::{self, stdin, Read, Write};
66

77
use event_manager::{EventOps, Events, MutEventSubscriber};
8-
#[cfg(target_arch = "aarch64")]
8+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
99
use vm_device::{bus::MmioAddress, MutDeviceMmio};
1010
#[cfg(target_arch = "x86_64")]
1111
use vm_device::{
@@ -106,7 +106,7 @@ impl<T: Trigger<E = io::Error>, W: Write> MutDevicePio for SerialWrapper<T, NoEv
106106
}
107107
}
108108

109-
#[cfg(target_arch = "aarch64")]
109+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
110110
impl<T: Trigger<E = io::Error>, W: Write> MutDeviceMmio for SerialWrapper<T, NoEvents, W> {
111111
fn mmio_read(&mut self, _base: MmioAddress, offset: u64, data: &mut [u8]) {
112112
// TODO: this function can't return an Err, so we'll mark error conditions
@@ -150,13 +150,13 @@ mod tests {
150150
// Check that passing invalid data does not result in a crash.
151151
#[cfg(target_arch = "x86_64")]
152152
serial_console.pio_read(PioAddress(0), valid_iir_offset, invalid_data);
153-
#[cfg(target_arch = "aarch64")]
153+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
154154
serial_console.mmio_read(MmioAddress(0), valid_iir_offset, invalid_data);
155155

156156
// The same scenario happens for writes.
157157
#[cfg(target_arch = "x86_64")]
158158
serial_console.pio_write(PioAddress(0), valid_iir_offset, invalid_data);
159-
#[cfg(target_arch = "aarch64")]
159+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
160160
serial_console.mmio_write(MmioAddress(0), valid_iir_offset, invalid_data);
161161
}
162162

@@ -182,7 +182,7 @@ mod tests {
182182
let invalid_offset = PioAddressOffset::MAX;
183183
serial_console.pio_write(PioAddress(0), invalid_offset, &data);
184184
}
185-
#[cfg(target_arch = "aarch64")]
185+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
186186
{
187187
let invalid_offset = u64::MAX;
188188
serial_console.mmio_write(MmioAddress(0), invalid_offset, &data);
@@ -203,7 +203,7 @@ mod tests {
203203
serial_console.pio_write(PioAddress(0), offset, &write_data);
204204
serial_console.pio_read(PioAddress(0), offset, read_data.as_mut());
205205
}
206-
#[cfg(target_arch = "aarch64")]
206+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
207207
{
208208
serial_console.mmio_write(MmioAddress(0), offset, &write_data);
209209
serial_console.mmio_read(MmioAddress(0), offset, read_data.as_mut());

src/devices/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
// we are striving to turn as much of the local code as possible into reusable building blocks
66
// going forward.
77

8+
pub mod intc;
89
pub mod legacy;
910
pub mod virtio;

src/devices/src/virtio/block/device.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ use vm_device::{DeviceMmio, MutDeviceMmio};
1616
use vm_memory::{GuestAddressSpace, GuestMemoryMmap};
1717

1818
use crate::virtio::block::{BLOCK_DEVICE_ID, VIRTIO_BLK_F_RO};
19-
use crate::virtio::{CommonConfig, Env, SingleFdSignalQueue, QUEUE_MAX_SIZE};
19+
use crate::virtio::{CommonConfig, Env, IrqTrigger, SingleFdSignalQueue, QUEUE_MAX_SIZE};
2020

2121
use super::inorder_handler::InOrderQueueHandler;
2222
use super::queue_handler::QueueHandler;
2323
use super::{build_config_space, BlockArgs, Error, Result};
2424

25+
use crate::intc::APlicTrigger;
26+
2527
// This Block device can only use the MMIO transport for now, but we plan to reuse large parts of
2628
// the functionality when we implement virtio PCI as well, for example by having a base generic
2729
// type, and then separate concrete instantiations for `MmioConfig` and `PciConfig`.
@@ -33,6 +35,7 @@ pub struct Block<M: GuestAddressSpace + Clone + Send + 'static> {
3335
// the outside.
3436
_root_device: bool,
3537
mem: Arc<GuestMemoryMmap>,
38+
aplic_trigger: Option<APlicTrigger>,
3639
}
3740

3841
impl<M: GuestAddressSpace + Clone + Send + Sync + 'static> Block<M> {
@@ -41,6 +44,7 @@ impl<M: GuestAddressSpace + Clone + Send + Sync + 'static> Block<M> {
4144
mem: Arc<GuestMemoryMmap>,
4245
env: &mut Env<M, B>,
4346
args: &BlockArgs,
47+
aplic_trigger: Option<APlicTrigger>,
4448
) -> Result<Self> {
4549
let device_features = args.device_features();
4650

@@ -57,6 +61,7 @@ impl<M: GuestAddressSpace + Clone + Send + Sync + 'static> Block<M> {
5761
file_path: args.file_path.clone(),
5862
read_only: args.read_only,
5963
_root_device: args.root_device,
64+
aplic_trigger,
6065
})
6166
}
6267

@@ -66,14 +71,20 @@ impl<M: GuestAddressSpace + Clone + Send + Sync + 'static> Block<M> {
6671
mem: Arc<GuestMemoryMmap>,
6772
env: &mut Env<M, B>,
6873
args: &BlockArgs,
74+
aplic_trigger: Option<APlicTrigger>,
6975
) -> Result<Arc<Mutex<Self>>>
7076
where
7177
// We're using this (more convoluted) bound so we can pass both references and smart
7278
// pointers such as mutex guards here.
7379
B: DerefMut,
7480
B::Target: MmioManager<D = Arc<dyn DeviceMmio + Send + Sync>>,
7581
{
76-
let block = Arc::new(Mutex::new(Self::create_block(mem, env, args)?));
82+
let block = Arc::new(Mutex::new(Self::create_block(
83+
mem,
84+
env,
85+
args,
86+
aplic_trigger,
87+
)?));
7788

7889
// Register the device on the MMIO bus.
7990
env.register_mmio_device(block.clone())
@@ -123,9 +134,14 @@ impl<M: GuestAddressSpace + Clone + Send + 'static> VirtioDeviceActions for Bloc
123134

124135
// TODO: Create the backend earlier (as part of `Block::new`)?
125136
let disk = StdIoBackend::new(file, features).map_err(Error::Backend)?;
137+
let irq_trigger = if cfg!(target_arch = "riscv64") {
138+
IrqTrigger::APlicTrigger(Some(self.aplic_trigger.clone().unwrap()))
139+
} else {
140+
IrqTrigger::IrqFd(self.cfg.irqfd.clone())
141+
};
126142

127143
let driver_notify = SingleFdSignalQueue {
128-
irqfd: self.cfg.irqfd.clone(),
144+
irq_trigger,
129145
interrupt_status: self.cfg.virtio.interrupt_status.clone(),
130146
};
131147

@@ -185,7 +201,16 @@ mod tests {
185201
advertise_flush: true,
186202
};
187203

188-
let block_mutex = Block::new(env.mem.clone(), &mut env, &args).unwrap();
204+
let aplic_trigger = if cfg!(target_arch = "riscv64") {
205+
Some(APlicTrigger {
206+
gsi: 5,
207+
vm: env.vm_fd.clone(),
208+
})
209+
} else {
210+
None
211+
};
212+
213+
let block_mutex = Block::new(env.mem.clone(), &mut env, &args, aplic_trigger).unwrap();
189214
let block = block_mutex.lock().unwrap();
190215

191216
assert_eq!(block.device_type(), BLOCK_DEVICE_ID);

src/devices/src/virtio/mod.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::ops::{Deref, DerefMut};
1212
use std::sync::atomic::{AtomicU8, Ordering};
1313
use std::sync::{Arc, Mutex};
1414

15+
use crate::intc::APlicTrigger;
1516
use event_manager::{
1617
Error as EvmgrError, EventManager, MutEventSubscriber, RemoteEndpoint, Result as EvmgrResult,
1718
SubscriberId,
@@ -24,6 +25,7 @@ use vm_device::bus::{self, MmioAddress, MmioRange};
2425
use vm_device::device_manager::MmioManager;
2526
use vm_device::DeviceMmio;
2627
use vm_memory::{GuestAddress, GuestAddressSpace};
28+
use vm_superio::Trigger;
2729
use vmm_sys_util::errno;
2830
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
2931

@@ -66,6 +68,28 @@ pub enum Error {
6668
RegisterIrqfd(errno::Error),
6769
}
6870

71+
// Enum that abstracts the way in which IRQs are triggered
72+
pub enum IrqTrigger {
73+
IrqFd(Arc<EventFd>),
74+
APlicTrigger(Option<APlicTrigger>),
75+
}
76+
77+
impl IrqTrigger {
78+
fn trigger(&self) {
79+
match self {
80+
IrqTrigger::IrqFd(irqfd) => {
81+
irqfd
82+
.write(1)
83+
.expect("Failed write to eventfd when signaling queue");
84+
}
85+
IrqTrigger::APlicTrigger(aplic_trigger) => aplic_trigger
86+
.clone()
87+
.unwrap()
88+
.trigger()
89+
.expect("Failed write to aplic trigger when signaling queue"),
90+
}
91+
}
92+
}
6993
type Result<T> = std::result::Result<T, Error>;
7094
pub type Subscriber = Arc<Mutex<dyn MutEventSubscriber + Send>>;
7195

@@ -168,6 +192,7 @@ impl<M: GuestAddressSpace + Clone + Send + 'static> CommonConfig<M> {
168192
pub fn new<B>(virtio_cfg: VirtioConfig<Queue>, env: &Env<M, B>) -> Result<Self> {
169193
let irqfd = Arc::new(EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?);
170194

195+
#[cfg(not(target_arch = "riscv64"))]
171196
env.vm_fd
172197
.register_irqfd(&irqfd, env.mmio_cfg.gsi)
173198
.map_err(Error::RegisterIrqfd)?;
@@ -264,17 +289,15 @@ pub trait SignalUsedQueue {
264289
/// Uses a single irqfd as the basis of signalling any queue (useful for the MMIO transport,
265290
/// where a single interrupt is shared for everything).
266291
pub struct SingleFdSignalQueue {
267-
pub irqfd: Arc<EventFd>,
292+
pub irq_trigger: IrqTrigger,
268293
pub interrupt_status: Arc<AtomicU8>,
269294
}
270295

271296
impl SignalUsedQueue for SingleFdSignalQueue {
272297
fn signal_used_queue(&self, _index: u16) {
273298
self.interrupt_status
274299
.fetch_or(VIRTIO_MMIO_INT_VRING, Ordering::SeqCst);
275-
self.irqfd
276-
.write(1)
277-
.expect("Failed write to eventfd when signalling queue");
300+
self.irq_trigger.trigger();
278301
}
279302
}
280303

0 commit comments

Comments
 (0)