Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions alioth/src/arch/aarch64/psci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ c_enum! {

pub const PSCI_VERSION_1_1: u32 = (1 << 16) | 1;

c_enum! {
pub struct PsciErr(i64);
{
NOT_SUPPORTED = -1;
INVALID_PARAMETERS = -2;
DENIED = -3;
ALREADY_ON = -4;
ON_PENDING = -5;
INTERNAL_FAILURE = -6;
NOT_PRESENT = -7;
DISABLED = -8;
INVALID_ADDRESS = -9;
}
}

c_enum! {
/// https://developer.arm.com/documentation/den0022/latest/
pub struct PsciMigrateInfo(u32);
Expand All @@ -67,3 +82,12 @@ c_enum! {
NOT_REQUIRED = 2;
}
}

c_enum! {
pub struct PsciAffinityInfo(u64);
{
ON_PENDING = 2;
OFF = 1;
ON = 0;
}
}
9 changes: 5 additions & 4 deletions alioth/src/board/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,12 @@ pub enum BoardState {
Running,
Shutdown,
RebootPending,
Fatal,
}

#[derive(Debug)]
struct MpSync {
state: BoardState,
fatal: bool,
count: u16,
}

Expand Down Expand Up @@ -251,6 +251,7 @@ where
mp_sync: Mutex::new(MpSync {
state: BoardState::Created,
count: 0,
fatal: false,
}),
cond_var: Condvar::new(),
}
Expand Down Expand Up @@ -365,7 +366,7 @@ where

fn sync_vcpus(&self, vcpus: &VcpuGuard) -> Result<()> {
let mut mp_sync = self.mp_sync.lock();
if mp_sync.state == BoardState::Fatal {
if mp_sync.fatal {
return error::PeerFailure.fail();
}

Expand All @@ -377,7 +378,7 @@ where
self.cond_var.wait(&mut mp_sync)
}

if mp_sync.state == BoardState::Fatal {
if mp_sync.fatal {
return error::PeerFailure.fail();
}

Expand Down Expand Up @@ -491,7 +492,7 @@ where

log::warn!("VCPU-{index} reported error, unblocking other VCPUs...");
let mut mp_sync = self.mp_sync.lock();
mp_sync.state = BoardState::Fatal;
mp_sync.fatal = true;
if mp_sync.count > 0 {
self.cond_var.notify_all();
}
Expand Down
22 changes: 21 additions & 1 deletion alioth/src/hv/hv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use std::thread::JoinHandle;

use serde::Deserialize;
use serde_aco::Help;
use snafu::Snafu;
use snafu::{AsErrorSource, Snafu};

#[cfg(target_arch = "x86_64")]
use crate::arch::cpuid::CpuidIn;
Expand Down Expand Up @@ -100,11 +100,31 @@ pub enum Error {
StopVcpu { error: std::io::Error },
#[snafu(display("Failed to handle VM exit: {msg}"))]
VmExit { msg: String },
#[snafu(display("Broken channel"))]
BrokenChannel,
#[cfg(target_os = "linux")]
#[snafu(display("KVM internal error"), context(false))]
KvmErr { source: Box<KvmError> },
}

impl From<std::sync::mpsc::RecvError> for Error {
fn from(error: std::sync::mpsc::RecvError) -> Self {
let source = error.as_error_source();
Error::BrokenChannel {
_location: snafu::GenerateImplicitData::generate_with_source(source),
}
}
}

impl<T: 'static> From<std::sync::mpsc::SendError<T>> for Error {
fn from(error: std::sync::mpsc::SendError<T>) -> Self {
let source = error.as_error_source();
Error::BrokenChannel {
_location: snafu::GenerateImplicitData::generate_with_source(source),
}
}
}

pub type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, Deserialize, Clone, Help)]
Expand Down
59 changes: 28 additions & 31 deletions alioth/src/hv/hvf/vcpu/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ mod vmentry;
mod vmexit;

use std::collections::HashMap;
use std::io::ErrorKind;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, mpsc};

use parking_lot::Mutex;
use snafu::ResultExt;

