summaryrefslogtreecommitdiff
path: root/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c
blob: 41cc763a74aa25ad44da62d912e472d44d1f0507 (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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2024 Intel Corporation */

#include <linux/slab.h>
#include <linux/types.h>
#include "adf_mstate_mgr.h"

#define ADF_MSTATE_MAGIC	0xADF5CAEA
#define ADF_MSTATE_VERSION	0x1

struct adf_mstate_sect_h {
	u8 id[ADF_MSTATE_ID_LEN];
	u32 size;
	u32 sub_sects;
	u8 state[];
};

u32 adf_mstate_state_size(struct adf_mstate_mgr *mgr)
{
	return mgr->state - mgr->buf;
}

static inline u32 adf_mstate_avail_room(struct adf_mstate_mgr *mgr)
{
	return mgr->buf + mgr->size - mgr->state;
}

void adf_mstate_mgr_init(struct adf_mstate_mgr *mgr, u8 *buf, u32 size)
{
	mgr->buf = buf;
	mgr->state = buf;
	mgr->size = size;
	mgr->n_sects = 0;
};

struct adf_mstate_mgr *adf_mstate_mgr_new(u8 *buf, u32 size)
{
	struct adf_mstate_mgr *mgr;

	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
	if (!mgr)
		return NULL;

	adf_mstate_mgr_init(mgr, buf, size);

	return mgr;
}

void adf_mstate_mgr_destroy(struct adf_mstate_mgr *mgr)
{
	kfree(mgr);
}

void adf_mstate_mgr_init_from_parent(struct adf_mstate_mgr *mgr,
				     struct adf_mstate_mgr *p_mgr)
{
	adf_mstate_mgr_init(mgr, p_mgr->state,
			    p_mgr->size - adf_mstate_state_size(p_mgr));
}

void adf_mstate_mgr_init_from_psect(struct adf_mstate_mgr *mgr,
				    struct adf_mstate_sect_h *p_sect)
{
	adf_mstate_mgr_init(mgr, p_sect->state, p_sect->size);
	mgr->n_sects = p_sect->sub_sects;
}

static void adf_mstate_preamble_init(struct adf_mstate_preh *preamble)
{
	preamble->magic = ADF_MSTATE_MAGIC;
	preamble->version = ADF_MSTATE_VERSION;
	preamble->preh_len = sizeof(*preamble);
	preamble->size = 0;
	preamble->n_sects = 0;
}

/* default preambles checker */
static int adf_mstate_preamble_def_checker(struct adf_mstate_preh *preamble,
					   void *opaque)
{
	struct adf_mstate_mgr *mgr = opaque;

	if (preamble->magic != ADF_MSTATE_MAGIC ||
	    preamble->version > ADF_MSTATE_VERSION ||
	    preamble->preh_len > mgr->size) {
		pr_debug("QAT: LM - Invalid state (magic=%#x, version=%#x, hlen=%u), state_size=%u\n",
			 preamble->magic, preamble->version, preamble->preh_len,
			 mgr->size);
		return -EINVAL;
	}

	return 0;
}

struct adf_mstate_preh *adf_mstate_preamble_add(struct adf_mstate_mgr *mgr)
{
	struct adf_mstate_preh *pre = (struct adf_mstate_preh *)mgr->buf;

	if (adf_mstate_avail_room(mgr) < sizeof(*pre)) {
		pr_err("QAT: LM - Not enough space for preamble\n");
		return NULL;
	}

	adf_mstate_preamble_init(pre);
	mgr->state += pre->preh_len;

	return pre;
}

int adf_mstate_preamble_update(struct adf_mstate_mgr *mgr)
{
	struct adf_mstate_preh *preamble = (struct adf_mstate_preh *)mgr->buf;

	preamble->size = adf_mstate_state_size(mgr) - preamble->preh_len;
	preamble->n_sects = mgr->n_sects;

	return 0;
}

static void adf_mstate_dump_sect(struct adf_mstate_sect_h *sect,
				 const char *prefix)
{
	pr_debug("QAT: LM - %s QAT state section %s\n", prefix, sect->id);
	print_hex_dump_debug("h-", DUMP_PREFIX_OFFSET, 16, 2, sect,
			     sizeof(*sect), true);
	print_hex_dump_debug("s-", DUMP_PREFIX_OFFSET, 16, 2, sect->state,
			     sect->size, true);
}

static inline void __adf_mstate_sect_update(struct adf_mstate_mgr *mgr,
					    struct adf_mstate_sect_h *sect,
					    u32 size,
					    u32 n_subsects)
{
	sect->size += size;
	sect->sub_sects += n_subsects;
	mgr->n_sects++;
	mgr->state += sect->size;

	adf_mstate_dump_sect(sect, "Add");
}

void adf_mstate_sect_update(struct adf_mstate_mgr *p_mgr,
			    struct adf_mstate_mgr *curr_mgr,
			    struct adf_mstate_sect_h *sect)
{
	__adf_mstate_sect_update(p_mgr, sect, adf_mstate_state_size(curr_mgr),
				 curr_mgr->n_sects);
}

static struct adf_mstate_sect_h *adf_mstate_sect_add_header(struct adf_mstate_mgr *mgr,
							    const char *id)
{
	struct adf_mstate_sect_h *sect = (struct adf_mstate_sect_h *)(mgr->state);

	if (adf_mstate_avail_room(mgr) < sizeof(*sect)) {
		pr_debug("QAT: LM - Not enough space for header of QAT state sect %s\n", id);
		return NULL;
	}

	strscpy(sect->id, id, sizeof(sect->id));
	sect->size = 0;
	sect->sub_sects = 0;
	mgr->state += sizeof(*sect);

	return sect;
}

struct adf_mstate_sect_h *adf_mstate_sect_add_vreg(struct adf_mstate_mgr *mgr,
						   const char *id,
						   struct adf_mstate_vreginfo *info)
{
	struct adf_mstate_sect_h *sect;

	sect = adf_mstate_sect_add_header(mgr, id);
	if (!sect)
		return NULL;

	if (adf_mstate_avail_room(mgr) < info->size) {
		pr_debug("QAT: LM - Not enough space for QAT state sect %s, requires %u\n",
			 id, info->size);
		return NULL;
	}

	memcpy(sect->state, info->addr, info->size);
	__adf_mstate_sect_update(mgr, sect, info->size, 0);

	return sect;
}

struct adf_mstate_sect_h *adf_mstate_sect_add(struct adf_mstate_mgr *mgr,
					      const char *id,
					      adf_mstate_populate populate,
					      void *opaque)
{
	struct adf_mstate_mgr sub_sects_mgr;
	struct adf_mstate_sect_h *sect;
	int avail_room, size;

	sect = adf_mstate_sect_add_header(mgr, id);
	if (!sect)
		return NULL;

	if (!populate)
		return sect;

	avail_room = adf_mstate_avail_room(mgr);
	adf_mstate_mgr_init_from_parent(&sub_sects_mgr, mgr);

	size = (*populate)(&sub_sects_mgr, sect->state, avail_room, opaque);
	if (size < 0)
		return NULL;

	size += adf_mstate_state_size(&sub_sects_mgr);
	if (avail_room < size) {
		pr_debug("QAT: LM - Not enough space for QAT state sect %s, requires %u\n",
			 id, size);
		return NULL;
	}
	__adf_mstate_sect_update(mgr, sect, size, sub_sects_mgr.n_sects);

	return sect;
}

static int adf_mstate_sect_validate(struct adf_mstate_mgr *mgr)
{
	struct adf_mstate_sect_h *start = (struct adf_mstate_sect_h *)mgr->state;
	struct adf_mstate_sect_h *sect = start;
	u64 end;
	int i;

	end = (uintptr_t)mgr->buf + mgr->size;
	for (i = 0; i < mgr->n_sects; i++) {
		uintptr_t s_start = (uintptr_t)sect->state;
		uintptr_t s_end = s_start + sect->size;

		if (s_end < s_start || s_end > end) {
			pr_debug("QAT: LM - Corrupted state section (index=%u, size=%u) in state_mgr (size=%u, secs=%u)\n",
				 i, sect->size, mgr->size, mgr->n_sects);
			return -EINVAL;
		}
		sect = (struct adf_mstate_sect_h *)s_end;
	}

	pr_debug("QAT: LM - Scanned section (last child=%s, size=%lu) in state_mgr (size=%u, secs=%u)\n",
		 start->id, sizeof(struct adf_mstate_sect_h) * (ulong)(sect - start),
		 mgr->size, mgr->n_sects);

	return 0;
}

u32 adf_mstate_state_size_from_remote(struct adf_mstate_mgr *mgr)
{
	struct adf_mstate_preh *preh = (struct adf_mstate_preh *)mgr->buf;

	return preh->preh_len + preh->size;
}

int adf_mstate_mgr_init_from_remote(struct adf_mstate_mgr *mgr, u8 *buf, u32 size,
				    adf_mstate_preamble_checker pre_checker,
				    void *opaque)
{
	struct adf_mstate_preh *pre;
	int ret;

	adf_mstate_mgr_init(mgr, buf, size);
	pre = (struct adf_mstate_preh *)(mgr->buf);

	pr_debug("QAT: LM - Dump state preambles\n");
	print_hex_dump_debug("", DUMP_PREFIX_OFFSET, 16, 2, pre, pre->preh_len, 0);

	if (pre_checker)
		ret = (*pre_checker)(pre, opaque);
	else
		ret = adf_mstate_preamble_def_checker(pre, mgr);
	if (ret)
		return ret;

	mgr->state = mgr->buf + pre->preh_len;
	mgr->n_sects = pre->n_sects;

	return adf_mstate_sect_validate(mgr);
}

struct adf_mstate_sect_h *adf_mstate_sect_lookup(struct adf_mstate_mgr *mgr,
						 const char *id,
						 adf_mstate_action action,
						 void *opaque)
{
	struct adf_mstate_sect_h *sect = (struct adf_mstate_sect_h *)mgr->state;
	struct adf_mstate_mgr sub_sects_mgr;
	int i, ret;

	for (i = 0; i < mgr->n_sects; i++) {
		if (!strncmp(sect->id, id, sizeof(sect->id)))
			goto found;

		sect = (struct adf_mstate_sect_h *)(sect->state + sect->size);
	}

	return NULL;

found:
	adf_mstate_dump_sect(sect, "Found");

	adf_mstate_mgr_init_from_psect(&sub_sects_mgr, sect);
	if (sect->sub_sects && adf_mstate_sect_validate(&sub_sects_mgr))
		return NULL;

	if (!action)
		return sect;

	ret = (*action)(&sub_sects_mgr, sect->state, sect->size, opaque);
	if (ret)
		return NULL;

	return sect;
}