File Index
Files and directories
Sign in
FILE

ffh.c

File preview with syntax highlighting.

linux-6.19.14/drivers/acpi/arm64/ffh.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2#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 as
  8 * specified in https://developer.arm.com/docs/den0048/latest
  9 */
 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}
~/ Read-only file preview.