Skip to content

Commit 9a434ce

Browse files
eberman-quicandersson
authored andcommitted
firmware: qcom_scm: Dynamically support SMCCC and legacy conventions
Dynamically support SMCCCC and legacy conventions by detecting which convention to use at runtime. qcom_scm_call_atomic and qcom_scm_call can then be moved in qcom_scm.c and use underlying convention backend as appropriate. Thus, rename qcom_scm-64,-32 to reflect that they are backends for -smc and -legacy, respectively. Also add support for making SCM calls earlier than when SCM driver probes to support use cases such as qcom_scm_set_cold_boot_addr. Support is added by lazily initializing the convention and guarding the query with a spin lock. The limitation of these early SCM calls is that they cannot use DMA, as in the case of >4 arguments for SMC convention and any non-atomic call for legacy convention. Tested-by: Brian Masney <[email protected]> # arm32 Tested-by: Stephan Gerhold <[email protected]> Signed-off-by: Elliot Berman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Bjorn Andersson <[email protected]>
1 parent 57d3b81 commit 9a434ce

File tree

6 files changed

+176
-135
lines changed

6 files changed

+176
-135
lines changed

drivers/firmware/Kconfig

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,6 @@ config QCOM_SCM
239239
depends on ARM || ARM64
240240
select RESET_CONTROLLER
241241

242-
config QCOM_SCM_32
243-
def_bool y
244-
depends on QCOM_SCM && ARM
245-
246-
config QCOM_SCM_64
247-
def_bool y
248-
depends on QCOM_SCM && ARM64
249-
250242
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
251243
bool "Qualcomm download mode enabled by default"
252244
depends on QCOM_SCM

drivers/firmware/Makefile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
1717
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
1818
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
1919
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
20-
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o
21-
obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o
22-
obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o
20+
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
2321
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
2422
obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
2523
obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o

drivers/firmware/qcom_scm-32.c renamed to drivers/firmware/qcom_scm-legacy.c

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ struct arm_smccc_args {
2626
unsigned long args[8];
2727
};
2828

29-
#define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff))
3029

