use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, PoisonError, mpsc};
use arcbox_hv::reg::{HV_REG_X0 as X0, HV_REG_X1 as X1, HV_REG_X2 as X2, HV_REG_X3 as X3};
const PSCI_CPU_ON_64: u64 = 0xC400_0003;
const PSCI_SYSTEM_OFF: u64 = 0x8400_0008;
const PSCI_SYSTEM_RESET: u64 = 0x8400_0009;
const PSCI_VERSION: u64 = 0x8400_0000;
const PSCI_SUCCESS: u64 = 0;
const PSCI_ALREADY_ON: u64 = (-4_i64) as u64;
pub struct CpuOnRequest {
pub _target_cpu: u64,
pub entry_point: u64,
pub context_id: u64,
}
pub type CpuOnSenders = Arc<Mutex<Vec<Option<mpsc::Sender<CpuOnRequest>>>>>;
pub fn handle_psci(
vcpu_id: u32,
func_id: u64,
vcpu: &arcbox_hv::HvVcpu,
running: &Arc<AtomicBool>,
cpu_on_senders: Option<&CpuOnSenders>,
) {
match func_id {
PSCI_VERSION => {
let _ = vcpu.set_reg(X0, 1 << 16);
}
PSCI_SYSTEM_OFF => {
tracing::info!("vCPU {vcpu_id}: PSCI SYSTEM_OFF");
running.store(false, Ordering::SeqCst);
}
PSCI_SYSTEM_RESET => {
tracing::info!("vCPU {vcpu_id}: PSCI SYSTEM_RESET");
running.store(false, Ordering::SeqCst);
}
PSCI_CPU_ON_64 => {
let target_mpidr = vcpu.get_reg(X1).unwrap_or(0);
let entry_point = vcpu.get_reg(X2).unwrap_or(0);
let context_id = vcpu.get_reg(X3).unwrap_or(0);
let target_cpu = (target_mpidr & 0xFF) as usize;
if let Some(senders) = cpu_on_senders {
let mut senders_guard = senders.lock().unwrap_or_else(PoisonError::into_inner);
if let Some(sender) = senders_guard.get_mut(target_cpu).and_then(|s| s.take()) {
match sender.send(CpuOnRequest {
_target_cpu: target_mpidr,
entry_point,
context_id,
}) {
Ok(()) => {
tracing::info!(
"vCPU {vcpu_id}: PSCI CPU_ON target={target_cpu} \
entry={entry_point:#x} ctx={context_id:#x}"
);
let _ = vcpu.set_reg(X0, PSCI_SUCCESS);
}
Err(_) => {
tracing::warn!(
"vCPU {vcpu_id}: PSCI CPU_ON target={target_cpu} \
channel closed"
);
let _ = vcpu.set_reg(X0, PSCI_ALREADY_ON);
}
}
} else {
tracing::debug!("vCPU {vcpu_id}: PSCI CPU_ON target={target_cpu} already on");
let _ = vcpu.set_reg(X0, PSCI_ALREADY_ON);
}
} else {
tracing::debug!("vCPU {vcpu_id}: PSCI CPU_ON ignored (single-vCPU VM)");
let _ = vcpu.set_reg(X0, u64::MAX); }
}
_ => {
tracing::debug!("vCPU {vcpu_id}: unhandled PSCI func {func_id:#x}");
let _ = vcpu.set_reg(X0, u64::MAX);
}
}
}