Commit f0c84315 authored by Jens Wiklander's avatar Jens Wiklander Committed by Ulf Hansson
Browse files

optee: probe RPMB device using RPMB subsystem



Adds support in the OP-TEE drivers (both SMC and FF-A ABIs) to probe and
use an RPMB device via the RPMB subsystem instead of passing the RPMB
frames via tee-supplicant in user space. A fallback mechanism is kept to
route RPMB frames via tee-supplicant if the RPMB subsystem isn't
available.

The OP-TEE RPC ABI is extended to support iterating over all RPMB
devices until one is found with the expected RPMB key already
programmed.

Signed-off-by: default avatarJens Wiklander <jens.wiklander@linaro.org>
Tested-by: default avatarManuel Traut <manut@mecka.net>
Reviewed-by: default avatarSumit Garg <sumit.garg@linaro.org>
Link: https://lore.kernel.org/r/20240814153558.708365-5-jens.wiklander@linaro.org


Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent c30b855e
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
What:		/sys/class/tee/tee{,priv}X/rpmb_routing_model
Date:		May 2024
KernelVersion:	6.10
Contact:        op-tee@lists.trustedfirmware.org
Description:
		RPMB frames can be routed to the RPMB device via the
		user-space daemon tee-supplicant or the RPMB subsystem
		in the kernel. The value "user" means that the driver
		will route the RPMB frames via user space. Conversely,
		"kernel" means that the frames are routed via the RPMB
		subsystem without assistance from tee-supplicant. It
		should be assumed that RPMB frames are routed via user
		space if the variable is absent. The primary purpose
		of this variable is to let systemd know whether
		tee-supplicant is needed in the early boot with initramfs.
+1 −0
Original line number Diff line number Diff line
@@ -22458,6 +22458,7 @@ M: Jens Wiklander <jens.wiklander@linaro.org>
R:	Sumit Garg <sumit.garg@linaro.org>
L:	op-tee@lists.trustedfirmware.org
S:	Maintained
F:	Documentation/ABI/testing/sysfs-class-tee
F:	Documentation/driver-api/tee.rst
F:	Documentation/tee/
F:	Documentation/userspace-api/tee.rst
+95 −1
Original line number Diff line number Diff line
@@ -10,17 +10,85 @@
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/rpmb.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/tee_core.h>
#include <linux/types.h>
#include "optee_private.h"

struct blocking_notifier_head optee_rpmb_intf_added =
	BLOCKING_NOTIFIER_INIT(optee_rpmb_intf_added);

static int rpmb_add_dev(struct device *dev)
{
	blocking_notifier_call_chain(&optee_rpmb_intf_added, 0,
				     to_rpmb_dev(dev));

	return 0;
}

static struct class_interface rpmb_class_intf = {
	.add_dev = rpmb_add_dev,
};

void optee_bus_scan_rpmb(struct work_struct *work)
{
	struct optee *optee = container_of(work, struct optee,
					   rpmb_scan_bus_work);
	int ret;

	if (!optee->rpmb_scan_bus_done) {
		ret = optee_enumerate_devices(PTA_CMD_GET_DEVICES_RPMB);
		optee->rpmb_scan_bus_done = !ret;
		if (ret && ret != -ENODEV)
			pr_info("Scanning for RPMB device: ret %d\n", ret);
	}
}

int optee_rpmb_intf_rdev(struct notifier_block *intf, unsigned long action,
			 void *data)
{
	struct optee *optee = container_of(intf, struct optee, rpmb_intf);

	schedule_work(&optee->rpmb_scan_bus_work);

	return 0;
}

static void optee_bus_scan(struct work_struct *work)
{
	WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
}

static ssize_t rpmb_routing_model_show(struct device *dev,
				       struct device_attribute *attr, char *buf)
{
	struct optee *optee = dev_get_drvdata(dev);
	const char *s;

	if (optee->in_kernel_rpmb_routing)
		s = "kernel";
	else
		s = "user";

	return scnprintf(buf, PAGE_SIZE, "%s\n", s);
}
static DEVICE_ATTR_RO(rpmb_routing_model);

static struct attribute *optee_dev_attrs[] = {
	&dev_attr_rpmb_routing_model.attr,
	NULL
};

ATTRIBUTE_GROUPS(optee_dev);

void optee_set_dev_group(struct optee *optee)
{
	tee_device_set_dev_groups(optee->teedev, optee_dev_groups);
	tee_device_set_dev_groups(optee->supp_teedev, optee_dev_groups);
}