3130
/**
3231
* struct scm_legacy_command - one SCM command buffer
@@ -129,8 +128,8 @@ static void __scm_legacy_do(const struct arm_smccc_args *smc,
129128
* and response buffers is taken care of by qcom_scm_call; however, callers are
130129
* responsible for any other cached buffers passed over to the secure world.
131130
*/
132-
int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
133-
struct qcom_scm_res *res)
131+
int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
132+
struct qcom_scm_res *res)
134133
{
135134
u8 arglen = desc->arginfo & 0xf;
136135
int ret = 0, context_id;
@@ -218,9 +217,9 @@ int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
218217
* This shall only be used with commands that are guaranteed to be
219218
* uninterruptable, atomic and SMP safe.
220219
*/
221-
int qcom_scm_call_atomic(struct device *unused,
222-
const struct qcom_scm_desc *desc,
223-
struct qcom_scm_res *res)
220+
int scm_legacy_call_atomic(struct device *unused,
221+
const struct qcom_scm_desc *desc,
222+
struct qcom_scm_res *res)
224223
{
225224
int context_id;
226225
struct arm_smccc_res smc_res;
@@ -241,23 +240,3 @@ int qcom_scm_call_atomic(struct device *unused,
241240

242241
return smc_res.a0;
243242
}
244-
245-
int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
246-
{
247-
int ret;
248-
struct qcom_scm_desc desc = {
249-
.svc = QCOM_SCM_SVC_INFO,
250-
.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
251-
.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id),
252-
.arginfo = QCOM_SCM_ARGS(1),
253-
};
254-
struct qcom_scm_res res;
255-
256-
ret = qcom_scm_call(dev, &desc, &res);
257-
258-
return ret ? : res.result[0];
259-
}
260-
261-
void __qcom_scm_init(void)
262-
{
263-
}

drivers/firmware/qcom_scm-64.c renamed to drivers/firmware/qcom_scm-smc.c

Lines changed: 5 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414

1515
#include "qcom_scm.h"
1616

17-
#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
18-
1917
/**
2018
* struct arm_smccc_args
2119
* @args: The array of values used in registers in smc instruction
@@ -24,7 +22,6 @@ struct arm_smccc_args {
2422
unsigned long args[8];
2523
};
2624

27-
static u64 qcom_smccc_convention = -1;
2825
static DEFINE_MUTEX(qcom_scm_lock);
2926

3027
#define QCOM_SCM_EBUSY_WAIT_MS 30
@@ -80,8 +77,8 @@ static void __scm_smc_do(const struct arm_smccc_args *smc,
8077
} while (res->a0 == QCOM_SCM_V2_EBUSY);
8178
}
8279

83-
static int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
84-
struct qcom_scm_res *res, bool atomic)
80+
int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
81+
struct qcom_scm_res *res, bool atomic)
8582
{
8683
int arglen = desc->arginfo & 0xf;
8784
int i;
@@ -90,6 +87,9 @@ static int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
9087
size_t alloc_len;
9188
gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
9289
u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
90+
u32 qcom_smccc_convention =
91+
(qcom_scm_convention == SMC_CONVENTION_ARM_32) ?
92+
ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
9393
struct arm_smccc_res smc_res;
9494
struct arm_smccc_args smc = {0};
9595

@@ -149,88 +149,3 @@ static int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
149149

150150
return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0;
151151
}
152-
153-
/**
154-
* qcom_scm_call() - Invoke a syscall in the secure world
155-
* @dev: device
156-
* @svc_id: service identifier
157-
* @cmd_id: command identifier
158-
* @desc: Descriptor structure containing arguments and return values
159-
*
160-
* Sends a command to the SCM and waits for the command to finish processing.
161-
* This should *only* be called in pre-emptible context.
162-
*/
163-
int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
164-
struct qcom_scm_res *res)
165-
{
166-
might_sleep();
167-
return __scm_smc_call(dev, desc, res, false);
168-
}
169-
170-
/**
171-
* qcom_scm_call_atomic() - atomic variation of qcom_scm_call()
172-
* @dev: device
173-
* @svc_id: service identifier
174-
* @cmd_id: command identifier
175-
* @desc: Descriptor structure containing arguments and return values
176-
* @res: Structure containing results from SMC/HVC call
177-
*
178-
* Sends a command to the SCM and waits for the command to finish processing.
179-
* This can be called in atomic context.
180-
*/
181-
int qcom_scm_call_atomic(struct device *dev, const struct qcom_scm_desc *desc,
182-
struct qcom_scm_res *res)
183-
{
184-
return __scm_smc_call(dev, desc, res, true);
185-
}
186-
187-
int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
188-
{
189-
int ret;
190-
struct qcom_scm_desc desc = {
191-
.svc = QCOM_SCM_SVC_INFO,
192-
.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
193-
.owner = ARM_SMCCC_OWNER_SIP,
194-
};
195-
struct qcom_scm_res res;
196-
197-
desc.arginfo = QCOM_SCM_ARGS(1);
198-
desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) |
199-
(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
200-
201-
ret = qcom_scm_call(dev, &desc, &res);
202-
203-
return ret ? : res.result[0];
204-
}
205-
206-
void __qcom_scm_init(void)
207-
{
208-
struct qcom_scm_desc desc = {
209-
.svc = QCOM_SCM_SVC_INFO,
210-
.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
211-
.args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO,
212-
QCOM_SCM_INFO_IS_CALL_AVAIL) |
213-
(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT),
214-
.arginfo = QCOM_SCM_ARGS(1),
215-
.owner = ARM_SMCCC_OWNER_SIP,
216-
};
217-
struct qcom_scm_res res;
218-
int ret;
219-
220-
qcom_smccc_convention = ARM_SMCCC_SMC_64;
221-
// Device isn't required as there is only one argument - no device
222-
// needed to dma_map_single to secure world
223-
ret = qcom_scm_call_atomic(NULL, &desc, &res);
224-
if (!ret && res.result[0] == 1)
225-
goto out;
226-
227-
qcom_smccc_convention = ARM_SMCCC_SMC_32;
228-
ret = qcom_scm_call_atomic(NULL, &desc, &res);
229-
if (!ret && res.result[0] == 1)
230-
goto out;
231-
232-
qcom_smccc_convention = -1;
233-
BUG();
234-
out:
235-
pr_info("QCOM SCM SMC Convention: %lld\n", qcom_smccc_convention);
236-
}

drivers/firmware/qcom_scm.c

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ static struct qcom_scm_wb_entry qcom_scm_wb[] = {
7272
{ .flag = QCOM_SCM_FLAG_WARMBOOT_CPU3 },
7373
};
7474

75+
static const char *qcom_scm_convention_names[] = {
76+
[SMC_CONVENTION_UNKNOWN] = "unknown",
77+
[SMC_CONVENTION_ARM_32] = "smc arm 32",
78+
[SMC_CONVENTION_ARM_64] = "smc arm 64",
79+
[SMC_CONVENTION_LEGACY] = "smc legacy",
80+
};
81+
7582
static struct qcom_scm *__scm;
7683

7784
static int qcom_scm_clk_enable(void)
@@ -107,6 +114,143 @@ static void qcom_scm_clk_disable(void)
107114
clk_disable_unprepare(__scm->bus_clk);
108115
}
109116

