1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2019 Linaro Ltd. |
4 | */ |
5 | |
6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/tee_drv.h> |
11 | #include <linux/uuid.h> |
12 | #include "optee_private.h" |
13 | |
14 | static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) |
15 | { |
16 | if (ver->impl_id == TEE_IMPL_ID_OPTEE) |
17 | return 1; |
18 | else |
19 | return 0; |
20 | } |
21 | |
22 | static int get_devices(struct tee_context *ctx, u32 session, |
23 | struct tee_shm *device_shm, u32 *shm_size, |
24 | u32 func) |
25 | { |
26 | int ret = 0; |
27 | struct tee_ioctl_invoke_arg inv_arg; |
28 | struct tee_param param[4]; |
29 | |
30 | memset(&inv_arg, 0, sizeof(inv_arg)); |
31 | memset(¶m, 0, sizeof(param)); |
32 | |
33 | inv_arg.func = func; |
34 | inv_arg.session = session; |
35 | inv_arg.num_params = 4; |
36 | |
37 | /* Fill invoke cmd params */ |
38 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; |
39 | param[0].u.memref.shm = device_shm; |
40 | param[0].u.memref.size = *shm_size; |
41 | param[0].u.memref.shm_offs = 0; |
42 | |
43 | ret = tee_client_invoke_func(ctx, arg: &inv_arg, param); |
44 | if ((ret < 0) || ((inv_arg.ret != TEEC_SUCCESS) && |
45 | (inv_arg.ret != TEEC_ERROR_SHORT_BUFFER))) { |
46 | pr_err("PTA_CMD_GET_DEVICES invoke function err: %x\n" , |
47 | inv_arg.ret); |
48 | return -EINVAL; |
49 | } |
50 | |
51 | *shm_size = param[0].u.memref.size; |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | static void optee_release_device(struct device *dev) |
57 | { |
58 | struct tee_client_device *optee_device = to_tee_client_device(dev); |
59 | |
60 | kfree(objp: optee_device); |
61 | } |
62 | |
63 | static int optee_register_device(const uuid_t *device_uuid) |
64 | { |
65 | struct tee_client_device *optee_device = NULL; |
66 | int rc; |
67 | |
68 | optee_device = kzalloc(size: sizeof(*optee_device), GFP_KERNEL); |
69 | if (!optee_device) |
70 | return -ENOMEM; |
71 | |
72 | optee_device->dev.bus = &tee_bus_type; |
73 | optee_device->dev.release = optee_release_device; |
74 | if (dev_set_name(dev: &optee_device->dev, name: "optee-ta-%pUb" , device_uuid)) { |
75 | kfree(objp: optee_device); |
76 | return -ENOMEM; |
77 | } |
78 | uuid_copy(dst: &optee_device->id.uuid, src: device_uuid); |
79 | |
80 | rc = device_register(dev: &optee_device->dev); |
81 | if (rc) { |
82 | pr_err("device registration failed, err: %d\n" , rc); |
83 | put_device(dev: &optee_device->dev); |
84 | } |
85 | |
86 | return rc; |
87 | } |
88 | |
89 | static int __optee_enumerate_devices(u32 func) |
90 | { |
91 | const uuid_t pta_uuid = |
92 | UUID_INIT(0x7011a688, 0xddde, 0x4053, |
93 | 0xa5, 0xa9, 0x7b, 0x3c, 0x4d, 0xdf, 0x13, 0xb8); |
94 | struct tee_ioctl_open_session_arg sess_arg; |
95 | struct tee_shm *device_shm = NULL; |
96 | const uuid_t *device_uuid = NULL; |
97 | struct tee_context *ctx = NULL; |
98 | u32 shm_size = 0, idx, num_devices = 0; |
99 | int rc; |
100 | |
101 | memset(&sess_arg, 0, sizeof(sess_arg)); |
102 | |
103 | /* Open context with OP-TEE driver */ |
104 | ctx = tee_client_open_context(NULL, match: optee_ctx_match, NULL, NULL); |
105 | if (IS_ERR(ptr: ctx)) |
106 | return -ENODEV; |
107 | |
108 | /* Open session with device enumeration pseudo TA */ |
109 | export_uuid(dst: sess_arg.uuid, src: &pta_uuid); |
110 | sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; |
111 | sess_arg.num_params = 0; |
112 | |
113 | rc = tee_client_open_session(ctx, arg: &sess_arg, NULL); |
114 | if ((rc < 0) || (sess_arg.ret != TEEC_SUCCESS)) { |
115 | /* Device enumeration pseudo TA not found */ |
116 | rc = 0; |
117 | goto out_ctx; |
118 | } |
119 | |
120 | rc = get_devices(ctx, session: sess_arg.session, NULL, shm_size: &shm_size, func); |
121 | if (rc < 0 || !shm_size) |
122 | goto out_sess; |
123 | |
124 | device_shm = tee_shm_alloc_kernel_buf(ctx, size: shm_size); |
125 | if (IS_ERR(ptr: device_shm)) { |
126 | pr_err("tee_shm_alloc_kernel_buf failed\n" ); |
127 | rc = PTR_ERR(ptr: device_shm); |
128 | goto out_sess; |
129 | } |
130 | |
131 | rc = get_devices(ctx, session: sess_arg.session, device_shm, shm_size: &shm_size, func); |
132 | if (rc < 0) |
133 | goto out_shm; |
134 | |
135 | device_uuid = tee_shm_get_va(shm: device_shm, offs: 0); |
136 | if (IS_ERR(ptr: device_uuid)) { |
137 | pr_err("tee_shm_get_va failed\n" ); |
138 | rc = PTR_ERR(ptr: device_uuid); |
139 | goto out_shm; |
140 | } |
141 | |
142 | num_devices = shm_size / sizeof(uuid_t); |
143 | |
144 | for (idx = 0; idx < num_devices; idx++) { |
145 | rc = optee_register_device(device_uuid: &device_uuid[idx]); |
146 | if (rc) |
147 | goto out_shm; |
148 | } |
149 | |
150 | out_shm: |
151 | tee_shm_free(shm: device_shm); |
152 | out_sess: |
153 | tee_client_close_session(ctx, session: sess_arg.session); |
154 | out_ctx: |
155 | tee_client_close_context(ctx); |
156 | |
157 | return rc; |
158 | } |
159 | |
160 | int optee_enumerate_devices(u32 func) |
161 | { |
162 | return __optee_enumerate_devices(func); |
163 | } |
164 | |
165 | static int __optee_unregister_device(struct device *dev, void *data) |
166 | { |
167 | if (!strncmp(dev_name(dev), "optee-ta" , strlen("optee-ta" ))) |
168 | device_unregister(dev); |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | void optee_unregister_devices(void) |
174 | { |
175 | bus_for_each_dev(bus: &tee_bus_type, NULL, NULL, |
176 | fn: __optee_unregister_device); |
177 | } |
178 | |