1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2018, Google Inc. All rights reserved.
4  */
5 
6 #include <common.h>
7 #include <bloblist.h>
8 #include <log.h>
9 #include <mapmem.h>
10 #include <asm/global_data.h>
11 #include <test/suites.h>
12 #include <test/test.h>
13 #include <test/ut.h>
14 
15 DECLARE_GLOBAL_DATA_PTR;
16 
17 /* Declare a new compression test */
18 #define BLOBLIST_TEST(_name, _flags) \
19 		UNIT_TEST(_name, _flags, bloblist_test)
20 
21 enum {
22 	TEST_TAG		= 1,
23 	TEST_TAG2		= 2,
24 	TEST_TAG_MISSING	= 3,
25 
26 	TEST_SIZE		= 10,
27 	TEST_SIZE2		= 20,
28 	TEST_SIZE_LARGE		= 0x3e0,
29 
30 	TEST_ADDR		= CONFIG_BLOBLIST_ADDR,
31 	TEST_BLOBLIST_SIZE	= 0x400,
32 
33 	ERASE_BYTE		= '\xff',
34 };
35 
clear_bloblist(void)36 static struct bloblist_hdr *clear_bloblist(void)
37 {
38 	struct bloblist_hdr *hdr;
39 
40 	/*
41 	 * Clear out any existing bloblist so we have a clean slate. Zero the
42 	 * header so that existing records are removed, but set everything else
43 	 * to 0xff for testing purposes.
44 	 */
45 	hdr = map_sysmem(CONFIG_BLOBLIST_ADDR, TEST_BLOBLIST_SIZE);
46 	memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
47 	memset(hdr, '\0', sizeof(*hdr));
48 
49 	return hdr;
50 }
51 
check_zero(void * data,int size)52 static int check_zero(void *data, int size)
53 {
54 	u8 *ptr;
55 	int i;
56 
57 	for (ptr = data, i = 0; i < size; i++, ptr++) {
58 		if (*ptr)
59 			return -EINVAL;
60 	}
61 
62 	return 0;
63 }
64 
bloblist_test_init(struct unit_test_state * uts)65 static int bloblist_test_init(struct unit_test_state *uts)
66 {
67 	struct bloblist_hdr *hdr;
68 
69 	hdr = clear_bloblist();
70 	ut_asserteq(-ENOENT, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
71 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
72 	hdr->version++;
73 	ut_asserteq(-EPROTONOSUPPORT, bloblist_check(TEST_ADDR,
74 						     TEST_BLOBLIST_SIZE));
75 
76 	ut_asserteq(-ENOSPC, bloblist_new(TEST_ADDR, 0x10, 0));
77 	ut_asserteq(-EFAULT, bloblist_new(1, TEST_BLOBLIST_SIZE, 0));
78 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
79 
80 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
81 	ut_assertok(bloblist_finish());
82 	ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
83 	hdr->flags++;
84 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
85 
86 	return 1;
87 }
88 BLOBLIST_TEST(bloblist_test_init, 0);
89 
bloblist_test_blob(struct unit_test_state * uts)90 static int bloblist_test_blob(struct unit_test_state *uts)
91 {
92 	struct bloblist_hdr *hdr;
93 	struct bloblist_rec *rec, *rec2;
94 	char *data;
95 
96 	/* At the start there should be no records */
97 	hdr = clear_bloblist();
98 	ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE));
99 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
100 	ut_asserteq(map_to_sysmem(hdr), TEST_ADDR);
101 
102 	/* Add a record and check that we can find it */
103 	data = bloblist_add(TEST_TAG, TEST_SIZE, 0);
104 	rec = (void *)(hdr + 1);
105 	ut_asserteq_addr(rec + 1, data);
106 	data = bloblist_find(TEST_TAG, TEST_SIZE);
107 	ut_asserteq_addr(rec + 1, data);
108 
109 	/* Check the data is zeroed */
110 	ut_assertok(check_zero(data, TEST_SIZE));
111 
112 	/* Check the 'ensure' method */
113 	ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
114 	ut_assertnull(bloblist_ensure(TEST_TAG, TEST_SIZE2));
115 	rec2 = (struct bloblist_rec *)(data + ALIGN(TEST_SIZE, BLOBLIST_ALIGN));
116 	ut_assertok(check_zero(data, TEST_SIZE));
117 
118 	/* Check for a non-existent record */
119 	ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
120 	ut_asserteq_addr(rec2 + 1, bloblist_ensure(TEST_TAG2, TEST_SIZE2));
121 	ut_assertnull(bloblist_find(TEST_TAG_MISSING, 0));
122 
123 	return 0;
124 }
125 BLOBLIST_TEST(bloblist_test_blob, 0);
126 
127 /* Check bloblist_ensure_size_ret() */
bloblist_test_blob_ensure(struct unit_test_state * uts)128 static int bloblist_test_blob_ensure(struct unit_test_state *uts)
129 {
130 	void *data, *data2;
131 	int size;
132 
133 	/* At the start there should be no records */
134 	clear_bloblist();
135 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
136 
137 	/* Test with an empty bloblist */
138 	size = TEST_SIZE;
139 	ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data));
140 	ut_asserteq(TEST_SIZE, size);
141 	ut_assertok(check_zero(data, TEST_SIZE));
142 
143 	/* Check that we get the same thing again */
144 	ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data2));
145 	ut_asserteq(TEST_SIZE, size);
146 	ut_asserteq_addr(data, data2);
147 
148 	/* Check that the size remains the same */
149 	size = TEST_SIZE2;
150 	ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data));
151 	ut_asserteq(TEST_SIZE, size);
152 
153 	/* Check running out of space */
154 	size = TEST_SIZE_LARGE;
155 	ut_asserteq(-ENOSPC, bloblist_ensure_size_ret(TEST_TAG2, &size, &data));
156 
157 	return 0;
158 }
159 BLOBLIST_TEST(bloblist_test_blob_ensure, 0);
160 
bloblist_test_bad_blob(struct unit_test_state * uts)161 static int bloblist_test_bad_blob(struct unit_test_state *uts)
162 {
163 	struct bloblist_hdr *hdr;
164 	void *data;
165 
166 	hdr = clear_bloblist();
167 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
168 	data = hdr + 1;
169 	data += sizeof(struct bloblist_rec);
170 	ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
171 	ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
172 
173 	return 0;
174 }
175 BLOBLIST_TEST(bloblist_test_bad_blob, 0);
176 
bloblist_test_checksum(struct unit_test_state * uts)177 static int bloblist_test_checksum(struct unit_test_state *uts)
178 {
179 	struct bloblist_hdr *hdr;
180 	char *data, *data2;
181 
182 	hdr = clear_bloblist();
183 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
184 	ut_assertok(bloblist_finish());
185 	ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
186 
187 	/*
188 	 * Now change things amd make sure that the checksum notices. We cannot
189 	 * change the size or alloced fields, since that will crash the code.
190 	 * It has to rely on these being correct.
191 	 */
192 	hdr->flags--;
193 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
194 	hdr->flags++;
195 
196 	hdr->size--;
197 	ut_asserteq(-EFBIG, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
198 	hdr->size++;
199 
200 	hdr->spare++;
201 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
202 	hdr->spare--;
203 
204 	hdr->chksum++;
205 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
206 	hdr->chksum--;
207 
208 	/* Make sure the checksum changes when we add blobs */
209 	data = bloblist_add(TEST_TAG, TEST_SIZE, 0);
210 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
211 
212 	data2 = bloblist_add(TEST_TAG2, TEST_SIZE2, 0);
213 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
214 	ut_assertok(bloblist_finish());
215 
216 	/* It should also change if we change the data */
217 	ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
218 	*data += 1;
219 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
220 	*data -= 1;
221 
222 	ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
223 	*data2 += 1;
224 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
225 	*data2 -= 1;
226 
227 	/*
228 	 * Changing data outside the range of valid data should not affect
229 	 * the checksum.
230 	 */
231 	ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
232 	data[TEST_SIZE]++;
233 	data2[TEST_SIZE2]++;
234 	ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
235 
236 	return 0;
237 }
238 BLOBLIST_TEST(bloblist_test_checksum, 0);
239 
240 /* Test the 'bloblist info' command */
bloblist_test_cmd_info(struct unit_test_state * uts)241 static int bloblist_test_cmd_info(struct unit_test_state *uts)
242 {
243 	struct bloblist_hdr *hdr;
244 	char *data, *data2;
245 
246 	hdr = clear_bloblist();
247 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
248 	data = bloblist_ensure(TEST_TAG, TEST_SIZE);
249 	data2 = bloblist_ensure(TEST_TAG2, TEST_SIZE2);
250 
251 	console_record_reset_enable();
252 	ut_silence_console(uts);
253 	console_record_reset();
254 	run_command("bloblist info", 0);
255 	ut_assert_nextline("base:     %lx", (ulong)map_to_sysmem(hdr));
256 	ut_assert_nextline("size:     400    1 KiB");
257 	ut_assert_nextline("alloced:  70     112 Bytes");
258 	ut_assert_nextline("free:     390    912 Bytes");
259 	ut_assert_console_end();
260 	ut_unsilence_console(uts);
261 
262 	return 0;
263 }
264 BLOBLIST_TEST(bloblist_test_cmd_info, 0);
265 
266 /* Test the 'bloblist list' command */
bloblist_test_cmd_list(struct unit_test_state * uts)267 static int bloblist_test_cmd_list(struct unit_test_state *uts)
268 {
269 	struct bloblist_hdr *hdr;
270 	char *data, *data2;
271 
272 	hdr = clear_bloblist();
273 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
274 	data = bloblist_ensure(TEST_TAG, TEST_SIZE);
275 	data2 = bloblist_ensure(TEST_TAG2, TEST_SIZE2);
276 
277 	console_record_reset_enable();
278 	ut_silence_console(uts);
279 	console_record_reset();
280 	run_command("bloblist list", 0);
281 	ut_assert_nextline("Address       Size  Tag Name");
282 	ut_assert_nextline("%08lx  %8x    1 EC host event",
283 			   (ulong)map_to_sysmem(data), TEST_SIZE);
284 	ut_assert_nextline("%08lx  %8x    2 SPL hand-off",
285 			   (ulong)map_to_sysmem(data2), TEST_SIZE2);
286 	ut_assert_console_end();
287 	ut_unsilence_console(uts);
288 
289 	return 0;
290 }
291 BLOBLIST_TEST(bloblist_test_cmd_list, 0);
292 
293 /* Test alignment of bloblist blobs */
bloblist_test_align(struct unit_test_state * uts)294 static int bloblist_test_align(struct unit_test_state *uts)
295 {
296 	struct bloblist_hdr *hdr;
297 	ulong addr;
298 	char *data;
299 	int i;
300 
301 	/* At the start there should be no records */
302 	hdr = clear_bloblist();
303 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
304 	ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE));
305 
306 	/* Check the default alignment */
307 	for (i = 0; i < 3; i++) {
308 		int size = i * 3;
309 		ulong addr;
310 		char *data;
311 		int j;
312 
313 		data = bloblist_add(i, size, 0);
314 		ut_assertnonnull(data);
315 		addr = map_to_sysmem(data);
316 		ut_asserteq(0, addr & (BLOBLIST_ALIGN - 1));
317 
318 		/* Only the bytes in the blob data should be zeroed */
319 		for (j = 0; j < size; j++)
320 			ut_asserteq(0, data[j]);
321 		for (; j < BLOBLIST_ALIGN; j++)
322 			ut_asserteq(ERASE_BYTE, data[j]);
323 	}
324 
325 	/* Check larger alignment */
326 	for (i = 0; i < 3; i++) {
327 		int align = 32 << i;
328 
329 		data = bloblist_add(3 + i, i * 4, align);
330 		ut_assertnonnull(data);
331 		addr = map_to_sysmem(data);
332 		ut_asserteq(0, addr & (align - 1));
333 	}
334 
335 	/* Check alignment with an bloblist starting on a smaller alignment */
336 	hdr = map_sysmem(TEST_ADDR + BLOBLIST_ALIGN, TEST_BLOBLIST_SIZE);
337 	memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
338 	memset(hdr, '\0', sizeof(*hdr));
339 	ut_assertok(bloblist_new(TEST_ADDR + BLOBLIST_ALIGN, TEST_BLOBLIST_SIZE,
340 				 0));
341 
342 	data = bloblist_add(1, 5, BLOBLIST_ALIGN * 2);
343 	ut_assertnonnull(data);
344 	addr = map_to_sysmem(data);
345 	ut_asserteq(0, addr & (BLOBLIST_ALIGN * 2 - 1));
346 
347 	return 0;
348 }
349 BLOBLIST_TEST(bloblist_test_align, 0);
350 
351 /* Test relocation of a bloblist */
bloblist_test_reloc(struct unit_test_state * uts)352 static int bloblist_test_reloc(struct unit_test_state *uts)
353 {
354 	const uint large_size = TEST_BLOBLIST_SIZE;
355 	const uint small_size = 0x20;
356 	void *old_ptr, *new_ptr;
357 	void *blob1, *blob2;
358 	ulong new_addr;
359 	ulong new_size;
360 
361 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
362 	old_ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
363 
364 	/* Add one blob and then one that won't fit */
365 	blob1 = bloblist_add(TEST_TAG, small_size, 0);
366 	ut_assertnonnull(blob1);
367 	blob2 = bloblist_add(TEST_TAG2, large_size, 0);
368 	ut_assertnull(blob2);
369 
370 	/* Relocate the bloblist somewhere else, a bit larger */
371 	new_addr = TEST_ADDR + TEST_BLOBLIST_SIZE;
372 	new_size = TEST_BLOBLIST_SIZE + 0x100;
373 	new_ptr = map_sysmem(new_addr, TEST_BLOBLIST_SIZE);
374 	bloblist_reloc(new_ptr, new_size, old_ptr, TEST_BLOBLIST_SIZE);
375 	gd->bloblist = new_ptr;
376 
377 	/* Check the old blob is there and that we can now add the bigger one */
378 	ut_assertnonnull(bloblist_find(TEST_TAG, small_size));
379 	ut_assertnull(bloblist_find(TEST_TAG2, small_size));
380 	blob2 = bloblist_add(TEST_TAG2, large_size, 0);
381 	ut_assertnonnull(blob2);
382 
383 	return 0;
384 }
385 BLOBLIST_TEST(bloblist_test_reloc, 0);
386 
do_ut_bloblist(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])387 int do_ut_bloblist(struct cmd_tbl *cmdtp, int flag, int argc,
388 		   char *const argv[])
389 {
390 	struct unit_test *tests = UNIT_TEST_SUITE_START(bloblist_test);
391 	const int n_ents = UNIT_TEST_SUITE_COUNT(bloblist_test);
392 
393 	return cmd_ut_category("bloblist", "bloblist_test_",
394 			       tests, n_ents, argc, argv);
395 }
396