Skip to content

Commit e2c9d85

Browse files
alicedreamedslp
authored andcommitted
vmm: Introduce RISC-V 64-bit architecture support
Integrate KvmAia, FDT and etc. into vmm to support vmm module to work on RISC-V 64-bit platforms. Signed-off-by: Bingxin Li <[email protected]>
1 parent 20d1a34 commit e2c9d85

File tree

4 files changed

+152
-18
lines changed

4 files changed

+152
-18
lines changed

src/vmm/src/builder.rs

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use crate::resources::VmResources;
2727
use crate::vmm_config::external_kernel::{ExternalKernel, KernelFormat};
2828
#[cfg(feature = "net")]
2929
use crate::vmm_config::net::NetBuilder;
30+
#[cfg(all(target_os = "linux", target_arch = "riscv64"))]
31+
use devices::legacy::KvmAia;
3032
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
3133
use devices::legacy::KvmGicV3;
3234
#[cfg(target_arch = "x86_64")]
@@ -466,7 +468,7 @@ impl Display for StartMicrovmError {
466468
enum Payload {
467469
#[cfg(all(target_arch = "x86_64", not(feature = "tee")))]
468470
KernelMmap,
469-
#[cfg(target_arch = "aarch64")]
471+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
470472
KernelCopy,
471473
ExternalKernel(ExternalKernel),
472474
#[cfg(test)]
@@ -489,7 +491,7 @@ fn choose_payload(vm_resources: &VmResources) -> Result<Payload, StartMicrovmErr
489491
#[cfg(all(target_os = "linux", target_arch = "x86_64", not(feature = "tee")))]
490492
return Ok(Payload::KernelMmap);
491493

492-
#[cfg(target_arch = "aarch64")]
494+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
493495
return Ok(Payload::KernelCopy);
494496
} else if let Some(external_kernel) = vm_resources.external_kernel() {
495497
Ok(Payload::ExternalKernel(external_kernel.clone()))
@@ -758,6 +760,29 @@ pub fn build_microvm(
758760
)?;
759761
}
760762

763+
#[cfg(all(target_arch = "riscv64", target_os = "linux"))]
764+
{
765+
vcpus = create_vcpus_riscv64(
766+
&vm,
767+
&vcpu_config,
768+
&guest_memory,
769+
payload_config.entry_addr,
770+
&exit_evt,
771+
)
772+
.map_err(StartMicrovmError::Internal)?;
773+
774+
intc = Arc::new(Mutex::new(IrqChipDevice::new(Box::new(
775+
KvmAia::new(vm.fd(), vm_resources.vm_config().vcpu_count.unwrap() as u32).unwrap(),
776+
))));
777+
778+
attach_legacy_devices(
779+
&vm,
780+
&mut mmio_device_manager,
781+
&mut kernel_cmdline,
782+
serial_device,
783+
)?;
784+
}
785+
761786
// We use this atomic to record the exit code set by init/init.c in the VM.
762787
let exit_code = Arc::new(AtomicI32::new(i32::MAX));
763788

@@ -902,7 +927,7 @@ fn load_external_kernel(
902927
// Raw images are treated as bundled kernels on x86_64
903928
#[cfg(target_arch = "x86_64")]
904929
KernelFormat::Raw => unreachable!(),
905-
#[cfg(target_arch = "aarch64")]
930+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
906931
KernelFormat::Raw => {
907932
let data: Vec<u8> = std::fs::read(external_kernel.path.clone())
908933
.map_err(StartMicrovmError::RawOpenKernel)?;
@@ -920,7 +945,7 @@ fn load_external_kernel(
920945
.map_err(StartMicrovmError::ElfLoadKernel)?;
921946
load_result.kernel_load
922947
}
923-
#[cfg(target_arch = "aarch64")]
948+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
924949
KernelFormat::PeGz => {
925950
let data: Vec<u8> = std::fs::read(external_kernel.path.clone())
926951
.map_err(StartMicrovmError::PeGzOpenKernel)?;
@@ -1054,7 +1079,7 @@ fn load_payload(
10541079
StartMicrovmError,
10551080
> {
10561081
match payload {
1057-
#[cfg(target_arch = "aarch64")]
1082+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
10581083
Payload::KernelCopy => {
10591084
let (kernel_entry_addr, kernel_host_addr, kernel_guest_addr, kernel_size) =
10601085
if let Some(kernel_bundle) = &_vm_resources.kernel_bundle {
@@ -1224,7 +1249,7 @@ fn create_guest_memory(
12241249
Payload::Empty => arch::arch_memory_regions(mem_size, None, 0, 0),
12251250
Payload::Efi => unreachable!(),
12261251
};
1227-
#[cfg(target_arch = "aarch64")]
1252+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
12281253
let (arch_mem_info, mut arch_mem_regions) = match payload {
12291254
Payload::ExternalKernel(external_kernel) => {
12301255
arch::arch_memory_regions(mem_size, external_kernel.initramfs_size)
@@ -1385,7 +1410,10 @@ fn attach_legacy_devices(
13851410
Ok(())
13861411
}
13871412

1388-
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
1413+
#[cfg(all(
1414+
any(target_arch = "aarch64", target_arch = "riscv64"),
1415+
target_os = "linux"
1416+
))]
13891417
fn attach_legacy_devices(
13901418
vm: &Vm,
13911419
mmio_device_manager: &mut MMIODeviceManager,
@@ -1399,6 +1427,7 @@ fn attach_legacy_devices(
13991427
.map_err(StartMicrovmError::Internal)?;
14001428
}
14011429

1430+
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
14021431
mmio_device_manager
14031432
.register_mmio_rtc(vm.fd())
14041433
.map_err(Error::RegisterMMIODevice)
@@ -1546,6 +1575,31 @@ fn create_vcpus_aarch64(
15461575
Ok(vcpus)
15471576
}
15481577

1578+
#[cfg(all(target_arch = "riscv64", target_os = "linux"))]
1579+
fn create_vcpus_riscv64(
1580+
vm: &Vm,
1581+
vcpu_config: &VcpuConfig,
1582+
guest_mem: &GuestMemoryMmap,
1583+
entry_addr: GuestAddress,
1584+
exit_evt: &EventFd,
1585+
) -> super::Result<Vec<Vcpu>> {
1586+
let mut vcpus = Vec::with_capacity(vcpu_config.vcpu_count as usize);
1587+
for cpu_index in 0..vcpu_config.vcpu_count {
1588+
let mut vcpu = Vcpu::new_riscv64(
1589+
cpu_index,
1590+
vm.fd(),
1591+
exit_evt.try_clone().map_err(Error::EventFd)?,
1592+
)
1593+
.map_err(Error::Vcpu)?;
1594+
1595+
vcpu.configure_riscv64(vm.fd(), guest_mem, entry_addr)
1596+
.map_err(Error::Vcpu)?;
1597+
1598+
vcpus.push(vcpu);
1599+
}
1600+
Ok(vcpus)
1601+
}
1602+
15491603
/// Attaches an MmioTransport device to the device manager.
15501604
fn attach_mmio_device(
15511605
vmm: &mut Vmm,

src/vmm/src/device_manager/kvm/mmio.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::collections::HashMap;
99
use std::sync::{Arc, Mutex};
1010
use std::{fmt, io};
1111

12-
#[cfg(target_arch = "aarch64")]
12+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
1313
use devices::fdt::DeviceInfoForFDT;
1414
use devices::{BusDevice, DeviceType};
1515
use kernel::cmdline as kernel_cmdline;
@@ -76,7 +76,7 @@ pub struct MMIODeviceManager {
7676
impl MMIODeviceManager {
7777
/// Create a new DeviceManager handling mmio devices (virtio net, block).
7878
pub fn new(mmio_base: &mut u64, irq_interval: (u32, u32)) -> MMIODeviceManager {
79-
if cfg!(target_arch = "aarch64") {
79+
if cfg!(any(target_arch = "aarch64", target_arch = "riscv64")) {
8080
*mmio_base += MMIO_LEN;
8181
}
8282
MMIODeviceManager {
@@ -175,7 +175,7 @@ impl MMIODeviceManager {
175175
.map_err(Error::Cmdline)
176176
}
177177

178-
#[cfg(target_arch = "aarch64")]
178+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
179179
/// Register an early console at some MMIO address.
180180
pub fn register_mmio_serial(
181181
&mut self,
@@ -197,7 +197,10 @@ impl MMIODeviceManager {
197197
cmdline
198198
.insert(
199199
"earlycon",
200+
#[cfg(target_arch = "aarch64")]
200201
&format!("pl011,mmio32,0x{:08x}", self.mmio_base),
202+
#[cfg(target_arch = "riscv64")]
203+
&format!("uart,mmio,0x{:08x}", self.mmio_base),
201204
)
202205
.map_err(Error::Cmdline)?;
203206

@@ -250,7 +253,7 @@ impl MMIODeviceManager {
250253
Ok(())
251254
}
252255

253-
#[cfg(target_arch = "aarch64")]
256+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
254257
/// Gets the information of the devices registered up to some point in time.
255258
pub fn get_device_info(&self) -> &HashMap<(DeviceType, String), MMIODeviceInfo> {
256259
&self.id_to_dev_info
@@ -282,7 +285,7 @@ pub struct MMIODeviceInfo {
282285
_len: u64,
283286
}
284287

285-
#[cfg(target_arch = "aarch64")]
288+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
286289
impl DeviceInfoForFDT for MMIODeviceInfo {
287290
fn addr(&self) -> u64 {
288291
self.addr

src/vmm/src/lib.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use crate::vstate::{Vcpu, VcpuHandle, VcpuResponse, Vm};
5555
use arch::{ArchMemoryInfo, InitrdConfig};
5656
#[cfg(target_os = "macos")]
5757
use crossbeam_channel::Sender;
58-
#[cfg(target_arch = "aarch64")]
58+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
5959
use devices::fdt;
6060
use devices::legacy::IrqChip;
6161
use devices::virtio::VmmExitObserver;
@@ -114,7 +114,7 @@ pub enum Error {
114114
RegisterMMIODevice(device_manager::mmio::Error),
115115
/// Write to the serial console failed.
116116
Serial(io::Error),
117-
#[cfg(target_arch = "aarch64")]
117+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
118118
/// Cannot generate or write FDT
119119
SetupFDT(devices::fdt::Error),
120120
/// Cannot create Timer file descriptor.
@@ -155,7 +155,7 @@ impl Display for Error {
155155
LoadCommandline(e) => write!(f, "Cannot load command line: {e}"),
156156
RegisterMMIODevice(e) => write!(f, "Cannot add a device to the MMIO Bus. {e}"),
157157
Serial(e) => write!(f, "Error writing to the serial console: {e:?}"),
158-
#[cfg(target_arch = "aarch64")]
158+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
159159
SetupFDT(e) => write!(f, "Error generating or writing FDT: {e:?}"),
160160
TimerFd(e) => write!(f, "Error creating timer fd: {e}"),
161161
Vcpu(e) => write!(f, "Vcpu error: {e}"),
@@ -319,6 +319,24 @@ impl Vmm {
319319
arch::aarch64::configure_system(&self.guest_memory, _smbios_oem_strings)
320320
.map_err(Error::ConfigureSystem)?;
321321
}
322+
323+
#[cfg(target_arch = "riscv64")]
324+
{
325+
fdt::create_fdt(
326+
&self.guest_memory,
327+
&self.arch_memory_info,
328+
vcpus.len() as u32,
329+
self.kernel_cmdline.as_str(),
330+
self.mmio_device_manager.get_device_info(),
331+
_intc,
332+
initrd,
333+
)
334+
.map_err(Error::SetupFDT)?;
335+
336+
arch::riscv64::configure_system(&self.guest_memory, _smbios_oem_strings)
337+
.map_err(Error::ConfigureSystem)?;
338+
}
339+
322340
Ok(())
323341
}
324342

src/vmm/src/linux/vstate.rs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ use kvm_bindings::{
4141
KVM_MAX_CPUID_ENTRIES,
4242
};
4343
use kvm_bindings::{
44-
kvm_create_guest_memfd, kvm_memory_attributes, kvm_userspace_memory_region,
45-
kvm_userspace_memory_region2, KVM_API_VERSION, KVM_MEMORY_ATTRIBUTE_PRIVATE,
46-
KVM_MEM_GUEST_MEMFD, KVM_SYSTEM_EVENT_RESET, KVM_SYSTEM_EVENT_SHUTDOWN,
44+
kvm_create_guest_memfd, kvm_userspace_memory_region, kvm_userspace_memory_region2,
45+
KVM_API_VERSION, KVM_MEM_GUEST_MEMFD, KVM_SYSTEM_EVENT_RESET, KVM_SYSTEM_EVENT_SHUTDOWN,
4746
};
4847
#[cfg(feature = "tee")]
4948
use kvm_bindings::{kvm_enable_cap, KVM_CAP_EXIT_HYPERCALL, KVM_MEMORY_EXIT_FLAG_PRIVATE};
49+
#[cfg(not(target_arch = "riscv64"))]
50+
use kvm_bindings::{kvm_memory_attributes, KVM_MEMORY_ATTRIBUTE_PRIVATE};
5051
use kvm_ioctls::{Cap::*, *};
5152
use utils::eventfd::EventFd;
5253
use utils::signal::{register_signal_handler, sigrtmin, Killable};
@@ -108,6 +109,9 @@ pub enum Error {
108109
#[cfg(target_arch = "aarch64")]
109110
/// Error configuring the general purpose aarch64 registers.
110111
REGSConfiguration(arch::aarch64::regs::Error),
112+
#[cfg(target_arch = "riscv64")]
113+
/// Error configuring the general purpose riscv64 registers.
114+
REGSConfiguration(arch::riscv64::regs::Error),
111115
#[cfg(target_arch = "x86_64")]
112116
/// Error configuring the general purpose registers
113117
REGSConfiguration(arch::x86_64::regs::Error),
@@ -300,6 +304,11 @@ impl Display for Error {
300304
f,
301305
"Error configuring the general purpose aarch64 registers: {e:?}"
302306
),
307+
#[cfg(target_arch = "riscv64")]
308+
REGSConfiguration(e) => write!(
309+
f,
310+
"Error configuring the general purpose riscv64 registers: {e:?}"
311+
),
303312
#[cfg(target_arch = "x86_64")]
304313
REGSConfiguration(e) => {
305314
write!(f, "Error configuring the general purpose registers: {e:?}")
@@ -415,6 +424,9 @@ impl KvmContext {
415424
#[cfg(target_arch = "aarch64")]
416425
let capabilities = [Irqchip, Ioeventfd, Irqfd, UserMemory, ArmPsci02];
417426

427+
#[cfg(target_arch = "riscv64")]
428+
let capabilities = [Irqchip, Ioeventfd, Irqfd, UserMemory];
429+
418430
// Check that all desired capabilities are supported.
419431
match capabilities
420432
.iter()
@@ -624,13 +636,15 @@ impl Vm {
624636
.map_err(Error::SetUserMemoryRegion)?;
625637
};
626638

639+
#[cfg(not(target_arch = "riscv64"))]
627640
let attr = kvm_memory_attributes {
628641
address: start,
629642
size: region.len(),
630643
attributes: KVM_MEMORY_ATTRIBUTE_PRIVATE as u64,
631644
flags: 0,
632645
};
633646

647+
#[cfg(not(target_arch = "riscv64"))]
634648
self.fd
635649
.set_memory_attributes(attr)
636650
.map_err(Error::SetMemoryAttributes)?;
@@ -950,6 +964,32 @@ impl Vcpu {
950964
})
951965
}
952966

967+
/// Constructs a new VCPU for `vm`.
968+
///
969+
/// # Arguments
970+
///
971+
/// * `id` - Represents the CPU number between [0, max vcpus).
972+
/// * `vm_fd` - The kvm `VmFd` for the virtual machine this vcpu will get attached to.
973+
/// * `exit_evt` - An `EventFd` that will be written into when this vcpu exits.
974+
/// * `create_ts` - A timestamp used by the vcpu to calculate its lifetime.
975+
#[cfg(target_arch = "riscv64")]
976+
pub fn new_riscv64(id: u8, vm_fd: &VmFd, exit_evt: EventFd) -> Result<Self> {
977+
let kvm_vcpu = vm_fd.create_vcpu(id as u64).map_err(Error::VcpuFd)?;
978+
let (event_sender, event_receiver) = unbounded();
979+
let (response_sender, response_receiver) = unbounded();
980+
981+
Ok(Vcpu {
982+
fd: kvm_vcpu,
983+
id,
984+
mmio_bus: None,
985+
exit_evt,
986+
event_receiver,
987+
event_sender: Some(event_sender),
988+
response_receiver: Some(response_receiver),
989+
response_sender,
990+
})
991+
}
992+
953993
/// Returns the cpu index as seen by the guest OS.
954994
pub fn cpu_index(&self) -> u8 {
955995
self.id
@@ -1050,6 +1090,25 @@ impl Vcpu {
10501090
Ok(())
10511091
}
10521092

1093+
#[cfg(target_arch = "riscv64")]
1094+
/// Configures an riscv64 specific vcpu.
1095+
///
1096+
/// # Arguments
1097+
///
1098+
/// * `vm_fd` - The kvm `VmFd` for this microvm.
1099+
/// * `guest_mem` - The guest memory used by this microvm.
1100+
/// * `kernel_load_addr` - Offset from `guest_mem` at which the kernel is loaded.
1101+
pub fn configure_riscv64(
1102+
&mut self,
1103+
_vm_fd: &VmFd,
1104+
guest_mem: &GuestMemoryMmap,
1105+
kernel_load_addr: GuestAddress,
1106+
) -> Result<()> {
1107+
arch::riscv64::regs::setup_regs(&self.fd, self.id, kernel_load_addr.raw_value(), guest_mem)
1108+
.map_err(Error::REGSConfiguration)?;
1109+
Ok(())
1110+
}
1111+
10531112
/// Moves the vcpu to its own thread and constructs a VcpuHandle.
10541113
/// The handle can be used to control the remote vcpu.
10551114
pub fn start_threaded(mut self) -> Result<VcpuHandle> {

0 commit comments

Comments
 (0)