int optee_open(struct tee_context *ctx, bool cap_memref_null)
{
	struct optee_context_data *ctxdata;
@@ -97,6 +165,9 @@ void optee_release_supp(struct tee_context *ctx)

void optee_remove_common(struct optee *optee)
{
	blocking_notifier_chain_unregister(&optee_rpmb_intf_added,
					   &optee->rpmb_intf);
	cancel_work_sync(&optee->rpmb_scan_bus_work);
	/* Unregister OP-TEE specific client devices on TEE bus */
	optee_unregister_devices();

@@ -113,13 +184,18 @@ void optee_remove_common(struct optee *optee)
	tee_shm_pool_free(optee->pool);
	optee_supp_uninit(&optee->supp);
	mutex_destroy(&optee->call_queue.mutex);
	rpmb_dev_put(optee->rpmb_dev);
	mutex_destroy(&optee->rpmb_dev_mutex);
}

static int smc_abi_rc;
static int ffa_abi_rc;
static bool intf_is_regged;

static int __init optee_core_init(void)
{
	int rc;

	/*
	 * The kernel may have crashed at the same time that all available
	 * secure world threads were suspended and we cannot reschedule the
@@ -130,18 +206,36 @@ static int __init optee_core_init(void)
	if (is_kdump_kernel())
		return -ENODEV;

	if (IS_REACHABLE(CONFIG_RPMB)) {
		rc = rpmb_interface_register(&rpmb_class_intf);
		if (rc)
			return rc;
		intf_is_regged = true;
	}

	smc_abi_rc = optee_smc_abi_register();
	ffa_abi_rc = optee_ffa_abi_register();

	/* If both failed there's no point with this module */
	if (smc_abi_rc && ffa_abi_rc)
	if (smc_abi_rc && ffa_abi_rc) {
		if (IS_REACHABLE(CONFIG_RPMB)) {
			rpmb_interface_unregister(&rpmb_class_intf);
			intf_is_regged = false;
		}
		return smc_abi_rc;
	}

	return 0;
}
module_init(optee_core_init);

static void __exit optee_core_exit(void)
{
	if (IS_REACHABLE(CONFIG_RPMB) && intf_is_regged) {
		rpmb_interface_unregister(&rpmb_class_intf);
		intf_is_regged = false;
	}

	if (!smc_abi_rc)
		optee_smc_abi_unregister();
	if (!ffa_abi_rc)
+7 −0
Original line number Diff line number Diff line
@@ -43,6 +43,13 @@ static int get_devices(struct tee_context *ctx, u32 session,
	ret = tee_client_invoke_func(ctx, &inv_arg, param);
	if ((ret < 0) || ((inv_arg.ret != TEEC_SUCCESS) &&
			  (inv_arg.ret != TEEC_ERROR_SHORT_BUFFER))) {
		/*
		 * TEE_ERROR_STORAGE_NOT_AVAILABLE is returned when getting
		 * the list of device TAs that depends on RPMB but a usable
		 * RPMB device isn't found.
		 */
		if (inv_arg.ret == TEE_ERROR_STORAGE_NOT_AVAILABLE)
			return -ENODEV;
		pr_err("PTA_CMD_GET_DEVICES invoke function err: %x\n",
		       inv_arg.ret);
		return -EINVAL;
+14 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

#include <linux/arm_ffa.h>
#include <linux/errno.h>
#include <linux/rpmb.h>
#include <linux/scatterlist.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -909,6 +910,10 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
	optee->ffa.bottom_half_value = U32_MAX;
	optee->rpc_param_count = rpc_param_count;

	if (IS_REACHABLE(CONFIG_RPMB) &&
	    (sec_caps & OPTEE_FFA_SEC_CAP_RPMB_PROBE))
		optee->in_kernel_rpmb_routing = true;

	teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool,
				  optee);
	if (IS_ERR(teedev)) {
@@ -925,6 +930,8 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
	}
	optee->supp_teedev = teedev;

	optee_set_dev_group(optee);

	rc = tee_device_register(optee->teedev);
	if (rc)
		goto err_unreg_supp_teedev;
@@ -940,6 +947,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
	optee_cq_init(&optee->call_queue, 0);
	optee_supp_init(&optee->supp);
	optee_shm_arg_cache_init(optee, arg_cache_flags);
	mutex_init(&optee->rpmb_dev_mutex);
	ffa_dev_set_drvdata(ffa_dev, optee);
	ctx = teedev_open(optee->teedev);
	if (IS_ERR(ctx)) {
@@ -961,6 +969,10 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
	if (rc)
		goto err_unregister_devices;

	INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb);
	optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev;
	blocking_notifier_chain_register(&optee_rpmb_intf_added,
					 &optee->rpmb_intf);
	pr_info("initialized driver\n");
	return 0;

@@ -974,6 +986,8 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
	teedev_close_context(ctx);
err_rhashtable_free:
	rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL);
	rpmb_dev_put(optee->rpmb_dev);
	mutex_destroy(&optee->rpmb_dev_mutex);
	optee_supp_uninit(&optee->supp);
	mutex_destroy(&optee->call_queue.mutex);
	mutex_destroy(&optee->ffa.mutex);
Loading