#include <linux/arm-smccc.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <tee/optee_service.h>
#include "optee_smc.h"
#include "optee_msg.h"
#define PAGELIST_ENTRIES_PER_PAGE \
((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
+/*
+ * PTA_DEVICE_ENUM interface exposed by OP-TEE to discover enumerated services
+ */
+#define PTA_DEVICE_ENUM { 0x7011a688, 0xddde, 0x4053, \
+ { 0xa5, 0xa9, 0x7b, 0x3c, 0x4d, 0xdf, 0x13, 0xb8 } }
+/*
+ * PTA_CMD_GET_DEVICES - List services without supplicant dependencies
+ *
+ * [out] memref[0]: List of the UUIDs of service enumerated by OP-TEE
+ */
+#define PTA_CMD_GET_DEVICES 0x0
+
+/*
+ * PTA_CMD_GET_DEVICES_SUPP - List services depending on tee supplicant
+ *
+ * [out] memref[0]: List of the UUIDs of service enumerated by OP-TEE
+ */
+#define PTA_CMD_GET_DEVICES_SUPP 0x1
+
typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long,
u32 a7;
};
+static struct optee_service *find_service_driver(const struct tee_optee_ta_uuid *uuid)
+{
+ struct optee_service *service;
+ u8 loc_uuid[TEE_UUID_LEN];
+ size_t service_cnt, idx;
+
+ service_cnt = ll_entry_count(struct optee_service, optee_service);
+ service = ll_entry_start(struct optee_service, optee_service);
+
+ for (idx = 0; idx < service_cnt; idx++, service++) {
+ tee_optee_ta_uuid_to_octets(loc_uuid, &service->uuid);
+ if (!memcmp(uuid, loc_uuid, sizeof(uuid)))
+ return service;
+ }
+
+ return NULL;
+}
+
+static int bind_service_list(struct udevice *dev, struct tee_shm *service_list, size_t count)
+{
+ const struct tee_optee_ta_uuid *service_uuid = (const void *)service_list->addr;
+ struct optee_service *service;
+ size_t idx;
+ int ret;
+
+ for (idx = 0; idx < count; idx++) {
+ service = find_service_driver(service_uuid + idx);
+ if (!service)
+ continue;
+
+ ret = device_bind_driver(dev, service->driver_name, service->driver_name, NULL);
+ if (ret) {
+ dev_warn(dev, "%s was not bound: %d, ignored\n", service->driver_name, ret);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static int __enum_services(struct udevice *dev, struct tee_shm *shm, size_t *shm_size, u32 tee_sess)
+{
+ struct tee_invoke_arg arg = { };
+ struct tee_param param = { };
+ int ret = 0;
+
+ arg.func = PTA_CMD_GET_DEVICES;
+ arg.session = tee_sess;
+
+ /* Fill invoke cmd params */
+ param.attr = TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
+ param.u.memref.shm = shm;
+ param.u.memref.size = *shm_size;
+
+ ret = tee_invoke_func(dev, &arg, 1, ¶m);
+ if (ret || (arg.ret && arg.ret != TEE_ERROR_SHORT_BUFFER)) {
+ dev_err(dev, "PTA_CMD_GET_DEVICES invoke function err: 0x%x\n", arg.ret);
+ return -EINVAL;
+ }
+
+ *shm_size = param.u.memref.size;
+
+ return 0;
+}
+
+static int enum_services(struct udevice *dev, struct tee_shm **shm, size_t *count, u32 tee_sess)
+{
+ size_t shm_size = 0;
+ int ret;
+
+ ret = __enum_services(dev, NULL, &shm_size, tee_sess);
+ if (ret)
+ return ret;
+
+ ret = tee_shm_alloc(dev, shm_size, 0, shm);
+ if (ret) {
+ dev_err(dev, "Failed to allocated shared memory: %d\n", ret);
+ return ret;
+ }
+
+ ret = __enum_services(dev, *shm, &shm_size, tee_sess);
+ if (!ret)
+ *count = shm_size / sizeof(struct tee_optee_ta_uuid);
+
+ return ret;
+}
+
+static int open_enum_session(struct udevice *dev, u32 *tee_sess)
+{
+ const struct tee_optee_ta_uuid pta_uuid = PTA_DEVICE_ENUM;
+ struct tee_open_session_arg arg = { };
+ int ret;
+
+ tee_optee_ta_uuid_to_octets(arg.uuid, &pta_uuid);
+
+ ret = tee_open_session(dev, &arg, 0, NULL);
+ if (ret || arg.ret) {
+ if (!ret)
+ ret = -EIO;
+ return ret;
+ }
+
+ *tee_sess = arg.session;
+
+ return 0;
+}
+
+static int bind_service_drivers(struct udevice *dev)
+{
+ struct tee_shm *service_list = NULL;
+ size_t service_count;
+ u32 tee_sess;
+ int ret;
+
+ ret = open_enum_session(dev, &tee_sess);
+ if (ret)
+ return ret;
+
+ ret = enum_services(dev, &service_list, &service_count, tee_sess);
+ if (!ret)
+ ret = bind_service_list(dev, service_list, service_count);
+
+ tee_shm_free(service_list);
+ tee_close_session(dev, tee_sess);
+
+ return ret;
+}
+
/**
* reg_pair_to_ptr() - Make a pointer of 2 32-bit values
* @reg0: High bits of the pointer
return 0;
}
+static int optee_bind(struct udevice *dev)
+{
+ if (IS_ENABLED(CONFIG_OPTEE_SERVICE_DISCOVERY))
+ dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
+
+ return 0;
+}
+
static int optee_probe(struct udevice *dev)
{
struct optee_pdata *pdata = dev_get_plat(dev);
return -ENOENT;
}
- /*
- * in U-Boot, the discovery of TA on the TEE bus is not supported:
- * only bind the drivers associated to the supported OP-TEE TA
- */
- if (IS_ENABLED(CONFIG_RNG_OPTEE)) {
+ if (IS_ENABLED(CONFIG_OPTEE_SERVICE_DISCOVERY)) {
+ ret = bind_service_drivers(dev);
+ if (ret)
+ return ret;
+ } else if (IS_ENABLED(CONFIG_RNG_OPTEE)) {
+ /*
+ * Discovery of TAs on the TEE bus is not supported in U-Boot:
+ * only bind the drivers associated to the supported OP-TEE TA
+ */
ret = device_bind_driver(dev, "optee-rng", "optee-rng", NULL);
if (ret)
return ret;
.of_match = optee_match,
.of_to_plat = optee_of_to_plat,
.probe = optee_probe,
+ .bind = optee_bind,
.ops = &optee_ops,
.plat_auto = sizeof(struct optee_pdata),
.priv_auto = sizeof(struct optee_private),