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