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