1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright(c) 2024 Intel Corporation */
3
4#include <linux/slab.h>
5#include <linux/types.h>
6#include "adf_mstate_mgr.h"
7
8#define ADF_MSTATE_MAGIC	0xADF5CAEA
9#define ADF_MSTATE_VERSION	0x1
10
11struct adf_mstate_sect_h {
12	u8 id[ADF_MSTATE_ID_LEN];
13	u32 size;
14	u32 sub_sects;
15	u8 state[];
16};
17
18u32 adf_mstate_state_size(struct adf_mstate_mgr *mgr)
19{
20	return mgr->state - mgr->buf;
21}
22
23static inline u32 adf_mstate_avail_room(struct adf_mstate_mgr *mgr)
24{
25	return mgr->buf + mgr->size - mgr->state;
26}
27
28void adf_mstate_mgr_init(struct adf_mstate_mgr *mgr, u8 *buf, u32 size)
29{
30	mgr->buf = buf;
31	mgr->state = buf;
32	mgr->size = size;
33	mgr->n_sects = 0;
34};
35
36struct adf_mstate_mgr *adf_mstate_mgr_new(u8 *buf, u32 size)
37{
38	struct adf_mstate_mgr *mgr;
39
40	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
41	if (!mgr)
42		return NULL;
43
44	adf_mstate_mgr_init(mgr, buf, size);
45
46	return mgr;
47}
48
49void adf_mstate_mgr_destroy(struct adf_mstate_mgr *mgr)
50{
51	kfree(mgr);
52}
53
54void adf_mstate_mgr_init_from_parent(struct adf_mstate_mgr *mgr,
55				     struct adf_mstate_mgr *p_mgr)
56{
57	adf_mstate_mgr_init(mgr, p_mgr->state,
58			    p_mgr->size - adf_mstate_state_size(p_mgr));
59}
60
61void adf_mstate_mgr_init_from_psect(struct adf_mstate_mgr *mgr,
62				    struct adf_mstate_sect_h *p_sect)
63{
64	adf_mstate_mgr_init(mgr, p_sect->state, p_sect->size);
65	mgr->n_sects = p_sect->sub_sects;
66}
67
68static void adf_mstate_preamble_init(struct adf_mstate_preh *preamble)
69{
70	preamble->magic = ADF_MSTATE_MAGIC;
71	preamble->version = ADF_MSTATE_VERSION;
72	preamble->preh_len = sizeof(*preamble);
73	preamble->size = 0;
74	preamble->n_sects = 0;
75}
76
77/* default preambles checker */
78static int adf_mstate_preamble_def_checker(struct adf_mstate_preh *preamble,
79					   void *opaque)
80{
81	struct adf_mstate_mgr *mgr = opaque;
82
83	if (preamble->magic != ADF_MSTATE_MAGIC ||
84	    preamble->version > ADF_MSTATE_VERSION ||
85	    preamble->preh_len > mgr->size) {
86		pr_debug("QAT: LM - Invalid state (magic=%#x, version=%#x, hlen=%u), state_size=%u\n",
87			 preamble->magic, preamble->version, preamble->preh_len,
88			 mgr->size);
89		return -EINVAL;
90	}
91
92	return 0;
93}
94
95struct adf_mstate_preh *adf_mstate_preamble_add(struct adf_mstate_mgr *mgr)
96{
97	struct adf_mstate_preh *pre = (struct adf_mstate_preh *)mgr->buf;
98
99	if (adf_mstate_avail_room(mgr) < sizeof(*pre)) {
100		pr_err("QAT: LM - Not enough space for preamble\n");
101		return NULL;
102	}
103
104	adf_mstate_preamble_init(pre);
105	mgr->state += pre->preh_len;
106
107	return pre;
108}
109
110int adf_mstate_preamble_update(struct adf_mstate_mgr *mgr)
111{
112	struct adf_mstate_preh *preamble = (struct adf_mstate_preh *)mgr->buf;
113
114	preamble->size = adf_mstate_state_size(mgr) - preamble->preh_len;
115	preamble->n_sects = mgr->n_sects;
116
117	return 0;
118}
119
120static void adf_mstate_dump_sect(struct adf_mstate_sect_h *sect,
121				 const char *prefix)
122{
123	pr_debug("QAT: LM - %s QAT state section %s\n", prefix, sect->id);
124	print_hex_dump_debug("h-", DUMP_PREFIX_OFFSET, 16, 2, sect,
125			     sizeof(*sect), true);
126	print_hex_dump_debug("s-", DUMP_PREFIX_OFFSET, 16, 2, sect->state,
127			     sect->size, true);
128}
129
130static inline void __adf_mstate_sect_update(struct adf_mstate_mgr *mgr,
131					    struct adf_mstate_sect_h *sect,
132					    u32 size,
133					    u32 n_subsects)
134{
135	sect->size += size;
136	sect->sub_sects += n_subsects;
137	mgr->n_sects++;
138	mgr->state += sect->size;
139
140	adf_mstate_dump_sect(sect, "Add");
141}
142
143void adf_mstate_sect_update(struct adf_mstate_mgr *p_mgr,
144			    struct adf_mstate_mgr *curr_mgr,
145			    struct adf_mstate_sect_h *sect)
146{
147	__adf_mstate_sect_update(p_mgr, sect, adf_mstate_state_size(curr_mgr),
148				 curr_mgr->n_sects);
149}
150
151static struct adf_mstate_sect_h *adf_mstate_sect_add_header(struct adf_mstate_mgr *mgr,
152							    const char *id)
153{
154	struct adf_mstate_sect_h *sect = (struct adf_mstate_sect_h *)(mgr->state);
155
156	if (adf_mstate_avail_room(mgr) < sizeof(*sect)) {
157		pr_debug("QAT: LM - Not enough space for header of QAT state sect %s\n", id);
158		return NULL;
159	}
160
161	strscpy(sect->id, id, sizeof(sect->id));
162	sect->size = 0;
163	sect->sub_sects = 0;
164	mgr->state += sizeof(*sect);
165
166	return sect;
167}
168
169struct adf_mstate_sect_h *adf_mstate_sect_add_vreg(struct adf_mstate_mgr *mgr,
170						   const char *id,
171						   struct adf_mstate_vreginfo *info)
172{
173	struct adf_mstate_sect_h *sect;
174
175	sect = adf_mstate_sect_add_header(mgr, id);
176	if (!sect)
177		return NULL;
178
179	if (adf_mstate_avail_room(mgr) < info->size) {
180		pr_debug("QAT: LM - Not enough space for QAT state sect %s, requires %u\n",
181			 id, info->size);
182		return NULL;
183	}
184
185	memcpy(sect->state, info->addr, info->size);
186	__adf_mstate_sect_update(mgr, sect, info->size, 0);
187
188	return sect;
189}
190
191struct adf_mstate_sect_h *adf_mstate_sect_add(struct adf_mstate_mgr *mgr,
192					      const char *id,
193					      adf_mstate_populate populate,
194					      void *opaque)
195{
196	struct adf_mstate_mgr sub_sects_mgr;
197	struct adf_mstate_sect_h *sect;
198	int avail_room, size;
199
200	sect = adf_mstate_sect_add_header(mgr, id);
201	if (!sect)
202		return NULL;
203
204	if (!populate)
205		return sect;
206
207	avail_room = adf_mstate_avail_room(mgr);
208	adf_mstate_mgr_init_from_parent(&sub_sects_mgr, mgr);
209
210	size = (*populate)(&sub_sects_mgr, sect->state, avail_room, opaque);
211	if (size < 0)
212		return NULL;
213
214	size += adf_mstate_state_size(&sub_sects_mgr);
215	if (avail_room < size) {
216		pr_debug("QAT: LM - Not enough space for QAT state sect %s, requires %u\n",
217			 id, size);
218		return NULL;
219	}
220	__adf_mstate_sect_update(mgr, sect, size, sub_sects_mgr.n_sects);
221
222	return sect;
223}
224
225static int adf_mstate_sect_validate(struct adf_mstate_mgr *mgr)
226{
227	struct adf_mstate_sect_h *start = (struct adf_mstate_sect_h *)mgr->state;
228	struct adf_mstate_sect_h *sect = start;
229	u64 end;
230	int i;
231
232	end = (uintptr_t)mgr->buf + mgr->size;
233	for (i = 0; i < mgr->n_sects; i++) {
234		uintptr_t s_start = (uintptr_t)sect->state;
235		uintptr_t s_end = s_start + sect->size;
236
237		if (s_end < s_start || s_end > end) {
238			pr_debug("QAT: LM - Corrupted state section (index=%u, size=%u) in state_mgr (size=%u, secs=%u)\n",
239				 i, sect->size, mgr->size, mgr->n_sects);
240			return -EINVAL;
241		}
242		sect = (struct adf_mstate_sect_h *)s_end;
243	}
244
245	pr_debug("QAT: LM - Scanned section (last child=%s, size=%lu) in state_mgr (size=%u, secs=%u)\n",
246		 start->id, sizeof(struct adf_mstate_sect_h) * (ulong)(sect - start),
247		 mgr->size, mgr->n_sects);
248
249	return 0;
250}
251
252u32 adf_mstate_state_size_from_remote(struct adf_mstate_mgr *mgr)
253{
254	struct adf_mstate_preh *preh = (struct adf_mstate_preh *)mgr->buf;
255
256	return preh->preh_len + preh->size;
257}
258
259int adf_mstate_mgr_init_from_remote(struct adf_mstate_mgr *mgr, u8 *buf, u32 size,
260				    adf_mstate_preamble_checker pre_checker,
261				    void *opaque)
262{
263	struct adf_mstate_preh *pre;
264	int ret;
265
266	adf_mstate_mgr_init(mgr, buf, size);
267	pre = (struct adf_mstate_preh *)(mgr->buf);
268
269	pr_debug("QAT: LM - Dump state preambles\n");
270	print_hex_dump_debug("", DUMP_PREFIX_OFFSET, 16, 2, pre, pre->preh_len, 0);
271
272	if (pre_checker)
273		ret = (*pre_checker)(pre, opaque);
274	else
275		ret = adf_mstate_preamble_def_checker(pre, mgr);
276	if (ret)
277		return ret;
278
279	mgr->state = mgr->buf + pre->preh_len;
280	mgr->n_sects = pre->n_sects;
281
282	return adf_mstate_sect_validate(mgr);
283}
284
285struct adf_mstate_sect_h *adf_mstate_sect_lookup(struct adf_mstate_mgr *mgr,
286						 const char *id,
287						 adf_mstate_action action,
288						 void *opaque)
289{
290	struct adf_mstate_sect_h *sect = (struct adf_mstate_sect_h *)mgr->state;
291	struct adf_mstate_mgr sub_sects_mgr;
292	int i, ret;
293
294	for (i = 0; i < mgr->n_sects; i++) {
295		if (!strncmp(sect->id, id, sizeof(sect->id)))
296			goto found;
297
298		sect = (struct adf_mstate_sect_h *)(sect->state + sect->size);
299	}
300
301	return NULL;
302
303found:
304	adf_mstate_dump_sect(sect, "Found");
305
306	adf_mstate_mgr_init_from_psect(&sub_sects_mgr, sect);
307	if (sect->sub_sects && adf_mstate_sect_validate(&sub_sects_mgr))
308		return NULL;
309
310	if (!action)
311		return sect;
312
313	ret = (*action)(&sub_sects_mgr, sect->state, sect->size, opaque);
314	if (ret)
315		return NULL;
316
317	return sect;
318}
319