/* * While a 64-bit OS can make calls with SMC32 calling conventions, for some * calls it is necessary to use SMC64 to pass or return 64-bit values. * For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate * (native-width) function ID.
*/ #ifdef CONFIG_64BIT #define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name #else #define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name #endif
/* * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF * calls to its resident CPU, so we must avoid issuing those. We never migrate * a Trusted OS even if it claims to be capable of migration -- doing so will * require cooperation with a Trusted OS driver.
*/ staticint resident_cpu = -1; struct psci_operations psci_ops; staticenum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE;
bool psci_tos_resident_on(int cpu)
{ return cpu == resident_cpu;
}
static __always_inline int psci_to_linux_errno(int errno)
{ switch (errno) { case PSCI_RET_SUCCESS: return 0; case PSCI_RET_NOT_SUPPORTED: return -EOPNOTSUPP; case PSCI_RET_INVALID_PARAMS: case PSCI_RET_INVALID_ADDRESS: return -EINVAL; case PSCI_RET_DENIED: return -EPERM;
}
#ifdef CONFIG_HIBERNATION staticint psci_sys_hibernate(struct sys_off_data *data)
{ /* * If no hibernate type is specified SYSTEM_OFF2 defaults to selecting * HIBERNATE_OFF. * * There are hypervisors in the wild that do not align with the spec and * reject calls that explicitly provide a hibernate type. For * compatibility with these nonstandard implementations, pass 0 as the * type.
*/ if (system_entering_hibernation())
invoke_psci_fn(PSCI_FN_NATIVE(1_3, SYSTEM_OFF2), 0, 0, 0); return NOTIFY_DONE;
}
staticint __init psci_hibernate_init(void)
{ if (psci_system_off2_hibernate_supported) { /* Higher priority than EFI shutdown, but only for hibernate */
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_FIRMWARE + 2,
psci_sys_hibernate, NULL);
} return 0;
}
subsys_initcall(psci_hibernate_init); #endif
staticvoid __init psci_init_system_reset2(void)
{ int ret;
ret = psci_features(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2));
if (ret != PSCI_RET_NOT_SUPPORTED)
psci_system_reset2_supported = true;
}
staticvoid __init psci_init_system_off2(void)
{ int ret;
ret = psci_features(PSCI_FN_NATIVE(1_3, SYSTEM_OFF2)); if (ret < 0) return;
if (ret & PSCI_1_3_OFF_TYPE_HIBERNATE_OFF)
psci_system_off2_hibernate_supported = true;
}
staticvoid __init psci_init_system_suspend(void)
{ int ret;
if (!IS_ENABLED(CONFIG_SUSPEND)) return;
ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND));
if (ret != PSCI_RET_NOT_SUPPORTED)
suspend_set_ops(&psci_suspend_ops);
}
staticvoid __init psci_init_cpu_suspend(void)
{ int feature = psci_features(PSCI_FN_NATIVE(0_2, CPU_SUSPEND));
if (feature != PSCI_RET_NOT_SUPPORTED)
psci_cpu_suspend_feature = feature;
}
/* * Detect the presence of a resident Trusted OS which may cause CPU_OFF to * return DENIED (which would be fatal).
*/ staticvoid __init psci_init_migrate(void)
{ unsignedlong cpuid; int type, cpu = -1;
type = psci_ops.migrate_info_type();
if (type == PSCI_0_2_TOS_MP) {
pr_info("Trusted OS migration not required\n"); return;
}
if (type == PSCI_RET_NOT_SUPPORTED) {
pr_info("MIGRATE_INFO_TYPE not supported.\n"); return;
}
if (type != PSCI_0_2_TOS_UP_MIGRATE &&
type != PSCI_0_2_TOS_UP_NO_MIGRATE) {
pr_err("MIGRATE_INFO_TYPE returned unknown type (%d)\n", type); return;
}
cpuid = psci_migrate_info_up_cpu(); if (cpuid & ~MPIDR_HWID_BITMASK) {
pr_warn("MIGRATE_INFO_UP_CPU reported invalid physical ID (0x%lx)\n",
cpuid); return;
}
cpu = get_logical_index(cpuid);
resident_cpu = cpu >= 0 ? cpu : -1;
pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid);
}
staticvoid __init psci_init_smccc(void)
{
u32 ver = ARM_SMCCC_VERSION_1_0; int feature;
if (feature != PSCI_RET_NOT_SUPPORTED) {
u32 ret;
ret = invoke_psci_fn(ARM_SMCCC_VERSION_FUNC_ID, 0, 0, 0); if (ret >= ARM_SMCCC_VERSION_1_1) {
arm_smccc_version_init(ret, psci_conduit);
ver = ret;
}
}
/* * Conveniently, the SMCCC and PSCI versions are encoded the * same way. No, this isn't accidental.
*/
pr_info("SMC Calling Convention v%d.%d\n",
PSCI_VERSION_MAJOR(ver), PSCI_VERSION_MINOR(ver));
}
staticvoid __init psci_0_2_set_functions(void)
{
pr_info("Using standard PSCI v0.2 function IDs\n");
/* * PSCI init function for PSCI versions >=0.2 * * Probe based on PSCI PSCI_VERSION function
*/ staticint __init psci_0_2_init(conststruct device_node *np)
{ int err;
err = get_set_conduit_method(np); if (err) return err;
/* * Starting with v0.2, the PSCI specification introduced a call * (PSCI_VERSION) that allows probing the firmware version, so * that PSCI function IDs and version specific initialization * can be carried out according to the specific version reported * by firmware
*/ return psci_probe();
}
/* * PSCI < v0.2 get PSCI Function IDs via DT.
*/ staticint __init psci_0_1_init(conststruct device_node *np)
{
u32 id; int err;
err = get_set_conduit_method(np); if (err) return err;
pr_info("Using PSCI v0.1 Function IDs from DT\n");
if (!np || !of_device_is_available(np)) {
of_node_put(np); return -ENODEV;
}
init_fn = (psci_initcall_t)matched_np->data;
ret = init_fn(np);
of_node_put(np); return ret;
}
#ifdef CONFIG_ACPI /* * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's * explicitly clarified in SBBR
*/ int __init psci_acpi_init(void)
{ if (!acpi_psci_present()) {
pr_info("is not implemented in ACPI.\n"); return -EOPNOTSUPP;
}
pr_info("probing for conduit method from ACPI.\n");
if (acpi_psci_use_hvc())
set_conduit(SMCCC_CONDUIT_HVC); else
set_conduit(SMCCC_CONDUIT_SMC);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.