summaryrefslogtreecommitdiffstats
path: root/drivers/firmware/broadcom/tee_bnxt_fw.c
blob: a5bf4c3f6dc74ba4b6c33c7aa70b48b920e601e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2019 Broadcom.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include <linux/uuid.h>

#include <linux/firmware/broadcom/tee_bnxt_fw.h>

#define MAX_SHM_MEM_SZ	SZ_4M

#define MAX_TEE_PARAM_ARRY_MEMB		4

enum ta_cmd {
	/*
	 * TA_CMD_BNXT_FASTBOOT - boot bnxt device by copying f/w into sram
	 *
	 *	param[0] unused
	 *	param[1] unused
	 *	param[2] unused
	 *	param[3] unused
	 *
	 * Result:
	 *	TEE_SUCCESS - Invoke command success
	 *	TEE_ERROR_ITEM_NOT_FOUND - Corrupt f/w image found on memory
	 */
	TA_CMD_BNXT_FASTBOOT = 0,

	/*
	 * TA_CMD_BNXT_COPY_COREDUMP - copy the core dump into shm
	 *
	 *	param[0] (inout memref) - Coredump buffer memory reference
	 *	param[1] (in value) - value.a: offset, data to be copied from
	 *			      value.b: size of data to be copied
	 *	param[2] unused
	 *	param[3] unused
	 *
	 * Result:
	 *	TEE_SUCCESS - Invoke command success
	 *	TEE_ERROR_BAD_PARAMETERS - Incorrect input param
	 *	TEE_ERROR_ITEM_NOT_FOUND - Corrupt core dump
	 */
	TA_CMD_BNXT_COPY_COREDUMP = 3,
};

/**
 * struct tee_bnxt_fw_private - OP-TEE bnxt private data
 * @dev:		OP-TEE based bnxt device.
 * @ctx:		OP-TEE context handler.
 * @session_id:		TA session identifier.
 */
struct tee_bnxt_fw_private {
	struct device *dev;
	struct tee_context *ctx;
	u32 session_id;
	struct tee_shm *fw_shm_pool;
};

static struct tee_bnxt_fw_private pvt_data;

static void prepare_args(int cmd,
			 struct tee_ioctl_invoke_arg *arg,
			 struct tee_param *param)
{
	memset(arg, 0, sizeof(*arg));
	memset(param, 0, MAX_TEE_PARAM_ARRY_MEMB * sizeof(*param));

	arg->func = cmd;
	arg->session = pvt_data.session_id;
	arg->num_params = MAX_TEE_PARAM_ARRY_MEMB;

	/* Fill invoke cmd params */
	switch (cmd) {
	case TA_CMD_BNXT_COPY_COREDUMP:
		param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
		param[0].u.memref.shm = pvt_data.fw_shm_pool;
		param[0].u.memref.size = MAX_SHM_MEM_SZ;
		param[0].u.memref.shm_offs = 0;
		param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
		break;
	case TA_CMD_BNXT_FASTBOOT:
	default:
		/* Nothing to do */
		break;
	}
}

/**
 * tee_bnxt_fw_load() - Load the bnxt firmware
 *		    Uses an OP-TEE call to start a secure
 *		    boot process.
 * Returns 0 on success, negative errno otherwise.
 */
int tee_bnxt_fw_load(void)
{
	int ret = 0;
	struct tee_ioctl_invoke_arg arg;
	struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB];

	if (!pvt_data.ctx)
		return -ENODEV;

	prepare_args(TA_CMD_BNXT_FASTBOOT, &arg, param);

	ret = tee_client_invoke_func(pvt_data.ctx, &arg, param);
	if (ret < 0 || arg.ret != 0) {
		dev_err(pvt_data.dev,
			"TA_CMD_BNXT_FASTBOOT invoke failed TEE err: %x, ret:%x\n",
			arg.ret, ret);
		return -EINVAL;
	}

	return 0;
}
EXPORT_SYMBOL(tee_bnxt_fw_load);

/**
 * tee_bnxt_copy_coredump() - Copy coredump from the allocated memory
 *			    Uses an OP-TEE call to copy coredump
 * @buf:	destination buffer where core dump is copied into
 * @offset:	offset from the base address of core dump area
 * @size:	size of the dump
 *
 * Returns 0 on success, negative errno otherwise.
 */
