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:
venkata sateesh 2023-03-15 13:49:04 +05:30 committed by Abdul Salam
parent fdd94c30ef
commit ffb768e52c
9 changed files with 297 additions and 27 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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

View 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");

View File

@ -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,

View 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_ */