117+
static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
118+
u32 cmd_id);
119+
120+
enum qcom_scm_convention qcom_scm_convention;
121+
static bool has_queried __read_mostly;
122+
static DEFINE_SPINLOCK(query_lock);
123+
124+
static void __query_convention(void)
125+
{
126+
unsigned long flags;
127+
struct qcom_scm_desc desc = {
128+
.svc = QCOM_SCM_SVC_INFO,
129+
.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
130+
.args[0] = SCM_SMC_FNID(QCOM_SCM_SVC_INFO,
131+
QCOM_SCM_INFO_IS_CALL_AVAIL) |
132+
(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT),
133+
.arginfo = QCOM_SCM_ARGS(1),
134+
.owner = ARM_SMCCC_OWNER_SIP,
135+
};
136+
struct qcom_scm_res res;
137+
int ret;
138+
139+
spin_lock_irqsave(&query_lock, flags);
140+
if (has_queried)
141+
goto out;
142+
143+
qcom_scm_convention = SMC_CONVENTION_ARM_64;
144+
// Device isn't required as there is only one argument - no device
145+
// needed to dma_map_single to secure world
146+
ret = scm_smc_call(NULL, &desc, &res, true);
147+
if (!ret && res.result[0] == 1)
148+
goto out;
149+
150+
qcom_scm_convention = SMC_CONVENTION_ARM_32;
151+
ret = scm_smc_call(NULL, &desc, &res, true);
152+
if (!ret && res.result[0] == 1)
153+
goto out;
154+
155+
qcom_scm_convention = SMC_CONVENTION_LEGACY;
156+
out:
157+
has_queried = true;
158+
spin_unlock_irqrestore(&query_lock, flags);
159+
pr_info("qcom_scm: convention: %s\n",
160+
qcom_scm_convention_names[qcom_scm_convention]);
161+
}
162+
163+
static inline enum qcom_scm_convention __get_convention(void)
164+
{
165+
if (unlikely(!has_queried))
166+
__query_convention();
167+
return qcom_scm_convention;
168+
}
169+
170+
/**
171+
* qcom_scm_call() - Invoke a syscall in the secure world
172+
* @dev: device
173+
* @svc_id: service identifier
174+
* @cmd_id: command identifier
175+
* @desc: Descriptor structure containing arguments and return values
176+
*
177+
* Sends a command to the SCM and waits for the command to finish processing.
178+
* This should *only* be called in pre-emptible context.
179+
*/
180+
static int qcom_scm_call(struct device *dev, const struct qcom_scm_desc *desc,
181+
struct qcom_scm_res *res)
182+
{
183+
might_sleep();
184+
switch (__get_convention()) {
185+
case SMC_CONVENTION_ARM_32:
186+
case SMC_CONVENTION_ARM_64:
187+
return scm_smc_call(dev, desc, res, false);
188+
case SMC_CONVENTION_LEGACY:
189+
return scm_legacy_call(dev, desc, res);
190+
default:
191+
pr_err("Unknown current SCM calling convention.\n");
192+
return -EINVAL;
193+
}
194+
}
195+
196+
/**
197+
* qcom_scm_call_atomic() - atomic variation of qcom_scm_call()
198+
* @dev: device
199+
* @svc_id: service identifier
200+
* @cmd_id: command identifier
201+
* @desc: Descriptor structure containing arguments and return values
202+
* @res: Structure containing results from SMC/HVC call
203+
*
204+
* Sends a command to the SCM and waits for the command to finish processing.
205+
* This can be called in atomic context.
206+
*/
207+
static int qcom_scm_call_atomic(struct device *dev,
208+
const struct qcom_scm_desc *desc,
209+
struct qcom_scm_res *res)
210+
{
211+
switch (__get_convention()) {
212+
case SMC_CONVENTION_ARM_32:
213+
case SMC_CONVENTION_ARM_64:
214+
return scm_smc_call(dev, desc, res, true);
215+
case SMC_CONVENTION_LEGACY:
216+
return scm_legacy_call_atomic(dev, desc, res);
217+
default:
218+
pr_err("Unknown current SCM calling convention.\n");
219+
return -EINVAL;
220+
}
221+
}
222+
223+
static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
224+
u32 cmd_id)
225+
{
226+
int ret;
227+
struct qcom_scm_desc desc = {
228+
.svc = QCOM_SCM_SVC_INFO,
229+
.cmd = QCOM_SCM_INFO_IS_CALL_AVAIL,
230+
.owner = ARM_SMCCC_OWNER_SIP,
231+
};
232+
struct qcom_scm_res res;
233+
234+
desc.arginfo = QCOM_SCM_ARGS(1);
235+
switch (__get_convention()) {
236+
case SMC_CONVENTION_ARM_32:
237+
case SMC_CONVENTION_ARM_64:
238+
desc.args[0] = SCM_SMC_FNID(svc_id, cmd_id) |
239+
(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
240+
break;
241+
case SMC_CONVENTION_LEGACY:
242+
desc.args[0] = SCM_LEGACY_FNID(svc_id, cmd_id);
243+
break;
244+
default:
245+
pr_err("Unknown SMC convention being used\n");
246+
return -EINVAL;
247+
}
248+
249+
ret = qcom_scm_call(dev, &desc, &res);
250+
251+
return ret ? : res.result[0];
252+
}
253+
110254
/**
111255
* qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
112256
* @entry: Entry point function for the cpus
@@ -971,7 +1115,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
9711115
__scm = scm;
9721116
__scm->dev = &pdev->dev;
9731117

974-
__qcom_scm_init();
1118+
__query_convention();
9751119

9761120
/*
9771121
* If requested enable "download mode", from this point on warmboot

0 commit comments

Comments
 (0)