blob: 22ce80de9012d0abdf490d931f36703a416202cf [file]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use base::errno_result;
use base::error;
use base::ioctl_with_ref;
use base::Error;
use base::Result;
use kvm_sys::*;
use libc::ENXIO;
use super::Config;
use super::Kvm;
use super::KvmVcpu;
use super::KvmVm;
use crate::ClockState;
use crate::DeviceKind;
use crate::Hypervisor;
use crate::IrqSourceChip;
use crate::ProtectionType;
use crate::VcpuExit;
use crate::VcpuRegister;
use crate::VcpuRiscv64;
use crate::VmCap;
use crate::VmRiscv64;
impl KvmVm {
/// Does platform specific initialization for the KvmVm.
pub fn init_arch(&self, _cfg: &Config) -> Result<()> {
Ok(())
}
/// Checks if a particular `VmCap` is available, or returns None if arch-independent
/// Vm.check_capability() should handle the check.
pub fn check_capability_arch(&self, _c: VmCap) -> Option<bool> {
None
}
/// Returns the params to pass to KVM_CREATE_DEVICE for a `kind` device on this arch, or None to
/// let the arch-independent `KvmVm::create_device` handle it.
pub fn get_device_params_arch(&self, kind: DeviceKind) -> Option<kvm_create_device> {
match kind {
DeviceKind::RiscvAia => Some(kvm_create_device {
type_: kvm_device_type_KVM_DEV_TYPE_RISCV_AIA,
fd: 0,
flags: 0,
}),
_ => None,
}
}
/// Arch-specific implementation of `Vm::get_pvclock`. Always returns an error on riscv64.
pub fn get_pvclock_arch(&self) -> Result<ClockState> {
Err(Error::new(ENXIO))
}
/// Arch-specific implementation of `Vm::set_pvclock`. Always returns an error on riscv64.
pub fn set_pvclock_arch(&self, _state: &ClockState) -> Result<()> {
Err(Error::new(ENXIO))
}
}
impl Kvm {
// The riscv machine type is always 0. Protected VMs are not supported, yet.
pub fn get_vm_type(&self, protection_type: ProtectionType) -> Result<u32> {
if protection_type == ProtectionType::Unprotected {
Ok(0)
} else {
error!("Protected mode is not supported on riscv64.");
Err(Error::new(libc::EINVAL))
}
}
/// Get the size of guest physical addresses in bits.
pub fn get_guest_phys_addr_bits(&self) -> u8 {
// assume sv48 addressing
48
}
}
impl VmRiscv64 for KvmVm {
fn get_hypervisor(&self) -> &dyn Hypervisor {
&self.kvm
}
fn create_vcpu(&self, id: usize) -> Result<Box<dyn VcpuRiscv64>> {
// create_vcpu is declared separately for each arch so it can return the arch-apropriate
// vcpu type. But all use the same implementation in KvmVm::create_vcpu.
Ok(Box::new(self.create_kvm_vcpu(id)?))
}
}
impl KvmVcpu {
/// Handles a `KVM_EXIT_SYSTEM_EVENT` with event type `KVM_SYSTEM_EVENT_RESET` with the given
/// event flags and returns the appropriate `VcpuExit` value for the run loop to handle.
///
/// `event_flags` should be one or more of the `KVM_SYSTEM_EVENT_RESET_FLAG_*` values defined by
/// KVM.
pub fn system_event_reset(&self, _event_flags: u64) -> Result<VcpuExit> {
Ok(VcpuExit::SystemEventReset)
}
#[inline]
pub(crate) fn handle_vm_exit_arch(&self, run: &mut kvm_run) -> Option<VcpuExit> {
match run.exit_reason {
KVM_EXIT_RISCV_SBI => {
// SAFETY: Safe because we trust the kernel to correctly fill in the union
let extension_id = unsafe { run.__bindgen_anon_1.riscv_sbi.extension_id };
// SAFETY: Safe because we trust the kernel to correctly fill in the union
let function_id = unsafe { run.__bindgen_anon_1.riscv_sbi.function_id };
// SAFETY: Safe because we trust the kernel to correctly fill in the union
let args = unsafe { run.__bindgen_anon_1.riscv_sbi.args };
Some(VcpuExit::Sbi {
extension_id,
function_id,
args,
})
}
KVM_EXIT_RISCV_CSR => {
// SAFETY: Safe because we trust the kernel to correctly fill in the union
let csr_num = unsafe { run.__bindgen_anon_1.riscv_csr.csr_num };
// SAFETY: Safe because we trust the kernel to correctly fill in the union
let new_value = unsafe { run.__bindgen_anon_1.riscv_csr.new_value };
// SAFETY: Safe because we trust the kernel to correctly fill in the union
let write_mask = unsafe { run.__bindgen_anon_1.riscv_csr.write_mask };
// SAFETY: Safe because we trust the kernel to correctly fill in the union
let ret_value = unsafe { run.__bindgen_anon_1.riscv_csr.ret_value };
Some(VcpuExit::RiscvCsr {
csr_num,
new_value,
write_mask,
ret_value,
})
}
_ => None,
}
}
}
impl VcpuRiscv64 for KvmVcpu {
fn set_one_reg(&self, reg: VcpuRegister, data: u64) -> Result<()> {
let data_ref = &data as *const u64;
let onereg = kvm_one_reg {
id: vcpu_reg_id(reg),
addr: data_ref as u64,
};
// SAFETY: Safe because we allocated the struct and we know the kernel will read exactly the
// size of the struct.
let ret = unsafe { ioctl_with_ref(self, KVM_SET_ONE_REG, &onereg) };
if ret == 0 {
Ok(())
} else {
errno_result()
}
}
fn get_one_reg(&self, reg: VcpuRegister) -> Result<u64> {
let mut val: u64 = 0;
let onereg = kvm_one_reg {
id: vcpu_reg_id(reg),
addr: (&mut val as *mut u64) as u64,
};
// SAFETY: Safe because we allocated the struct and we know the kernel will read exactly the
// size of the struct.
let ret = unsafe { ioctl_with_ref(self, KVM_GET_ONE_REG, &onereg) };
if ret == 0 {
Ok(val)
} else {
errno_result()
}
}
}
// Returns the id used for call to `KVM_[GET|SET]_ONE_REG`.
fn vcpu_reg_id(reg: VcpuRegister) -> u64 {
fn id_from_reg(reg_type: u32, index: u64) -> u64 {
reg_type as u64 | index | KVM_REG_RISCV as u64 | KVM_REG_SIZE_U64
}
match reg {
VcpuRegister::Config(r) => id_from_reg(KVM_REG_RISCV_CONFIG, r as u64),
VcpuRegister::Core(r) => id_from_reg(KVM_REG_RISCV_CORE, r as u64),
VcpuRegister::Timer(r) => id_from_reg(KVM_REG_RISCV_TIMER, r as u64),
}
}
// This function translates an IrqSrouceChip to the kvm u32 equivalent. It has a different
// implementation between the architectures because the irqchip KVM constants are not defined on all
// of them.
pub(super) fn chip_to_kvm_chip(chip: IrqSourceChip) -> u32 {
match chip {
// Riscv does not have a constant for this, but the default routing
// setup seems to set this to 0
IrqSourceChip::Aia => 0,
_ => {
error!("Invalid IrqChipSource for Riscv {:?}", chip);
0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CoreRegister;
#[test]
fn reg_id() {
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::Pc)),
0x8030_0000_0200_0000
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::Ra)),
0x8030_0000_0200_0001
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::Sp)),
0x8030_0000_0200_0002
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::Gp)),
0x8030_0000_0200_0003
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::Tp)),
0x8030_0000_0200_0004
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::T0)),
0x8030_0000_0200_0005
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::T1)),
0x8030_0000_0200_0006
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::T2)),
0x8030_0000_0200_0007
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S0)),
0x8030_0000_0200_0008
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S1)),
0x8030_0000_0200_0009
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::A0)),
0x8030_0000_0200_000a
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::A1)),
0x8030_0000_0200_000b
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::A2)),
0x8030_0000_0200_000c
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::A3)),
0x8030_0000_0200_000d
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::A4)),
0x8030_0000_0200_000e
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::A5)),
0x8030_0000_0200_000f
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::A6)),
0x8030_0000_0200_0010
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::A7)),
0x8030_0000_0200_0011
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S2)),
0x8030_0000_0200_0012
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S3)),
0x8030_0000_0200_0013
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S4)),
0x8030_0000_0200_0014
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S5)),
0x8030_0000_0200_0015
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S6)),
0x8030_0000_0200_0016
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S7)),
0x8030_0000_0200_0017
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S8)),
0x8030_0000_0200_0018
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S9)),
0x8030_0000_0200_0019
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S10)),
0x8030_0000_0200_001a
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::S11)),
0x8030_0000_0200_001b
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::T3)),
0x8030_0000_0200_001c
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::T4)),
0x8030_0000_0200_001d
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::T5)),
0x8030_0000_0200_001e
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::T6)),
0x8030_0000_0200_001f
);
assert_eq!(
vcpu_reg_id(VcpuRegister::Core(CoreRegister::Mode)),
0x8030_0000_0200_0020
);
}
}