Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/arm64/ffh.c
26282 views
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
*/
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);
20
};
21
22
int 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
54
static 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
65
int 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
109