drivers: firmware: Add qcom scm hab driver
qcom scm hab driver serves scm with required hab channel operations to enable qcpe-tz virtualization. Change-Id: I3ae212ab3a38fc496b8b95ef2c2dbf990ec5a821 Signed-off-by: venkata sateesh <quic_vencher@quicinc.com>
This commit is contained in:
parent
fdd94c30ef
commit
ffb768e52c
@ -226,6 +226,16 @@ config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
|
||||
|
||||
Say Y here to enable "download mode" by default.
|
||||
|
||||
config QCOM_SCM_HAB
|
||||
tristate "To enable QCOM SCM HAB for QCPE-TZ virtualization"
|
||||
depends on QCOM_SCM && MSM_HAB
|
||||
help
|
||||
QCOM SCM HAB serves the scm with HAB channel with Secure Channel
|
||||
Mananger(SCM) support for SoC in virtualized Linux,
|
||||
where SCM backend is QCPE (QCOM Protected environment). The SCM
|
||||
channel will use QCOM HAB interface for front-end to back-end
|
||||
communication.
|
||||
|
||||
config QTEE_SHM_BRIDGE
|
||||
bool "QTI TEE shared memory bridge"
|
||||
depends on QCOM_SCM
|
||||
|
@ -21,6 +21,7 @@ obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
|
||||
obj-$(CONFIG_QCOM_SCM) += qcom-scm.o
|
||||
qcom-scm-objs-$(CONFIG_QTEE_SHM_BRIDGE) += qtee_shmbridge.o
|
||||
qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o $(qcom-scm-objs-y)
|
||||
obj-$(CONFIG_QCOM_SCM_HAB) += qcom_scm_hab.o
|
||||
obj-$(CONFIG_SYSFB) += sysfb.o
|
||||
obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o
|
||||
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2015 Linaro Ltd.
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
@ -17,16 +18,6 @@
|
||||
|
||||
static DEFINE_MUTEX(qcom_scm_lock);
|
||||
|
||||
|
||||
/**
|
||||
* struct arm_smccc_args
|
||||
* @args: The array of values used in registers in smc instruction
|
||||
*/
|
||||
struct arm_smccc_args {
|
||||
unsigned long args[8];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct scm_legacy_command - one SCM command buffer
|
||||
* @len: total available memory for command and response
|
||||
|
@ -13,16 +13,11 @@
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/qtee_shmbridge.h>
|
||||
#include <linux/qcom_scm_hab.h>
|
||||
|
||||
#include "qcom_scm.h"
|
||||
|
||||
/**
|
||||
* struct arm_smccc_args
|
||||
* @args: The array of values used in registers in smc instruction
|
||||
*/
|
||||
struct arm_smccc_args {
|
||||
unsigned long args[8];
|
||||
};
|
||||
static bool hab_calling_convention;
|
||||
|
||||
static DEFINE_MUTEX(qcom_scm_lock);
|
||||
|
||||
@ -40,18 +35,23 @@ static void __scm_smc_do_quirk(const struct arm_smccc_args *smc,
|
||||
{
|
||||
unsigned long a0 = smc->args[0];
|
||||
struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 };
|
||||
bool atomic = ARM_SMCCC_IS_FAST_CALL(smc->args[0]) ? true : false;
|
||||
|
||||
quirk.state.a6 = 0;
|
||||
|
||||
do {
|
||||
arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2],
|
||||
smc->args[3], smc->args[4], smc->args[5],
|
||||
quirk.state.a6, smc->args[7], res, &quirk);
|
||||
if (hab_calling_convention) {
|
||||
scm_call_qcpe(smc, res, atomic);
|
||||
} else {
|
||||
do {
|
||||
arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2],
|
||||
smc->args[3], smc->args[4],
|
||||
smc->args[5], quirk.state.a6,
|
||||
smc->args[7], res, &quirk);
|
||||
if (res->a0 == QCOM_SCM_INTERRUPTED)
|
||||
a0 = res->a0;
|
||||
} while (res->a0 == QCOM_SCM_INTERRUPTED);
|
||||
}
|
||||
|
||||
if (res->a0 == QCOM_SCM_INTERRUPTED)
|
||||
a0 = res->a0;
|
||||
|
||||
} while (res->a0 == QCOM_SCM_INTERRUPTED);
|
||||
}
|
||||
|
||||
#define IS_WAITQ_SLEEP_OR_WAKE(res) \
|
||||
@ -292,3 +292,24 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __qcom_scm_init(void)
|
||||
{
|
||||
int ret;
|
||||
/**
|
||||
* The HAB connection should be opened before first SMC call.
|
||||
* If not, there could be errors that might cause the
|
||||
* system to crash.
|
||||
*/
|
||||
ret = scm_qcpe_hab_open();
|
||||
if (ret != -EOPNOTSUPP) {
|
||||
hab_calling_convention = true;
|
||||
pr_debug("using HAB channel communication ret = %d\n", ret);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void __qcom_scm_qcpe_exit(void)
|
||||
{
|
||||
scm_qcpe_hab_close();
|
||||
}
|
||||
|
@ -2941,6 +2941,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
__qcom_scm_init();
|
||||
__get_convention();
|
||||
|
||||
scm->restart_nb.notifier_call = qcom_scm_do_restart;
|
||||
@ -3027,6 +3028,7 @@ subsys_initcall(qcom_scm_init);
|
||||
#if IS_MODULE(CONFIG_QCOM_SCM)
|
||||
static void __exit qcom_scm_exit(void)
|
||||
{
|
||||
__qcom_scm_qcpe_exit();
|
||||
platform_driver_unregister(&qcom_scm_driver);
|
||||
qtee_shmbridge_driver_exit();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2010-2015,2019,2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
#ifndef __QCOM_SCM_INT_H
|
||||
#define __QCOM_SCM_INT_H
|
||||
@ -230,6 +230,9 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
#define QCOM_SCM_FEAT_LOG_ID 0x0a
|
||||
#define QCOM_SCM_MP_CP_FEAT_ID 0x0c
|
||||
|
||||
extern void __qcom_scm_init(void);
|
||||
extern void __qcom_scm_qcpe_exit(void);
|
||||
|
||||
/* common error codes */
|
||||
#define QCOM_SCM_V2_EBUSY -12
|
||||
#define QCOM_SCM_ENOMEM -5
|
||||
|
202
drivers/firmware/qcom_scm_hab.c
Normal file
202
drivers/firmware/qcom_scm_hab.c
Normal file
@ -0,0 +1,202 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/habmm.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <soc/qcom/qseecom_scm.h>
|
||||
|
||||
#include "qcom_scm.h"
|
||||
|
||||
/**
|
||||
* struct smc_params_s
|
||||
* @fn_id: Function id used for hab channel communication
|
||||
* @arginfo: Argument information used for hab channel communication
|
||||
* @args: The array of values used for hab cannel communication
|
||||
*/
|
||||
struct smc_params_s {
|
||||
uint64_t fn_id;
|
||||
uint64_t arginfo;
|
||||
uint64_t args[MAX_SCM_ARGS];
|
||||
} __packed;
|
||||
|
||||
static u32 handle;
|
||||
static bool opened;
|
||||
|
||||
int scm_qcpe_hab_open(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!opened) {
|
||||
ret = habmm_socket_open(&handle, MM_QCPE_VM1, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("habmm_socket_open failed with ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
opened = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(scm_qcpe_hab_open);
|
||||
|
||||
void scm_qcpe_hab_close(void)
|
||||
{
|
||||
if (opened) {
|
||||
habmm_socket_close(handle);
|
||||
opened = false;
|
||||
handle = 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(scm_qcpe_hab_close);
|
||||
|
||||
/*
|
||||
* Send SMC over HAB, receive the response. Both operations are blocking.
|
||||
* This is meant to be called from non-atomic context.
|
||||
*/
|
||||
static int scm_qcpe_hab_send_receive(struct smc_params_s *smc_params,
|
||||
u32 *size_bytes)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = habmm_socket_send(handle, smc_params, sizeof(*smc_params), 0);
|
||||
if (ret) {
|
||||
pr_err("habmm_socket_send failed, ret= 0x%x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(smc_params, 0x0, sizeof(*smc_params));
|
||||
|
||||
do {
|
||||
*size_bytes = sizeof(*smc_params);
|
||||
ret = habmm_socket_recv(handle, smc_params, size_bytes, 0,
|
||||
HABMM_SOCKET_RECV_FLAGS_UNINTERRUPTIBLE);
|
||||
} while (-EINTR == ret);
|
||||
|
||||
if (ret) {
|
||||
pr_err("habmm_socket_recv failed, ret= 0x%x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send SMC over HAB, receive the response, in non-blocking mode.
|
||||
* This is meant to be called from atomic context.
|
||||
*/
|
||||
static int scm_qcpe_hab_send_receive_atomic(struct smc_params_s *smc_params,
|
||||
u32 *size_bytes)
|
||||
{
|
||||
int ret;
|
||||
unsigned long delay;
|
||||
|
||||
delay = jiffies + msecs_to_jiffies(1000); /* 1 second delay for send */
|
||||
|
||||
do {
|
||||
ret = habmm_socket_send(handle, smc_params, sizeof(*smc_params),
|
||||
HABMM_SOCKET_SEND_FLAGS_NON_BLOCKING);
|
||||
} while ((-EAGAIN == ret) && time_before(jiffies, delay));
|
||||
|
||||
if (ret) {
|
||||
pr_err("HAB send failed, non-blocking, ret= 0x%x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(smc_params, 0x0, sizeof(*smc_params));
|
||||
|
||||
delay = jiffies + msecs_to_jiffies(1000); /* 1 second delay for receive */
|
||||
|
||||
do {
|
||||
*size_bytes = sizeof(*smc_params);
|
||||
ret = habmm_socket_recv(handle, smc_params, size_bytes, 0,
|
||||
HABMM_SOCKET_RECV_FLAGS_NON_BLOCKING);
|
||||
} while ((-EAGAIN == ret) && time_before(jiffies, delay) &&
|
||||
(*size_bytes == 0));
|
||||
|
||||
if (ret) {
|
||||
pr_err("HAB recv failed, non-blocking, ret= 0x%x\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scm_call_qcpe(const struct arm_smccc_args *smc,
|
||||
struct arm_smccc_res *res, const bool atomic)
|
||||
{
|
||||
u32 size_bytes;
|
||||
struct smc_params_s smc_params = {0,};
|
||||
int ret;
|
||||
|
||||
if (!opened) {
|
||||
if (!atomic) {
|
||||
if (scm_qcpe_hab_open()) {
|
||||
pr_err("HAB channel re-open failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else {
|
||||
pr_err("HAB channel is not opened\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
smc_params.fn_id = smc->args[0];
|
||||
smc_params.arginfo = smc->args[1];
|
||||
smc_params.args[0] = smc->args[2];
|
||||
smc_params.args[1] = smc->args[3];
|
||||
smc_params.args[2] = smc->args[4];
|
||||
|
||||
smc_params.args[3] = smc->args[5];
|
||||
smc_params.args[4] = 0;
|
||||
|
||||
if (!atomic) {
|
||||
ret = scm_qcpe_hab_send_receive(&smc_params, &size_bytes);
|
||||
if (ret) {
|
||||
pr_err("send/receive failed, non-atomic, ret= 0x%x\n",
|
||||
ret);
|
||||
goto err_ret;
|
||||
}
|
||||
} else {
|
||||
ret = scm_qcpe_hab_send_receive_atomic(&smc_params,
|
||||
&size_bytes);
|
||||
if (ret) {
|
||||
pr_err("send/receive failed, ret= 0x%x\n", ret);
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (size_bytes != sizeof(smc_params)) {
|
||||
pr_err("habmm_socket_recv expected size: %lu, actual=%u\n",
|
||||
sizeof(smc_params), size_bytes);
|
||||
ret = QCOM_SCM_ERROR;
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
res->a1 = smc_params.args[1];
|
||||
res->a2 = smc_params.args[2];
|
||||
res->a3 = smc_params.args[3];
|
||||
res->a0 = smc_params.args[0];
|
||||
|
||||
goto no_err;
|
||||
|
||||
err_ret:
|
||||
if (!atomic) {
|
||||
/* In case of an error, try to recover the hab connection
|
||||
* for next time. This can only be done if called in
|
||||
* non-atomic context.
|
||||
*/
|
||||
scm_qcpe_hab_close();
|
||||
if (scm_qcpe_hab_open())
|
||||
pr_err("scm_qcpe_hab_open failed\n");
|
||||
}
|
||||
|
||||
no_err:
|
||||
return res->a0;
|
||||
}
|
||||
EXPORT_SYMBOL(scm_call_qcpe);
|
||||
|
||||
MODULE_DESCRIPTION("SCM HAB driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,7 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2010-2015, 2018-2019, 2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2015 Linaro Ltd.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
#ifndef __QCOM_SCM_H
|
||||
#define __QCOM_SCM_H
|
||||
@ -65,6 +65,14 @@ struct qcom_scm_mem_map_info {
|
||||
__le64 mem_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct arm_smccc_args
|
||||
* @args: The array of values used in registers in smc instruction
|
||||
*/
|
||||
struct arm_smccc_args {
|
||||
unsigned long args[8];
|
||||
};
|
||||
|
||||
enum qcom_scm_ice_cipher {
|
||||
QCOM_SCM_ICE_CIPHER_AES_128_XTS = 0,
|
||||
QCOM_SCM_ICE_CIPHER_AES_128_CBC = 1,
|
||||
|
32
include/linux/qcom_scm_hab.h
Normal file
32
include/linux/qcom_scm_hab.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QCOM_SCM_HAB_H_
|
||||
#define __QCOM_SCM_HAB_H_
|
||||
|
||||
#if IS_ENABLED(CONFIG_QCOM_SCM_HAB)
|
||||
int scm_qcpe_hab_open(void);
|
||||
void scm_qcpe_hab_close(void);
|
||||
int scm_call_qcpe(const struct arm_smccc_args *smc,
|
||||
struct arm_smccc_res *res, const bool atomic);
|
||||
|
||||
#else
|
||||
static inline int scm_qcpe_hab_open(void)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void scm_qcpe_hab_close(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int scm_call_qcpe(const struct arm_smccc_args *smc,
|
||||
struct arm_smccc_res *res, const bool atomic)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif /* CONFIG_QCOM_SCM_HAB */
|
||||
|
||||
#endif /* __QCOM_SCM_HAB_H_ */
|
Loading…
Reference in New Issue
Block a user