1ed9172d89777b650d450a9a0d17c0a0f5b0031b
[platform/kernel/u-boot.git] / common / bloblist.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2018 Google, Inc
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6
7 #define LOG_DEBUG
8 #define LOG_CATEGORY    LOGC_BLOBLIST
9
10 #include <common.h>
11 #include <bloblist.h>
12 #include <log.h>
13 #include <malloc.h>
14 #include <mapmem.h>
15 #include <spl.h>
16 #include <asm/global_data.h>
17 #include <u-boot/crc.h>
18
19 /*
20  * A bloblist is a single contiguous chunk of memory with a header
21  * (struct bloblist_hdr) and a number of blobs in it.
22  *
23  * Each blob starts on a BLOBLIST_ALIGN boundary relative to the start of the
24  * bloblist and consists of a struct bloblist_rec, some padding to the required
25  * alignment for the blog and then the actual data. The padding ensures that the
26  * start address of the data in each blob is aligned as required. Note that
27  * each blob's *data* is aligned to BLOBLIST_ALIGN regardless of the alignment
28  * of the bloblist itself or the blob header.
29  *
30  * So far, only BLOBLIST_ALIGN alignment is supported.
31  */
32
33 DECLARE_GLOBAL_DATA_PTR;
34
35 static struct tag_name {
36         enum bloblist_tag_t tag;
37         const char *name;
38 } tag_name[] = {
39         { BLOBLISTT_NONE, "(none)" },
40
41         /* BLOBLISTT_AREA_FIRMWARE_TOP */
42
43         /* BLOBLISTT_AREA_FIRMWARE */
44         { BLOBLISTT_ACPI_GNVS, "ACPI GNVS" },
45         { BLOBLISTT_INTEL_VBT, "Intel Video-BIOS table" },
46         { BLOBLISTT_TPM2_TCG_LOG, "TPM v2 log space" },
47         { BLOBLISTT_TCPA_LOG, "TPM log space" },
48         { BLOBLISTT_ACPI_TABLES, "ACPI tables for x86" },
49         { BLOBLISTT_SMBIOS_TABLES, "SMBIOS tables for x86" },
50         { BLOBLISTT_VBOOT_CTX, "Chrome OS vboot context" },
51
52         /* BLOBLISTT_PROJECT_AREA */
53         { BLOBLISTT_U_BOOT_SPL_HANDOFF, "SPL hand-off" },
54
55         /* BLOBLISTT_VENDOR_AREA */
56 };
57
58 const char *bloblist_tag_name(enum bloblist_tag_t tag)
59 {
60         int i;
61
62         for (i = 0; i < ARRAY_SIZE(tag_name); i++) {
63                 if (tag_name[i].tag == tag)
64                         return tag_name[i].name;
65         }
66
67         return "invalid";
68 }
69
70 static struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr)
71 {
72         if (hdr->alloced <= hdr->hdr_size)
73                 return NULL;
74         return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size);
75 }
76
77 static ulong bloblist_blob_end_ofs(struct bloblist_hdr *hdr,
78                                    struct bloblist_rec *rec)
79 {
80         ulong offset;
81
82         offset = (void *)rec - (void *)hdr;
83         offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN);
84
85         return offset;
86 }
87
88 static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr,
89                                                struct bloblist_rec *rec)
90 {
91         ulong offset = bloblist_blob_end_ofs(hdr, rec);
92
93         if (offset >= hdr->alloced)
94                 return NULL;
95         return (struct bloblist_rec *)((void *)hdr + offset);
96 }
97
98 #define foreach_rec(_rec, _hdr) \
99         for (_rec = bloblist_first_blob(_hdr); \
100              _rec; \
101              _rec = bloblist_next_blob(_hdr, _rec))
102
103 static struct bloblist_rec *bloblist_findrec(uint tag)
104 {
105         struct bloblist_hdr *hdr = gd->bloblist;
106         struct bloblist_rec *rec;
107
108         if (!hdr)
109                 return NULL;
110
111         foreach_rec(rec, hdr) {
112                 if (rec->tag == tag)
113                         return rec;
114         }
115
116         return NULL;
117 }
118
119 static int bloblist_addrec(uint tag, int size, int align,
120                            struct bloblist_rec **recp)
121 {
122         struct bloblist_hdr *hdr = gd->bloblist;
123         struct bloblist_rec *rec;
124         int data_start, new_alloced;
125
126         if (!align)
127                 align = BLOBLIST_ALIGN;
128
129         /* Figure out where the new data will start */
130         data_start = map_to_sysmem(hdr) + hdr->alloced + sizeof(*rec);
131
132         /* Align the address and then calculate the offset from ->alloced */
133         data_start = ALIGN(data_start, align) - map_to_sysmem(hdr);
134
135         /* Calculate the new allocated total */
136         new_alloced = data_start + ALIGN(size, align);
137
138         if (new_alloced > hdr->size) {
139                 log_err("Failed to allocate %x bytes size=%x, need size=%x\n",
140                         size, hdr->size, new_alloced);
141                 return log_msg_ret("bloblist add", -ENOSPC);
142         }
143         rec = (void *)hdr + hdr->alloced;
144
145         rec->tag = tag;
146         rec->hdr_size = data_start - hdr->alloced;
147         rec->size = size;
148         rec->spare = 0;
149
150         /* Zero the record data */
151         memset((void *)rec + rec->hdr_size, '\0', rec->size);
152
153         hdr->alloced = new_alloced;
154         *recp = rec;
155
156         return 0;
157 }
158
159 static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size,
160                               int align)
161 {
162         struct bloblist_rec *rec;
163
164         rec = bloblist_findrec(tag);
165         if (rec) {
166                 if (size && size != rec->size) {
167                         *recp = rec;
168                         return -ESPIPE;
169                 }
170         } else {
171                 int ret;
172
173                 ret = bloblist_addrec(tag, size, align, &rec);
174                 if (ret)
175                         return ret;
176         }
177         *recp = rec;
178
179         return 0;
180 }
181
182 void *bloblist_find(uint tag, int size)
183 {
184         struct bloblist_rec *rec;
185
186         rec = bloblist_findrec(tag);
187         if (!rec)
188                 return NULL;
189         if (size && size != rec->size)
190                 return NULL;
191
192         return (void *)rec + rec->hdr_size;
193 }
194
195 void *bloblist_add(uint tag, int size, int align)
196 {
197         struct bloblist_rec *rec;
198
199         if (bloblist_addrec(tag, size, align, &rec))
200                 return NULL;
201
202         return (void *)rec + rec->hdr_size;
203 }
204
205 int bloblist_ensure_size(uint tag, int size, int align, void **blobp)
206 {
207         struct bloblist_rec *rec;
208         int ret;
209
210         ret = bloblist_ensurerec(tag, &rec, size, align);
211         if (ret)
212                 return ret;
213         *blobp = (void *)rec + rec->hdr_size;
214
215         return 0;
216 }
217
218 void *bloblist_ensure(uint tag, int size)
219 {
220         struct bloblist_rec *rec;
221
222         if (bloblist_ensurerec(tag, &rec, size, 0))
223                 return NULL;
224
225         return (void *)rec + rec->hdr_size;
226 }
227
228 int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp)
229 {
230         struct bloblist_rec *rec;
231         int ret;
232
233         ret = bloblist_ensurerec(tag, &rec, *sizep, 0);
234         if (ret == -ESPIPE)
235                 *sizep = rec->size;
236         else if (ret)
237                 return ret;
238         *blobp = (void *)rec + rec->hdr_size;
239
240         return 0;
241 }
242
243 static int bloblist_resize_rec(struct bloblist_hdr *hdr,
244                                struct bloblist_rec *rec,
245                                int new_size)
246 {
247         int expand_by;  /* Number of bytes to expand by (-ve to contract) */
248         int new_alloced;        /* New value for @hdr->alloced */
249         ulong next_ofs; /* Offset of the record after @rec */
250
251         expand_by = ALIGN(new_size - rec->size, BLOBLIST_ALIGN);
252         new_alloced = ALIGN(hdr->alloced + expand_by, BLOBLIST_ALIGN);
253         if (new_size < 0) {
254                 log_debug("Attempt to shrink blob size below 0 (%x)\n",
255                           new_size);
256                 return log_msg_ret("size", -EINVAL);
257         }
258         if (new_alloced > hdr->size) {
259                 log_err("Failed to allocate %x bytes size=%x, need size=%x\n",
260                         new_size, hdr->size, new_alloced);
261                 return log_msg_ret("alloc", -ENOSPC);
262         }
263
264         /* Move the following blobs up or down, if this is not the last */
265         next_ofs = bloblist_blob_end_ofs(hdr, rec);
266         if (next_ofs != hdr->alloced) {
267                 memmove((void *)hdr + next_ofs + expand_by,
268                         (void *)hdr + next_ofs, new_alloced - next_ofs);
269         }
270         hdr->alloced = new_alloced;
271
272         /* Zero the new part of the blob */
273         if (expand_by > 0) {
274                 memset((void *)rec + rec->hdr_size + rec->size, '\0',
275                        new_size - rec->size);
276         }
277
278         /* Update the size of this blob */
279         rec->size = new_size;
280
281         return 0;
282 }
283
284 int bloblist_resize(uint tag, int new_size)
285 {
286         struct bloblist_hdr *hdr = gd->bloblist;
287         struct bloblist_rec *rec;
288         int ret;
289
290         rec = bloblist_findrec(tag);
291         if (!rec)
292                 return log_msg_ret("find", -ENOENT);
293         ret = bloblist_resize_rec(hdr, rec, new_size);
294         if (ret)
295                 return log_msg_ret("resize", ret);
296
297         return 0;
298 }
299
300 static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr)
301 {
302         struct bloblist_rec *rec;
303         u32 chksum;
304
305         chksum = crc32(0, (unsigned char *)hdr,
306                        offsetof(struct bloblist_hdr, chksum));
307         foreach_rec(rec, hdr) {
308                 chksum = crc32(chksum, (void *)rec, rec->hdr_size);
309                 chksum = crc32(chksum, (void *)rec + rec->hdr_size, rec->size);
310         }
311
312         return chksum;
313 }
314
315 int bloblist_new(ulong addr, uint size, uint flags)
316 {
317         struct bloblist_hdr *hdr;
318
319         if (size < sizeof(*hdr))
320                 return log_ret(-ENOSPC);
321         if (addr & (BLOBLIST_ALIGN - 1))
322                 return log_ret(-EFAULT);
323         hdr = map_sysmem(addr, size);
324         memset(hdr, '\0', sizeof(*hdr));
325         hdr->version = BLOBLIST_VERSION;
326         hdr->hdr_size = sizeof(*hdr);
327         hdr->flags = flags;
328         hdr->magic = BLOBLIST_MAGIC;
329         hdr->size = size;
330         hdr->alloced = hdr->hdr_size;
331         hdr->chksum = 0;
332         gd->bloblist = hdr;
333
334         return 0;
335 }
336
337 int bloblist_check(ulong addr, uint size)
338 {
339         struct bloblist_hdr *hdr;
340         u32 chksum;
341
342         hdr = map_sysmem(addr, sizeof(*hdr));
343         if (hdr->magic != BLOBLIST_MAGIC)
344                 return log_msg_ret("Bad magic", -ENOENT);
345         if (hdr->version != BLOBLIST_VERSION)
346                 return log_msg_ret("Bad version", -EPROTONOSUPPORT);
347         if (size && hdr->size != size)
348                 return log_msg_ret("Bad size", -EFBIG);
349         chksum = bloblist_calc_chksum(hdr);
350         if (hdr->chksum != chksum) {
351                 log_err("Checksum %x != %x\n", hdr->chksum, chksum);
352                 return log_msg_ret("Bad checksum", -EIO);
353         }
354         gd->bloblist = hdr;
355
356         return 0;
357 }
358
359 int bloblist_finish(void)
360 {
361         struct bloblist_hdr *hdr = gd->bloblist;
362
363         hdr->chksum = bloblist_calc_chksum(hdr);
364         log_debug("Finished bloblist size %lx at %lx\n", (ulong)hdr->size,
365                   (ulong)map_to_sysmem(hdr));
366
367         return 0;
368 }
369
370 ulong bloblist_get_base(void)
371 {
372         return map_to_sysmem(gd->bloblist);
373 }
374
375 ulong bloblist_get_size(void)
376 {
377         struct bloblist_hdr *hdr = gd->bloblist;
378
379         return hdr->size;
380 }
381
382 void bloblist_get_stats(ulong *basep, ulong *sizep, ulong *allocedp)
383 {
384         struct bloblist_hdr *hdr = gd->bloblist;
385
386         *basep = map_to_sysmem(gd->bloblist);
387         *sizep = hdr->size;
388         *allocedp = hdr->alloced;
389 }
390
391 static void show_value(const char *prompt, ulong value)
392 {
393         printf("%s:%*s %-5lx  ", prompt, 8 - (int)strlen(prompt), "", value);
394         print_size(value, "\n");
395 }
396
397 void bloblist_show_stats(void)
398 {
399         ulong base, size, alloced;
400
401         bloblist_get_stats(&base, &size, &alloced);
402         printf("base:     %lx\n", base);
403         show_value("size", size);
404         show_value("alloced", alloced);
405         show_value("free", size - alloced);
406 }
407
408 void bloblist_show_list(void)
409 {
410         struct bloblist_hdr *hdr = gd->bloblist;
411         struct bloblist_rec *rec;
412
413         printf("%-8s  %8s   Tag Name\n", "Address", "Size");
414         for (rec = bloblist_first_blob(hdr); rec;
415              rec = bloblist_next_blob(hdr, rec)) {
416                 printf("%08lx  %8x  %4x %s\n",
417                        (ulong)map_to_sysmem((void *)rec + rec->hdr_size),
418                        rec->size, rec->tag, bloblist_tag_name(rec->tag));
419         }
420 }
421
422 void bloblist_reloc(void *to, uint to_size, void *from, uint from_size)
423 {
424         struct bloblist_hdr *hdr;
425
426         memcpy(to, from, from_size);
427         hdr = to;
428         hdr->size = to_size;
429 }
430
431 int bloblist_init(void)
432 {
433         int ret = -ENOENT;
434         ulong addr, size;
435         bool expected;
436
437         /**
438          * Wed expect to find an existing bloblist in the first phase of U-Boot
439          * that runs
440          */
441         expected = !u_boot_first_phase();
442         if (spl_prev_phase() == PHASE_TPL && !IS_ENABLED(CONFIG_TPL_BLOBLIST))
443                 expected = false;
444         addr = bloblist_addr();
445         size = CONFIG_BLOBLIST_SIZE;
446         if (expected) {
447                 ret = bloblist_check(addr, size);
448                 if (ret) {
449                         log_warning("Expected bloblist at %lx not found (err=%d)\n",
450                                     addr, ret);
451                 } else {
452                         /* Get the real size, if it is not what we expected */
453                         size = gd->bloblist->size;
454                 }
455         }
456         if (ret) {
457                 if (CONFIG_IS_ENABLED(BLOBLIST_ALLOC)) {
458                         void *ptr = memalign(BLOBLIST_ALIGN, size);
459
460                         if (!ptr)
461                                 return log_msg_ret("alloc", -ENOMEM);
462                         addr = map_to_sysmem(ptr);
463                 }
464                 log_debug("Creating new bloblist size %lx at %lx\n", size,
465                           addr);
466                 ret = bloblist_new(addr, size, 0);
467         } else {
468                 log_debug("Found existing bloblist size %lx at %lx\n", size,
469                           addr);
470         }
471
472         return ret;
473 }