use crate::arch::reg::{MpidrEl1, Reg, SReg};
use crate::arch::reg::{MpidrEl1, Pstate, Reg, SReg};
use crate::hv::hvf::check_ret;
use crate::hv::hvf::vm::{HvfVm, VcpuEvent};
use crate::hv::{Result, Vcpu, VmEntry, VmExit, error};
Expand All @@ -36,6 +36,7 @@ use crate::sys::hvf::{
#[derive(Debug)]
pub struct VcpuHandle {
pub vcpu_id: u64,
pub power_on: Arc<AtomicBool>,
pub sender: Sender<VcpuEvent>,
}

Expand All @@ -45,20 +46,17 @@ pub struct HvfVcpu {
vcpu_id: u64,
vmexit: Option<VmExit>,
exit_reg: Option<HvReg>,
vcpus: Arc<Mutex<HashMap<MpidrEl1, VcpuHandle>>>,
vcpus: Arc<Mutex<HashMap<MpidrEl1, Arc<VcpuHandle>>>>,
receiver: Receiver<VcpuEvent>,
power_on: bool,
power_on: Arc<AtomicBool>,
}

impl HvfVcpu {
fn handle_event(&mut self, event: &VcpuEvent) -> Result<()> {
match event {
VcpuEvent::PowerOn { pc, context } => {
self.set_regs(&[(Reg::Pc, *pc), (Reg::X0, *context), (Reg::Pstate, 5)])?;
self.power_on = true;
}
VcpuEvent::PowerOff => self.power_on = false,
}
fn power_on(&mut self, pc: u64, context: u64) -> Result<()> {
let pstate = (Pstate::EL_H | Pstate::EL_BIT2).bits() as u64;
self.set_regs(&[(Reg::Pc, pc), (Reg::X0, context), (Reg::Pstate, pstate)])?;
self.set_sregs(&[(SReg::SCTLR_EL1, 0x0)])?;
self.power_on.store(true, Ordering::Relaxed);
Ok(())
}

Expand All @@ -74,7 +72,12 @@ impl HvfVcpu {

let (sender, receiver) = mpsc::channel();

let handle = VcpuHandle { vcpu_id, sender };
let power_on = Arc::new(AtomicBool::new(false));
let handle = Arc::new(VcpuHandle {
vcpu_id,
sender,
power_on: power_on.clone(),
});

vm.vcpus.lock().insert(mpidr, handle);

Expand All @@ -85,7 +88,7 @@ impl HvfVcpu {
exit_reg: None,
vcpus: vm.vcpus.clone(),
receiver,
power_on: false,
power_on,
})
}
}
Expand Down Expand Up @@ -147,8 +150,8 @@ impl Reg {

impl Vcpu for HvfVcpu {
fn reset(&mut self, is_bsp: bool) -> Result<()> {
self.power_on = is_bsp;
self.set_sregs(&[(SReg::SCTLR_EL1, 0)])
self.power_on.store(is_bsp, Ordering::Relaxed);
self.set_sregs(&[(SReg::SCTLR_EL1, 0x0)])
}

fn dump(&self) -> Result<()> {
Expand All @@ -160,33 +163,27 @@ impl Vcpu for HvfVcpu {
VmEntry::None => {}
VmEntry::Mmio { data } => self.entry_mmio(data)?,
VmEntry::Shutdown => return Ok(VmExit::Shutdown),
_ => unimplemented!("{entry:?}"),
VmEntry::Reboot => return Ok(VmExit::Reboot),
}
if !self.power_on {
let Ok(event) = self.receiver.recv() else {
return Err(ErrorKind::BrokenPipe.into()).context(error::RunVcpu);
};
self.handle_event(&event)?;
if !self.power_on {
return Ok(VmExit::Shutdown);

if !self.power_on.load(Ordering::Relaxed) {
match self.receiver.recv()? {
VcpuEvent::Interrupt => return Ok(VmExit::Interrupted),
VcpuEvent::PowerOn { pc, context } => {
self.power_on(pc, context)?;
}
}
}
loop {
let ret = unsafe { hv_vcpu_run(self.vcpu_id) };
check_ret(ret).context(error::RunVcpu)?;

while let Ok(event) = self.receiver.try_recv() {
self.handle_event(&event)?;
if !self.power_on {
return Ok(VmExit::Shutdown);
}
}

let exit = unsafe { &*self.exit };
match exit.reason {
HvExitReason::EXCEPTION => {
self.handle_exception(&exit.exception)?;
}
HvExitReason::CANCEL => break Ok(VmExit::Interrupted),
_ => {
break error::VmExit {
msg: format!("{exit:x?}"),
Expand Down
47 changes: 43 additions & 4 deletions alioth/src/hv/hvf/vcpu/vmexit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::atomic::Ordering;

use snafu::ResultExt;

use crate::arch::psci::{PSCI_VERSION_1_1, PsciFunc, PsciMigrateInfo};
use crate::arch::psci::{PSCI_VERSION_1_1, PsciAffinityInfo, PsciErr, PsciFunc, PsciMigrateInfo};
use crate::arch::reg::{EsrEl2DataAbort, EsrEl2Ec, EsrEl2SysReg, MpidrEl1, Reg, SReg, encode};
use crate::hv::hvf::check_ret;
use crate::hv::hvf::vcpu::HvfVcpu;
Expand Down Expand Up @@ -80,6 +82,9 @@ impl HvfVcpu {
| PsciFunc::SYSTEM_OFF2_64
| PsciFunc::CPU_ON_32
| PsciFunc::CPU_ON_64
| PsciFunc::CPU_OFF
| PsciFunc::AFFINITY_INFO_32
| PsciFunc::AFFINITY_INFO_64
| PsciFunc::SYSTEM_RESET
| PsciFunc::SYSTEM_RESET2_32
| PsciFunc::SYSTEM_RESET2_64 => 0,
Expand All @@ -95,9 +100,7 @@ impl HvfVcpu {
let pc = self.get_reg(Reg::X2)?;
let context = self.get_reg(Reg::X3)?;
if let Some(vcpu) = self.vcpus.lock().get(&MpidrEl1(mpidr)) {
vcpu.sender
.send(VcpuEvent::PowerOn { pc, context })
.unwrap();
vcpu.sender.send(VcpuEvent::PowerOn { pc, context })?;
0
} else {
log::error!("Failed to find CPU with mpidr {mpidr:#x}");
Expand All @@ -108,6 +111,8 @@ impl HvfVcpu {
self.vmexit = Some(VmExit::Reboot);
return Ok(());
}
PsciFunc::AFFINITY_INFO_32 | PsciFunc::AFFINITY_INFO_64 => self.psci_affinity_info()?,
PsciFunc::CPU_OFF => self.psci_cpu_off()?,
f => {
return error::VmExit {
msg: format!("HVC: {f:x?}"),
Expand Down Expand Up @@ -146,4 +151,38 @@ impl HvfVcpu {
}
.fail()
}

fn psci_affinity_info(&mut self) -> Result<u64> {
let lowest_affinity_level = self.get_reg(Reg::X2)?;
if lowest_affinity_level != 0 {
// PSCI 1.0 and later no longer requires AFFINITY_INFO to
// support affinity levels greater than 0.
return Ok(PsciErr::INVALID_PARAMETERS.raw() as u64);
}
let target_affinity = self.get_reg(Reg::X1)?;
let vcpus = self.vcpus.lock();
let Some(vcpu) = vcpus.get(&MpidrEl1(target_affinity)) else {
return Ok(PsciErr::INVALID_PARAMETERS.raw() as u64);
};
let info = if vcpu.power_on.load(Ordering::Relaxed) {
PsciAffinityInfo::ON
} else {
PsciAffinityInfo::OFF
};
Ok(info.raw())
}

fn psci_cpu_off(&mut self) -> Result<u64> {
self.power_on.store(false, Ordering::Relaxed);
match self.receiver.recv()? {
VcpuEvent::PowerOn { pc, context } => {
self.power_on(pc, context)?;
Ok(context)
}
VcpuEvent::Interrupt => {
self.vmexit = Some(VmExit::Interrupted);
Ok(0)
}
}
}
}
8 changes: 3 additions & 5 deletions alioth/src/hv/hvf/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,13 @@ impl Its for HvfIts {
#[derive(Debug)]
pub enum VcpuEvent {
PowerOn { pc: u64, context: u64 },
PowerOff,
Interrupt,
}

#[derive(Debug)]
pub struct HvfVm {
gic_config: Mutex<(OsObject, bool)>,
pub vcpus: Arc<Mutex<HashMap<MpidrEl1, VcpuHandle>>>,
pub vcpus: Arc<Mutex<HashMap<MpidrEl1, Arc<VcpuHandle>>>>,
}

impl HvfVm {
Expand Down Expand Up @@ -322,9 +322,7 @@ impl Vm for HvfVm {
return Err(ErrorKind::NotFound.into()).context(error::StopVcpu);
};

if vcpu.sender.send(VcpuEvent::PowerOff).is_err() {
return Err(ErrorKind::BrokenPipe.into()).context(error::StopVcpu);
};
vcpu.sender.send(VcpuEvent::Interrupt)?;
let ret = unsafe { hv_vcpus_exit(&vcpu.vcpu_id, 1) };
check_ret(ret).context(error::StopVcpu)
}
Expand Down