int tee_bnxt_copy_coredump(void *buf, u32 offset, u32 size)
{
	struct tee_ioctl_invoke_arg arg;
	struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB];
	void *core_data;
	u32 rbytes = size;
	u32 nbytes = 0;
	int ret = 0;

	if (!pvt_data.ctx)
		return -ENODEV;

	prepare_args(TA_CMD_BNXT_COPY_COREDUMP, &arg, param);

	while (rbytes)  {
		nbytes = rbytes;

		nbytes = min_t(u32, rbytes, param[0].u.memref.size);

		/* Fill additional invoke cmd params */
		param[1].u.value.a = offset;
		param[1].u.value.b = nbytes;

		ret = tee_client_invoke_func(pvt_data.ctx, &arg, param);
		if (ret < 0 || arg.ret != 0) {
			dev_err(pvt_data.dev,
				"TA_CMD_BNXT_COPY_COREDUMP invoke failed TEE err: %x, ret:%x\n",
				arg.ret, ret);
			return -EINVAL;
		}

		core_data = tee_shm_get_va(pvt_data.fw_shm_pool, 0);
		if (IS_ERR(core_data)) {
			dev_err(pvt_data.dev, "tee_shm_get_va failed\n");
			return PTR_ERR(core_data);
		}

		memcpy(buf, core_data, nbytes);

		rbytes -= nbytes;
		buf += nbytes;
		offset += nbytes;
	}

	return 0;
}
EXPORT_SYMBOL(tee_bnxt_copy_coredump);

static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
{
	return (ver->impl_id == TEE_IMPL_ID_OPTEE);
}

static int tee_bnxt_fw_probe(struct device *dev)
{
	struct tee_client_device *bnxt_device = to_tee_client_device(dev);
	int ret, err = -ENODEV;
	struct tee_ioctl_open_session_arg sess_arg;
	struct tee_shm *fw_shm_pool;

	memset(&sess_arg, 0, sizeof(sess_arg));

	/* Open context with TEE driver */
	pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,
					       NULL);
	if (IS_ERR(pvt_data.ctx))
		return -ENODEV;

	/* Open session with Bnxt load Trusted App */
	memcpy(sess_arg.uuid, bnxt_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
	sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
	sess_arg.num_params = 0;

	ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
	if (ret < 0 || sess_arg.ret != 0) {
		dev_err(dev, "tee_client_open_session failed, err: %x\n",
			sess_arg.ret);
		err = -EINVAL;
		goto out_ctx;
	}
	pvt_data.session_id = sess_arg.session;

	pvt_data.dev = dev;

	fw_shm_pool = tee_shm_alloc_kernel_buf(pvt_data.ctx, MAX_SHM_MEM_SZ);
	if (IS_ERR(fw_shm_pool)) {
		dev_err(pvt_data.dev, "tee_shm_alloc_kernel_buf failed\n");
		err = PTR_ERR(fw_shm_pool);
		goto out_sess;
	}

	pvt_data.fw_shm_pool = fw_shm_pool;

	return 0;

out_sess:
	tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
out_ctx:
	tee_client_close_context(pvt_data.ctx);

	return err;
}

static int tee_bnxt_fw_remove(struct device *dev)
{
	tee_shm_free(pvt_data.fw_shm_pool);
	tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
	tee_client_close_context(pvt_data.ctx);
	pvt_data.ctx = NULL;

	return 0;
}

static void tee_bnxt_fw_shutdown(struct device *dev)
{
	tee_shm_free(pvt_data.fw_shm_pool);
	tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
	tee_client_close_context(pvt_data.ctx);
	pvt_data.ctx = NULL;
}

static const struct tee_client_device_id tee_bnxt_fw_id_table[] = {
	{UUID_INIT(0x6272636D, 0x2019, 0x0716,
		    0x42, 0x43, 0x4D, 0x5F, 0x53, 0x43, 0x48, 0x49)},
	{}
};

MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table);

static struct tee_client_driver tee_bnxt_fw_driver = {
	.id_table	= tee_bnxt_fw_id_table,
	.driver		= {
		.name		= KBUILD_MODNAME,
		.bus		= &tee_bus_type,
		.probe		= tee_bnxt_fw_probe,
		.remove		= tee_bnxt_fw_remove,
		.shutdown	= tee_bnxt_fw_shutdown,
	},
};

static int __init tee_bnxt_fw_mod_init(void)
{
	return driver_register(&tee_bnxt_fw_driver.driver);
}

static void __exit tee_bnxt_fw_mod_exit(void)
{
	driver_unregister(&tee_bnxt_fw_driver.driver);
}

module_init(tee_bnxt_fw_mod_init);
module_exit(tee_bnxt_fw_mod_exit);

MODULE_AUTHOR("Vikas Gupta <vikas.gupta@broadcom.com>");
MODULE_DESCRIPTION("Broadcom bnxt firmware manager");
MODULE_LICENSE("GPL v2");