brintos

brintos / linux-shallow public Read only

0
0
Text · 2.8 KiB · 877edc6 Raw
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