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