vmm: Introduce riscv64 architecture support

Integrate all works done previously to enable booting riscv linux on
riscv platforms, example command:

```console
./target/debug/cloud-hypervisor \
        --kernel path/to/kernel \
        --disk path=path/to/disk \
        --cmdline "console=hvc0 root=/dev/vda rw" \
        --cpus boot=1 \
        --memory size=1024M \
        --seccomp false
```

Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
This commit is contained in:
Ruoqing He
2025-03-11 08:55:07 +08:00
committed by Rob Bradford
parent 08c1bb4b00
commit c99660a8f9
8 changed files with 306 additions and 25 deletions

View File

@@ -21,6 +21,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Barrier, Mutex};
use std::{cmp, io, result, thread};
#[cfg(not(target_arch = "riscv64"))]
use acpi_tables::sdt::Sdt;
use acpi_tables::{aml, Aml};
use anyhow::anyhow;
@@ -390,6 +391,8 @@ impl Vcpu {
self.mpidr = arch::configure_vcpu(&self.vcpu, self.id, boot_setup)
.map_err(Error::VcpuConfiguration)?;
}
#[cfg(target_arch = "riscv64")]
arch::configure_vcpu(&self.vcpu, self.id, boot_setup).map_err(Error::VcpuConfiguration)?;
info!("Configuring vCPU: cpu_id = {}", self.id);
#[cfg(target_arch = "x86_64")]
arch::configure_vcpu(
@@ -413,7 +416,7 @@ impl Vcpu {
}
/// Gets the saved vCPU state.
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
pub fn get_saved_state(&self) -> Option<CpuState> {
self.saved_state.clone()
}
@@ -797,7 +800,7 @@ impl CpuManager {
let topology = self.get_vcpu_topology();
#[cfg(target_arch = "x86_64")]
let x2apic_id = arch::x86_64::get_x2apic_id(cpu_id as u32, topology);
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
let x2apic_id = cpu_id as u32;
let mut vcpu = Vcpu::new(
@@ -871,6 +874,9 @@ impl CpuManager {
#[cfg(target_arch = "aarch64")]
vcpu.configure(&self.vm, boot_setup)?;
#[cfg(target_arch = "riscv64")]
vcpu.configure(boot_setup)?;
Ok(())
}
@@ -1396,6 +1402,7 @@ impl CpuManager {
.map(|t| (t.threads_per_core, t.cores_per_die, t.packages))
}
#[cfg(not(target_arch = "riscv64"))]
pub fn create_madt(&self) -> Sdt {
use crate::acpi;
// This is also checked in the commandline parsing.

View File

@@ -18,15 +18,18 @@ use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::PathBuf;
use std::result;
use std::sync::{Arc, Mutex};
#[cfg(not(target_arch = "riscv64"))]
use std::time::Instant;
use acpi_tables::sdt::GenericAddress;
#[cfg(not(target_arch = "riscv64"))]
use acpi_tables::{aml, Aml};
#[cfg(not(target_arch = "riscv64"))]
use anyhow::anyhow;
#[cfg(target_arch = "x86_64")]
use arch::layout::{APIC_START, IOAPIC_SIZE, IOAPIC_START};
use arch::{layout, NumaNodes};
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use arch::{DeviceType, MmioDeviceInfo};
use block::async_io::DiskFile;
use block::fixed_vhd_sync::FixedVhdDiskSync;
@@ -39,6 +42,10 @@ use block::{
};
#[cfg(feature = "io_uring")]
use block::{fixed_vhd_async::FixedVhdDiskAsync, raw_async::RawFileDisk};
#[cfg(target_arch = "riscv64")]
use devices::aia;
#[cfg(target_arch = "x86_64")]
use devices::debug_console;
#[cfg(target_arch = "x86_64")]
use devices::debug_console::DebugConsole;
#[cfg(target_arch = "aarch64")]
@@ -48,6 +55,8 @@ use devices::interrupt_controller::InterruptController;
use devices::ioapic;
#[cfg(target_arch = "aarch64")]
use devices::legacy::Pl011;
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
use devices::legacy::Serial;
#[cfg(feature = "pvmemcontrol")]
use devices::pvmemcontrol::{PvmemcontrolBusDevice, PvmemcontrolPciDevice};
use devices::{interrupt_controller, AcpiNotificationFlags};
@@ -88,8 +97,6 @@ use vm_migration::{
};
use vm_virtio::{AccessPlatform, VirtioDeviceType};
use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "x86_64")]
use {devices::debug_console, devices::legacy::Serial};
use crate::console_devices::{ConsoleDeviceError, ConsoleInfo, ConsoleOutput};
use crate::cpu::{CpuManager, CPU_MANAGER_ACPI_SIZE};
@@ -105,7 +112,7 @@ use crate::vm_config::{
};
use crate::{device_node, GuestRegionMmap, PciDeviceInfo, DEVICE_MANAGER_SNAPSHOT_ID};
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
const MMIO_LEN: u64 = 0x1000;
// Singleton devices / devices the user cannot name
@@ -823,9 +830,11 @@ pub struct DeviceManager {
interrupt_controller: Option<Arc<Mutex<ioapic::Ioapic>>>,
#[cfg(target_arch = "aarch64")]
interrupt_controller: Option<Arc<Mutex<gic::Gic>>>,
#[cfg(target_arch = "riscv64")]
interrupt_controller: Option<Arc<Mutex<aia::Aia>>>,
// Things to be added to the commandline (e.g. aarch64 early console)
#[cfg(target_arch = "aarch64")]
// Things to be added to the commandline (e.g. aarch64 or riscv64 early console)
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
cmdline_additions: Vec<String>,
// ACPI GED notification device
@@ -888,7 +897,7 @@ pub struct DeviceManager {
exit_evt: EventFd,
reset_evt: EventFd,
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
id_to_dev_info: HashMap<(DeviceType, String), MmioDeviceInfo>,
// seccomp action
@@ -904,6 +913,7 @@ pub struct DeviceManager {
// activation and thus start the threads from the VMM thread
activate_evt: EventFd,
#[cfg(not(target_arch = "riscv64"))]
acpi_address: GuestAddress,
selected_segment: usize,
@@ -936,12 +946,14 @@ pub struct DeviceManager {
// List of unique identifiers provided at boot through the configuration.
boot_id_list: BTreeSet<String>,
#[cfg(not(target_arch = "riscv64"))]
// Start time of the VM
timestamp: Instant,
// Pending activations
pending_activations: Arc<Mutex<Vec<VirtioPciDeviceActivator>>>,
#[cfg(not(target_arch = "riscv64"))]
// Addresses for ACPI platform devices e.g. ACPI PM timer, sleep/reset registers
acpi_platform_addresses: AcpiPlatformAddresses,
@@ -996,7 +1008,7 @@ impl DeviceManager {
activate_evt: &EventFd,
force_iommu: bool,
boot_id_list: BTreeSet<String>,
timestamp: Instant,
#[cfg(not(target_arch = "riscv64"))] timestamp: Instant,
snapshot: Option<Snapshot>,
dynamic: bool,
) -> DeviceManagerResult<Arc<Mutex<Self>>> {
@@ -1161,7 +1173,7 @@ impl DeviceManager {
address_manager: Arc::clone(&address_manager),
console: Arc::new(Console::default()),
interrupt_controller: None,
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
cmdline_additions: Vec::new(),
ged_notification_device: None,
config,
@@ -1181,7 +1193,7 @@ impl DeviceManager {
device_tree,
exit_evt,
reset_evt,
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
id_to_dev_info: HashMap::new(),
seccomp_action,
numa_nodes,
@@ -1189,6 +1201,7 @@ impl DeviceManager {
activate_evt: activate_evt
.try_clone()
.map_err(DeviceManagerError::EventFd)?,
#[cfg(not(target_arch = "riscv64"))]
acpi_address,
selected_segment: 0,
serial_manager: None,
@@ -1204,8 +1217,10 @@ impl DeviceManager {
io_uring_supported: None,
aio_supported: None,
boot_id_list,
#[cfg(not(target_arch = "riscv64"))]
timestamp,
pending_activations: Arc::new(Mutex::new(Vec::default())),
#[cfg(not(target_arch = "riscv64"))]
acpi_platform_addresses: AcpiPlatformAddresses::default(),
snapshot,
rate_limit_groups,
@@ -1299,6 +1314,7 @@ impl DeviceManager {
console_resize_pipe,
)?;
#[cfg(not(target_arch = "riscv64"))]
if let Some(tpm) = self.config.clone().lock().unwrap().tpm.as_ref() {
let tpm_dev = self.add_tpm_device(tpm.socket.clone())?;
self.bus_devices
@@ -1347,11 +1363,21 @@ impl DeviceManager {
vgic_config.msi_addr + vgic_config.msi_size - 1,
)
}
#[cfg(target_arch = "riscv64")]
{
let vcpus = self.config.lock().unwrap().cpus.boot_vcpus;
let vaia_config = aia::Aia::create_default_config(vcpus.into());
(
vaia_config.imsic_addr,
vaia_config.imsic_addr + vaia_config.vcpu_count as u64 * arch::layout::IMSIC_SIZE
- 1,
)
}
#[cfg(target_arch = "x86_64")]
(0xfee0_0000, 0xfeef_ffff)
}
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
/// Gets the information of the devices registered up to some point in time.
pub fn get_device_info(&self) -> &HashMap<(DeviceType, String), MmioDeviceInfo> {
&self.id_to_dev_info
@@ -1516,6 +1542,43 @@ impl DeviceManager {
self.interrupt_controller.as_ref()
}
#[cfg(target_arch = "riscv64")]
fn add_interrupt_controller(
&mut self,
) -> DeviceManagerResult<Arc<Mutex<dyn InterruptController>>> {
let interrupt_controller: Arc<Mutex<aia::Aia>> = Arc::new(Mutex::new(
aia::Aia::new(
self.config.lock().unwrap().cpus.boot_vcpus,
Arc::clone(&self.msi_interrupt_manager),
self.address_manager.vm.clone(),
)
.map_err(DeviceManagerError::CreateInterruptController)?,
));
self.interrupt_controller = Some(interrupt_controller.clone());
// Restore the vAia if this is in the process of restoration
let id = String::from(aia::_AIA_SNAPSHOT_ID);
if let Some(_vaia_snapshot) = snapshot_from_id(self.snapshot.as_ref(), &id) {
// TODO: vAia snapshotting and restoration is scheduled to next stage of riscv64 support.
// TODO: PMU support is scheduled to next stage of riscv64 support.
// PMU support is optional. Nothing should be impacted if the PMU initialization failed.
unimplemented!()
}
self.device_tree
.lock()
.unwrap()
.insert(id.clone(), device_node!(id, interrupt_controller));
Ok(interrupt_controller)
}
#[cfg(target_arch = "riscv64")]
pub fn get_interrupt_controller(&mut self) -> Option<&Arc<Mutex<aia::Aia>>> {
self.interrupt_controller.as_ref()
}
#[cfg(target_arch = "x86_64")]
fn add_interrupt_controller(
&mut self,
@@ -1995,6 +2058,69 @@ impl DeviceManager {
Ok(serial)
}
#[cfg(target_arch = "riscv64")]
fn add_serial_device(
&mut self,
interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = LegacyIrqGroupConfig>>,
serial_writer: Option<Box<dyn io::Write + Send>>,
) -> DeviceManagerResult<Arc<Mutex<Serial>>> {
let id = String::from(SERIAL_DEVICE_NAME);
let serial_irq = self
.address_manager
.allocator
.lock()
.unwrap()
.allocate_irq()
.unwrap();
let interrupt_group = interrupt_manager
.create_group(LegacyIrqGroupConfig {
irq: serial_irq as InterruptIndex,
})
.map_err(DeviceManagerError::CreateInterruptGroup)?;
let serial = Arc::new(Mutex::new(Serial::new(
id.clone(),
interrupt_group,
serial_writer,
state_from_id(self.snapshot.as_ref(), id.as_str())
.map_err(DeviceManagerError::RestoreGetState)?,
)));
self.bus_devices
.push(Arc::clone(&serial) as Arc<dyn BusDeviceSync>);
let addr = arch::layout::LEGACY_SERIAL_MAPPED_IO_START;
self.address_manager
.mmio_bus
.insert(serial.clone(), addr.0, MMIO_LEN)
.map_err(DeviceManagerError::BusError)?;
self.id_to_dev_info.insert(
(DeviceType::Serial, DeviceType::Serial.to_string()),
MmioDeviceInfo {
addr: addr.0,
len: MMIO_LEN,
irq: serial_irq,
},
);
self.cmdline_additions
.push(format!("earlycon=uart,mmio,0x{:08x}", addr.0));
// Fill the device tree with a new node. In case of restore, we
// know there is nothing to do, so we can simply override the
// existing entry.
self.device_tree
.lock()
.unwrap()
.insert(id.clone(), device_node!(id, serial));
Ok(serial)
}
fn add_virtio_console_device(
&mut self,
virtio_devices: &mut Vec<MetaVirtioDevice>,
@@ -2159,6 +2285,7 @@ impl DeviceManager {
Ok(Arc::new(Console { console_resizer }))
}
#[cfg(not(target_arch = "riscv64"))]
fn add_tpm_device(
&mut self,
tpm_path: PathBuf,
@@ -3905,7 +4032,7 @@ impl DeviceManager {
&self.pci_segments
}
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
pub fn cmdline_additions(&self) -> &[String] {
self.cmdline_additions.as_slice()
}
@@ -4491,6 +4618,7 @@ impl DeviceManager {
Ok(())
}
#[cfg(not(target_arch = "riscv64"))]
pub(crate) fn acpi_platform_addresses(&self) -> &AcpiPlatformAddresses {
&self.acpi_platform_addresses
}
@@ -4516,8 +4644,10 @@ fn numa_node_id_from_pci_segment_id(numa_nodes: &NumaNodes, pci_segment_id: u16)
0
}
#[cfg(not(target_arch = "riscv64"))]
struct TpmDevice {}
#[cfg(not(target_arch = "riscv64"))]
impl Aml for TpmDevice {
fn to_aml_bytes(&self, sink: &mut dyn acpi_tables::AmlSink) {
aml::Device::new(
@@ -4539,6 +4669,7 @@ impl Aml for TpmDevice {
}
}
#[cfg(not(target_arch = "riscv64"))]
impl Aml for DeviceManager {
fn to_aml_bytes(&self, sink: &mut dyn acpi_tables::AmlSink) {
#[cfg(target_arch = "aarch64")]

View File

@@ -19,6 +19,7 @@ use std::path::PathBuf;
use std::rc::Rc;
use std::sync::mpsc::{Receiver, RecvError, SendError, Sender};
use std::sync::{Arc, Mutex};
#[cfg(not(target_arch = "riscv64"))]
use std::time::Instant;
use std::{io, result, thread};
@@ -64,6 +65,7 @@ use crate::vm_config::{
VmConfig, VsockConfig,
};
#[cfg(not(target_arch = "riscv64"))]
mod acpi;
pub mod api;
mod clone3;
@@ -940,6 +942,7 @@ impl Vmm {
MigratableError::MigrateReceive(anyhow!("Error cloning activate EventFd: {}", e))
})?;
#[cfg(not(target_arch = "riscv64"))]
let timestamp = Instant::now();
let hypervisor_vm = mm.lock().unwrap().vm.clone();
let mut vm = Vm::new_from_memory_manager(
@@ -953,6 +956,7 @@ impl Vmm {
&self.seccomp_action,
self.hypervisor.clone(),
activate_evt,
#[cfg(not(target_arch = "riscv64"))]
timestamp,
self.console_info.clone(),
self.console_resize_pipe.clone(),

View File

@@ -1246,6 +1246,11 @@ impl MemoryManager {
memory_manager.add_uefi_flash()?;
}
#[cfg(target_arch = "riscv64")]
{
memory_manager.allocate_address_space()?;
}
#[cfg(target_arch = "x86_64")]
if let Some(sgx_epc_config) = sgx_epc_config {
memory_manager.setup_sgx(sgx_epc_config)?;
@@ -1607,6 +1612,7 @@ impl MemoryManager {
.checked_add(1)
.ok_or(Error::GuestAddressOverFlow)?;
#[cfg(not(target_arch = "riscv64"))]
if mem_end < arch::layout::MEM_32BIT_RESERVED_START {
return Ok(arch::layout::RAM_64BIT_START);
}

View File

@@ -141,7 +141,7 @@ impl PciSegment {
Ok(segment)
}
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
pub(crate) fn new_default_segment(
address_manager: &Arc<AddressManager>,
mem32_allocator: Arc<Mutex<AddressAllocator>>,

View File

@@ -447,6 +447,12 @@ fn create_vmm_ioctl_seccomp_rule_kvm() -> Result<Vec<SeccompRule>, BackendError>
Ok(arch_rules)
}
#[cfg(all(target_arch = "riscv64", feature = "kvm"))]
fn create_vmm_ioctl_seccomp_rule_kvm() -> Result<Vec<SeccompRule>, BackendError> {
let common_rules = create_vmm_ioctl_seccomp_rule_common(HypervisorType::Kvm)?;
Ok(common_rules)
}
#[cfg(all(target_arch = "x86_64", feature = "mshv"))]
fn create_vmm_ioctl_seccomp_rule_mshv() -> Result<Vec<SeccompRule>, BackendError> {
create_vmm_ioctl_seccomp_rule_common(HypervisorType::Mshv)

View File

@@ -16,7 +16,7 @@ use std::{io, result, thread};
#[cfg(target_arch = "aarch64")]
use devices::legacy::Pl011;
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
use devices::legacy::Serial;
use libc::EFD_NONBLOCK;
use serial_buffer::SerialBuffer;
@@ -108,7 +108,7 @@ impl From<u64> for EpollDispatch {
}
pub struct SerialManager {
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
serial: Arc<Mutex<Serial>>,
#[cfg(target_arch = "aarch64")]
serial: Arc<Mutex<Pl011>>,
@@ -122,7 +122,7 @@ pub struct SerialManager {
impl SerialManager {
pub fn new(
#[cfg(target_arch = "x86_64")] serial: Arc<Mutex<Serial>>,
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] serial: Arc<Mutex<Serial>>,
#[cfg(target_arch = "aarch64")] serial: Arc<Mutex<Pl011>>,
mut output: ConsoleOutput,
socket: Option<PathBuf>,
@@ -226,7 +226,7 @@ impl SerialManager {
// after the connection happened, and if that's the case it flushes
// all output from the serial to the PTY. Otherwise, it's a no-op.
fn trigger_pty_flush(
#[cfg(target_arch = "x86_64")] serial: &Arc<Mutex<Serial>>,
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] serial: &Arc<Mutex<Serial>>,
#[cfg(target_arch = "aarch64")] serial: &Arc<Mutex<Pl011>>,
pty_write_out: Option<&Arc<AtomicBool>>,
) -> Result<()> {

View File

@@ -20,6 +20,7 @@ use std::num::Wrapping;
use std::ops::Deref;
use std::os::unix::net::UnixStream;
use std::sync::{Arc, Mutex, RwLock};
#[cfg(not(target_arch = "riscv64"))]
use std::time::Instant;
use std::{cmp, result, str, thread};
@@ -28,7 +29,7 @@ use anyhow::anyhow;
use arch::layout::{KVM_IDENTITY_MAP_START, KVM_TSS_START};
#[cfg(feature = "tdx")]
use arch::x86_64::tdx::TdvfSection;
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use arch::PciSpaceInfo;
use arch::{get_host_cpu_phys_bits, EntryPoint, NumaNode, NumaNodes};
#[cfg(target_arch = "aarch64")]
@@ -47,7 +48,7 @@ use linux_loader::elf;
use linux_loader::loader::bzimage::BzImage;
#[cfg(target_arch = "x86_64")]
use linux_loader::loader::elf::PvhBootCapability::PvhEntryPresent;
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
use linux_loader::loader::pe::Error::InvalidImageMagicNumber;
use linux_loader::loader::KernelLoader;
use seccompiler::SeccompAction;
@@ -466,8 +467,10 @@ pub struct Vm {
vm: Arc<dyn hypervisor::Vm>,
#[cfg(target_arch = "x86_64")]
saved_clock: Option<hypervisor::ClockData>,
#[cfg(not(target_arch = "riscv64"))]
numa_nodes: NumaNodes,
#[cfg_attr(any(not(feature = "kvm"), target_arch = "aarch64"), allow(dead_code))]
#[cfg(not(target_arch = "riscv64"))]
hypervisor: Arc<dyn hypervisor::Hypervisor>,
stop_on_boot: bool,
load_payload_handle: Option<thread::JoinHandle<Result<EntryPoint>>>,
@@ -487,7 +490,7 @@ impl Vm {
seccomp_action: &SeccompAction,
hypervisor: Arc<dyn hypervisor::Hypervisor>,
activate_evt: EventFd,
timestamp: Instant,
#[cfg(not(target_arch = "riscv64"))] timestamp: Instant,
console_info: Option<ConsoleInfo>,
console_resize_pipe: Option<Arc<File>>,
original_termios: Arc<Mutex<Option<termios>>>,
@@ -634,6 +637,7 @@ impl Vm {
&activate_evt,
force_iommu,
boot_id_list,
#[cfg(not(target_arch = "riscv64"))]
timestamp,
snapshot_from_id(snapshot.as_ref(), DEVICE_MANAGER_SNAPSHOT_ID),
dynamic,
@@ -694,7 +698,9 @@ impl Vm {
vm,
#[cfg(target_arch = "x86_64")]
saved_clock,
#[cfg(not(target_arch = "riscv64"))]
numa_nodes,
#[cfg(not(target_arch = "riscv64"))]
hypervisor,
stop_on_boot,
load_payload_handle,
@@ -803,6 +809,7 @@ impl Vm {
) -> Result<Self> {
trace_scoped!("Vm::new");
#[cfg(not(target_arch = "riscv64"))]
let timestamp = Instant::now();
#[cfg(feature = "tdx")]
@@ -873,6 +880,7 @@ impl Vm {
seccomp_action,
hypervisor,
activate_evt,
#[cfg(not(target_arch = "riscv64"))]
timestamp,
console_info,
console_resize_pipe,
@@ -943,14 +951,16 @@ impl Vm {
pub fn generate_cmdline(
payload: &PayloadConfig,
#[cfg(target_arch = "aarch64")] device_manager: &Arc<Mutex<DeviceManager>>,
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] device_manager: &Arc<
Mutex<DeviceManager>,
>,
) -> Result<Cmdline> {
let mut cmdline = Cmdline::new(arch::CMDLINE_MAX_SIZE).map_err(Error::CmdLineCreate)?;
if let Some(s) = payload.cmdline.as_ref() {
cmdline.insert_str(s).map_err(Error::CmdLineInsertStr)?;
}
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
for entry in device_manager.lock().unwrap().cmdline_additions() {
cmdline.insert_str(entry).map_err(Error::CmdLineInsertStr)?;
}
@@ -1005,6 +1015,47 @@ impl Vm {
Ok(EntryPoint { entry_addr })
}
#[cfg(target_arch = "riscv64")]
fn load_kernel(
firmware: Option<File>,
kernel: Option<File>,
memory_manager: Arc<Mutex<MemoryManager>>,
) -> Result<EntryPoint> {
let guest_memory = memory_manager.lock().as_ref().unwrap().guest_memory();
let mem = guest_memory.memory();
let alignment = 0x20_0000;
let aligned_kernel_addr = arch::layout::KERNEL_START.0 + (alignment - 1) & !(alignment - 1);
let entry_addr = match (firmware, kernel) {
(None, Some(mut kernel)) => {
match linux_loader::loader::pe::PE::load(
mem.deref(),
Some(GuestAddress(aligned_kernel_addr)),
&mut kernel,
None,
) {
Ok(entry_addr) => entry_addr.kernel_load,
// Try to load the binary as kernel PE file at first.
// If failed, retry to load it as UEFI binary.
// As the UEFI binary is formatless, it must be the last option to try.
Err(linux_loader::loader::Error::Pe(InvalidImageMagicNumber)) => {
// TODO: UEFI for riscv64 is scheduled to next stage.
unimplemented!()
}
Err(e) => {
return Err(Error::KernelLoad(e));
}
}
}
(Some(_firmware), None) => {
// TODO: UEFI for riscv64 is scheduled to next stage.
unimplemented!()
}
_ => return Err(Error::InvalidPayload),
};
Ok(EntryPoint { entry_addr })
}
#[cfg(feature = "igvm")]
fn load_igvm(
igvm: File,
@@ -1133,7 +1184,7 @@ impl Vm {
}
}
#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
fn load_payload(
payload: &PayloadConfig,
memory_manager: Arc<Mutex<MemoryManager>>,
@@ -1351,6 +1402,72 @@ impl Vm {
Ok(())
}
#[cfg(target_arch = "riscv64")]
fn configure_system(&mut self) -> Result<()> {
let cmdline = Self::generate_cmdline(
self.config.lock().unwrap().payload.as_ref().unwrap(),
&self.device_manager,
)?;
let num_vcpu = self.cpu_manager.lock().unwrap().vcpus().len();
let mem = self.memory_manager.lock().unwrap().boot_guest_memory();
let mut pci_space_info: Vec<PciSpaceInfo> = Vec::new();
let initramfs_config = match self.initramfs {
Some(_) => Some(self.load_initramfs(&mem)?),
None => None,
};
let device_info = &self
.device_manager
.lock()
.unwrap()
.get_device_info()
.clone();
for pci_segment in self.device_manager.lock().unwrap().pci_segments().iter() {
let pci_space = PciSpaceInfo {
pci_segment_id: pci_segment.id,
mmio_config_address: pci_segment.mmio_config_address,
pci_device_space_start: pci_segment.start_of_mem64_area,
pci_device_space_size: pci_segment.end_of_mem64_area
- pci_segment.start_of_mem64_area
+ 1,
};
pci_space_info.push(pci_space);
}
// TODO: IOMMU for riscv64 is not yet support in kernel.
let vaia = self
.device_manager
.lock()
.unwrap()
.get_interrupt_controller()
.unwrap()
.lock()
.unwrap()
.get_vaia()
.map_err(|_| {
Error::ConfigureSystem(arch::Error::PlatformSpecific(
arch::riscv64::Error::SetupAia,
))
})?;
// TODO: PMU support for riscv64 is scheduled to next stage.
arch::configure_system(
&mem,
cmdline.as_cstring().unwrap().to_str().unwrap(),
num_vcpu as u32,
device_info,
&initramfs_config,
&pci_space_info,
&vaia,
)
.map_err(Error::ConfigureSystem)?;
Ok(())
}
pub fn console_resize_pipe(&self) -> Option<Arc<File>> {
self.device_manager.lock().unwrap().console_resize_pipe()
}
@@ -2017,6 +2134,7 @@ impl Vm {
// In case of TDX being used, this is a no-op since the tables will be
// created and passed when populating the HOB.
#[cfg(not(target_arch = "riscv64"))]
fn create_acpi_tables(&self) -> Option<GuestAddress> {
#[cfg(feature = "tdx")]
if self.config.lock().unwrap().is_tdx_enabled() {
@@ -2118,6 +2236,7 @@ impl Vm {
#[cfg(target_arch = "aarch64")]
let rsdp_addr = self.create_acpi_tables();
#[cfg(not(target_arch = "riscv64"))]
// Configure shared state based on loaded kernel
entry_point
.map(|entry_point| {
@@ -2127,6 +2246,9 @@ impl Vm {
})
.transpose()?;
#[cfg(target_arch = "riscv64")]
self.configure_system().unwrap();
#[cfg(target_arch = "x86_64")]
// Note: For x86, always call this function before invoking start boot vcpus.
// Otherwise guest would fail to boot because we haven't created the
@@ -2327,6 +2449,11 @@ impl Vm {
.map_err(Error::PowerButton)
}
#[cfg(target_arch = "riscv64")]
pub fn power_button(&self) -> Result<()> {
unimplemented!()
}
pub fn memory_manager_data(&self) -> MemoryManagerSnapshotData {
self.memory_manager.lock().unwrap().snapshot_data()
}