1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <linux/acpi.h>
3 #include <linux/arm-smccc.h>
4 #include <linux/slab.h>
7 * Implements ARM64 specific callbacks to support ACPI FFH Operation Region as
8 * specified in https://developer.arm.com/docs/den0048/latest
10 struct 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
);
22 int acpi_ffh_address_space_arch_setup(void *handler_ctxt
, void **region_ctxt
)
24 enum arm_smccc_conduit conduit
;
25 struct acpi_ffh_data
*ffh_ctxt
;
27 if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2
)
30 conduit
= arm_smccc_1_1_get_conduit();
31 if (conduit
== SMCCC_CONDUIT_NONE
) {
32 pr_err("%s: invalid SMCCC conduit\n", __func__
);
36 ffh_ctxt
= kzalloc(sizeof(*ffh_ctxt
), GFP_KERNEL
);
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
;
44 ffh_ctxt
->invoke_ffh_fn
= __arm_smccc_hvc
;
45 ffh_ctxt
->invoke_ffh64_fn
= arm_smccc_1_2_hvc
;
48 memcpy(ffh_ctxt
, handler_ctxt
, sizeof(ffh_ctxt
->info
));
50 *region_ctxt
= ffh_ctxt
;
54 static bool acpi_ffh_smccc_owner_allowed(u32 fid
)
56 int owner
= ARM_SMCCC_OWNER_NUM(fid
);
58 if (owner
== ARM_SMCCC_OWNER_STANDARD
||
59 owner
== ARM_SMCCC_OWNER_SIP
|| owner
== ARM_SMCCC_OWNER_OEM
)
65 int acpi_ffh_address_space_arch_handler(acpi_integer
*value
, void *region_context
)
68 struct acpi_ffh_data
*ffh_ctxt
= region_context
;
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
;
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) {
80 int idx
, len
= ffh_ctxt
->info
.length
>> 2;
82 for (idx
= 0; idx
< len
; idx
++)
83 a
[idx
] = *(ptr
+ idx
);
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
));
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
;
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
)) {
99 ffh_ctxt
->invoke_ffh64_fn(r
, r
);
100 memcpy(value
, r
, ffh_ctxt
->info
.length
);