use core::{arch::global_asm, mem::offset_of};
use bsp_define::smp::{PsciCompatible, PsciEnableMethod};
use crate::{
bsp::__smp_define,
println,
processor::{PROCESSOR_CPU_ON, PROCESSOR_SYS_POWEROFF, PROCESSOR_SYS_RESTART},
};
const PSCI_0_2_FN_BASE: usize = 0x84000000;
const fn psci_0_2_fn(n: usize) -> usize {
PSCI_0_2_FN_BASE + n
}
const PSCI_0_2_64BIT: usize = 0x40000000;
const PSCI_0_2_FN64_BASE: usize = PSCI_0_2_FN_BASE + PSCI_0_2_64BIT;
const fn psci_0_2_fn64(n: usize) -> usize {
PSCI_0_2_FN64_BASE + n
}
const PSCI_0_2_FN_PSCI_VERSION: usize = psci_0_2_fn(0);
const PSCI_0_2_FN64_CPU_ON: usize = psci_0_2_fn64(3);
const PSCI_0_2_FN_SYSTEM_OFF: usize = psci_0_2_fn(8);
const PSCI_0_2_FN_SYSTEM_RESET: usize = psci_0_2_fn(9);
const PSCI_VERSION_MAJOR_SHIFT: usize = 16;
const PSCI_VERSION_MINOR_MASK: usize = (1 << PSCI_VERSION_MAJOR_SHIFT) - 1;
const PSCI_VERSION_MAJOR_MASK: usize = !PSCI_VERSION_MINOR_MASK;
const fn psci_version_major(ver: usize) -> usize {
(ver & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT
}
const fn psci_version_minor(ver: usize) -> usize {
ver & PSCI_VERSION_MINOR_MASK
}
global_asm!(r"
ARM_SMCCC_RES_X0_OFFS = {}
ARM_SMCCC_RES_X2_OFFS = {}
.macro SMCCC instr
.cfi_startproc
\instr #0
ldr x4, [sp]
stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
ret
.cfi_endproc
.endm
.global __arm_smccc_smc; .align 2; __arm_smccc_smc:
SMCCC smc
.type __arm_smccc_smc, @function; .size __arm_smccc_smc, .-__arm_smccc_smc
.global __arm_smccc_hvc; .align 2; __arm_smccc_hvc:
SMCCC hvc
.type __arm_smccc_hvc, @function; .size __arm_smccc_hvc, .-__arm_smccc_hvc
", const ARM_SMCCC_RES_X0_OFFS, const ARM_SMCCC_RES_X2_OFFS);
unsafe extern "C" {
fn __arm_smccc_smc(
a0: usize,
a1: usize,
a2: usize,
a3: usize,
a4: usize,
a5: usize,
a6: usize,
a7: usize,
res: &mut ArmSmcccRes,
a9: usize,
);
fn __arm_smccc_hvc(
a0: usize,
a1: usize,
a2: usize,
a3: usize,
a4: usize,
a5: usize,
a6: usize,
a7: usize,
res: &mut ArmSmcccRes,
a9: usize,
);
}
#[repr(C)]
struct ArmSmcccRes {
a0: usize,
a1: usize,
a2: usize,
a3: usize,
}
const ARM_SMCCC_RES_X0_OFFS: usize = offset_of!(ArmSmcccRes, a0);
const ARM_SMCCC_RES_X2_OFFS: usize = offset_of!(ArmSmcccRes, a2);
static mut PSCI_FN_CPU_ON_ID: usize = 0;
fn psci_cpu_on(cpuid: usize, entry_point: usize) -> bool {
let ret = unsafe { INVOKE_PSCI_FN(PSCI_FN_CPU_ON_ID, cpuid, entry_point, 0) };
ret == 0
}
fn psci_sys_reset() {
unsafe {
INVOKE_PSCI_FN(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
}
}
fn psci_sys_poweroff() {
unsafe {
INVOKE_PSCI_FN(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
}
}
fn psci_0_2_set_functions() {
println!("Using standard PSCI v0.2 function IDs");
unsafe {
PSCI_FN_CPU_ON_ID = PSCI_0_2_FN64_CPU_ON;
PROCESSOR_CPU_ON = psci_cpu_on;
PROCESSOR_SYS_RESTART = psci_sys_reset;
PROCESSOR_SYS_POWEROFF = psci_sys_poweroff;
}
}
fn psci_probe() {
let ver = unsafe { INVOKE_PSCI_FN(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0) };
println!("PSCIv{}.{} detected in firmware.", psci_version_major(ver), psci_version_minor(ver));
assert!(!(psci_version_major(ver) == 0 && psci_version_minor(ver) < 2));
psci_0_2_set_functions();
}
fn __invoke_psci_fn_hvc(function_id: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let mut res = ArmSmcccRes { a0: 0, a1: 0, a2: 0, a3: 0 };
unsafe {
__arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &mut res, 0);
}
res.a0
}
fn __invoke_psci_fn_smc(function_id: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let mut res = ArmSmcccRes { a0: 0, a1: 0, a2: 0, a3: 0 };
unsafe {
__arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &mut res, 0);
}
res.a0
}
static mut INVOKE_PSCI_FN: fn(usize, usize, usize, usize) -> usize = __invoke_psci_fn_hvc;
fn set_conduit(method: PsciEnableMethod) {
unsafe {
if matches!(method, PsciEnableMethod::Smc) {
INVOKE_PSCI_FN = __invoke_psci_fn_smc;
}
}
}
fn get_set_conduit_method() {
unsafe {
let method = __smp_define().method();
set_conduit(method);
}
}
fn psci_0_2_init() {
get_set_conduit_method();
psci_probe();
}
fn psci_0_1_init() {
get_set_conduit_method();
println!("Using PSCI v0.1 Function IDs");
unsafe {
PSCI_FN_CPU_ON_ID = __smp_define().cpu_on_id();
PROCESSOR_CPU_ON = psci_cpu_on;
}
}
pub(crate) fn psci_init() {
let compatible = unsafe { __smp_define().compatible() };
match compatible {
PsciCompatible::Psci0_1 => {
psci_0_1_init();
},
_ => {
psci_0_2_init();
},
}
}