108 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2#include <linux/acpi.h>3#include <linux/arm-smccc.h>4#include <linux/slab.h>5 6/*7 * Implements ARM64 specific callbacks to support ACPI FFH Operation Region as8 * specified in https://developer.arm.com/docs/den0048/latest9 */10struct acpi_ffh_data {11 struct acpi_ffh_info info;12 void (*invoke_ffh_fn)(unsigned long a0, unsigned long a1,13 unsigned long a2, unsigned long a3,14 unsigned long a4, unsigned long a5,15 unsigned long a6, unsigned long a7,16 struct arm_smccc_res *args,17 struct arm_smccc_quirk *res);18 void (*invoke_ffh64_fn)(const struct arm_smccc_1_2_regs *args,19 struct arm_smccc_1_2_regs *res);20};21 22int acpi_ffh_address_space_arch_setup(void *handler_ctxt, void **region_ctxt)23{24 enum arm_smccc_conduit conduit;25 struct acpi_ffh_data *ffh_ctxt;26 27 if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)28 return -EOPNOTSUPP;29 30 conduit = arm_smccc_1_1_get_conduit();31 if (conduit == SMCCC_CONDUIT_NONE) {32 pr_err("%s: invalid SMCCC conduit\n", __func__);33 return -EOPNOTSUPP;34 }35 36 ffh_ctxt = kzalloc(sizeof(*ffh_ctxt), GFP_KERNEL);37 if (!ffh_ctxt)38 return -ENOMEM;39 40 if (conduit == SMCCC_CONDUIT_SMC) {41 ffh_ctxt->invoke_ffh_fn = __arm_smccc_smc;42 ffh_ctxt->invoke_ffh64_fn = arm_smccc_1_2_smc;43 } else {44 ffh_ctxt->invoke_ffh_fn = __arm_smccc_hvc;45 ffh_ctxt->invoke_ffh64_fn = arm_smccc_1_2_hvc;46 }47 48 memcpy(ffh_ctxt, handler_ctxt, sizeof(ffh_ctxt->info));49 50 *region_ctxt = ffh_ctxt;51 return AE_OK;52}53 54static bool acpi_ffh_smccc_owner_allowed(u32 fid)55{56 int owner = ARM_SMCCC_OWNER_NUM(fid);57 58 if (owner == ARM_SMCCC_OWNER_STANDARD ||59 owner == ARM_SMCCC_OWNER_SIP || owner == ARM_SMCCC_OWNER_OEM)60 return true;61 62 return false;63}64 65int acpi_ffh_address_space_arch_handler(acpi_integer *value, void *region_context)66{67 int ret = 0;68 struct acpi_ffh_data *ffh_ctxt = region_context;69 70 if (ffh_ctxt->info.offset == 0) {71 /* SMC/HVC 32bit call */72 struct arm_smccc_res res;73 u32 a[8] = { 0 }, *ptr = (u32 *)value;74 75 if (!ARM_SMCCC_IS_FAST_CALL(*ptr) || ARM_SMCCC_IS_64(*ptr) ||76 !acpi_ffh_smccc_owner_allowed(*ptr) ||77 ffh_ctxt->info.length > 32) {78 ret = AE_ERROR;79 } else {80 int idx, len = ffh_ctxt->info.length >> 2;81 82 for (idx = 0; idx < len; idx++)83 a[idx] = *(ptr + idx);84 85 ffh_ctxt->invoke_ffh_fn(a[0], a[1], a[2], a[3], a[4],86 a[5], a[6], a[7], &res, NULL);87 memcpy(value, &res, sizeof(res));88 }89 90 } else if (ffh_ctxt->info.offset == 1) {91 /* SMC/HVC 64bit call */92 struct arm_smccc_1_2_regs *r = (struct arm_smccc_1_2_regs *)value;93 94 if (!ARM_SMCCC_IS_FAST_CALL(r->a0) || !ARM_SMCCC_IS_64(r->a0) ||95 !acpi_ffh_smccc_owner_allowed(r->a0) ||96 ffh_ctxt->info.length > sizeof(*r)) {97 ret = AE_ERROR;98 } else {99 ffh_ctxt->invoke_ffh64_fn(r, r);100 memcpy(value, r, ffh_ctxt->info.length);101 }102 } else {103 ret = AE_ERROR;104 }105 106 return ret;107}108