Merge branch '2020-10-23-misc-changes'
[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 <mapmem.h>
11 #include <spl.h>
12 #include <u-boot/crc.h>
13
14 /*
15  * A bloblist is a single contiguous chunk of memory with a header
16  * (struct bloblist_hdr) and a number of blobs in it.
17  *
18  * Each blob starts on a BLOBLIST_ALIGN boundary relative to the start of the
19  * bloblist and consists of a struct bloblist_rec, some padding to the required
20  * alignment for the blog and then the actual data. The padding ensures that the
21  * start address of the data in each blob is aligned as required. Note that
22  * each blob's *data* is aligned to BLOBLIST_ALIGN regardless of the alignment
23  * of the bloblist itself or the blob header.
24  *
25  * So far, only BLOBLIST_ALIGN alignment is supported.
26  */
27
28 DECLARE_GLOBAL_DATA_PTR;
29
30 static const char *const tag_name[] = {
31         [BLOBLISTT_NONE]                = "(none)",
32         [BLOBLISTT_EC_HOSTEVENT]        = "EC host event",
33         [BLOBLISTT_SPL_HANDOFF]         = "SPL hand-off",
34         [BLOBLISTT_VBOOT_CTX]           = "Chrome OS vboot context",
35         [BLOBLISTT_VBOOT_HANDOFF]       = "Chrome OS vboot hand-off",
36 };
37
38 const char *bloblist_tag_name(enum bloblist_tag_t tag)
39 {
40         if (tag < 0 || tag >= BLOBLISTT_COUNT)
41                 return "invalid";
42
43         return tag_name[tag];
44 }
45
46 static struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr)
47 {
48         if (hdr->alloced <= hdr->hdr_size)
49                 return NULL;
50         return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size);
51 }
52
53 static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr,
54                                                struct bloblist_rec *rec)
55 {
56         ulong offset;
57
58         offset = (void *)rec - (void *)hdr;
59         offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN);
60         if (offset >= hdr->alloced)
61                 return NULL;
62         return (struct bloblist_rec *)((void *)hdr + offset);
63 }
64
65 #define foreach_rec(_rec, _hdr) \
66         for (_rec = bloblist_first_blob(_hdr); \
67              _rec; \
68              _rec = bloblist_next_blob(_hdr, _rec))
69
70 static struct bloblist_rec *bloblist_findrec(uint tag)
71 {
72         struct bloblist_hdr *hdr = gd->bloblist;
73         struct bloblist_rec *rec;
74
75         if (!hdr)
76                 return NULL;
77
78         foreach_rec(rec, hdr) {
79                 if (rec->tag == tag)
80                         return rec;
81         }
82
83         return NULL;
84 }
85
86 static int bloblist_addrec(uint tag, int size, int align,
87                            struct bloblist_rec **recp)
88 {
89         struct bloblist_hdr *hdr = gd->bloblist;
90         struct bloblist_rec *rec;
91         int data_start, new_alloced;
92
93         if (!align)
94                 align = BLOBLIST_ALIGN;
95
96         /* Figure out where the new data will start */
97         data_start = map_to_sysmem(hdr) + hdr->alloced + sizeof(*rec);
98
99         /* Align the address and then calculate the offset from ->alloced */
100         data_start = ALIGN(data_start, align) - map_to_sysmem(hdr);
101
102         /* Calculate the new allocated total */
103         new_alloced = data_start + ALIGN(size, align);
104
105         if (new_alloced >= hdr->size) {
106                 log(LOGC_BLOBLIST, LOGL_ERR,
107                     "Failed to allocate %x bytes size=%x, need size=%x\n",
108                     size, hdr->size, new_alloced);
109                 return log_msg_ret("bloblist add", -ENOSPC);
110         }
111         rec = (void *)hdr + hdr->alloced;
112
113         rec->tag = tag;
114         rec->hdr_size = data_start - hdr->alloced;
115         rec->size = size;
116         rec->spare = 0;
117
118         /* Zero the record data */
119         memset((void *)rec + rec->hdr_size, '\0', rec->size);
120
121         hdr->alloced = new_alloced;
122         *recp = rec;
123
124         return 0;
125 }
126
127 static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size,
128                               int align)
129 {
130         struct bloblist_rec *rec;
131
132         rec = bloblist_findrec(tag);
133         if (rec) {
134                 if (size && size != rec->size) {
135                         *recp = rec;
136                         return -ESPIPE;
137                 }
138         } else {
139                 int ret;
140
141                 ret = bloblist_addrec(tag, size, align, &rec);
142                 if (ret)
143                         return ret;
144         }
145         *recp = rec;
146
147         return 0;
148 }
149
150 void *bloblist_find(uint tag, int size)
151 {
152         struct bloblist_rec *rec;
153
154         rec = bloblist_findrec(tag);
155         if (!rec)
156                 return NULL;
157         if (size && size != rec->size)
158                 return NULL;
159
160         return (void *)rec + rec->hdr_size;
161 }
162
163 void *bloblist_add(uint tag, int size, int align)
164 {
165         struct bloblist_rec *rec;
166
167         if (bloblist_addrec(tag, size, align, &rec))
168                 return NULL;
169
170         return (void *)rec + rec->hdr_size;
171 }
172
173 int bloblist_ensure_size(uint tag, int size, int align, void **blobp)
174 {
175         struct bloblist_rec *rec;
176         int ret;
177
178         ret = bloblist_ensurerec(tag, &rec, size, align);
179         if (ret)
180                 return ret;
181         *blobp = (void *)rec + rec->hdr_size;
182
183         return 0;
184 }
185
186 void *bloblist_ensure(uint tag, int size)
187 {
188         struct bloblist_rec *rec;
189
190         if (bloblist_ensurerec(tag, &rec, size, 0))
191                 return NULL;
192
193         return (void *)rec + rec->hdr_size;
194 }
195
196 int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp)
197 {
198         struct bloblist_rec *rec;
199         int ret;
200
201         ret = bloblist_ensurerec(tag, &rec, *sizep, 0);
202         if (ret == -ESPIPE)
203                 *sizep = rec->size;
204         else if (ret)
205                 return ret;
206         *blobp = (void *)rec + rec->hdr_size;
207
208         return 0;
209 }
210
211 static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr)
212 {
213         struct bloblist_rec *rec;
214         u32 chksum;
215
216         chksum = crc32(0, (unsigned char *)hdr,
217                        offsetof(struct bloblist_hdr, chksum));
218         foreach_rec(rec, hdr) {
219                 chksum = crc32(chksum, (void *)rec, rec->hdr_size);
220                 chksum = crc32(chksum, (void *)rec + rec->hdr_size, rec->size);
221         }
222
223         return chksum;
224 }
225
226 int bloblist_new(ulong addr, uint size, uint flags)
227 {
228         struct bloblist_hdr *hdr;
229
230         if (size < sizeof(*hdr))
231                 return log_ret(-ENOSPC);
232         if (addr & (BLOBLIST_ALIGN - 1))
233                 return log_ret(-EFAULT);
234         hdr = map_sysmem(addr, size);
235         memset(hdr, '\0', sizeof(*hdr));
236         hdr->version = BLOBLIST_VERSION;
237         hdr->hdr_size = sizeof(*hdr);
238         hdr->flags = flags;
239         hdr->magic = BLOBLIST_MAGIC;
240         hdr->size = size;
241         hdr->alloced = hdr->hdr_size;
242         hdr->chksum = 0;
243         gd->bloblist = hdr;
244
245         return 0;
246 }
247
248 int bloblist_check(ulong addr, uint size)
249 {
250         struct bloblist_hdr *hdr;
251         u32 chksum;
252
253         hdr = map_sysmem(addr, sizeof(*hdr));
254         if (hdr->magic != BLOBLIST_MAGIC)
255                 return log_msg_ret("Bad magic", -ENOENT);
256         if (hdr->version != BLOBLIST_VERSION)
257                 return log_msg_ret("Bad version", -EPROTONOSUPPORT);
258         if (size && hdr->size != size)
259                 return log_msg_ret("Bad size", -EFBIG);
260         chksum = bloblist_calc_chksum(hdr);
261         if (hdr->chksum != chksum) {
262                 log(LOGC_BLOBLIST, LOGL_ERR, "Checksum %x != %x\n", hdr->chksum,
263                     chksum);
264                 return log_msg_ret("Bad checksum", -EIO);
265         }
266         gd->bloblist = hdr;
267
268         return 0;
269 }
270
271 int bloblist_finish(void)
272 {
273         struct bloblist_hdr *hdr = gd->bloblist;
274
275         hdr->chksum = bloblist_calc_chksum(hdr);
276
277         return 0;
278 }
279
280 void bloblist_get_stats(ulong *basep, ulong *sizep, ulong *allocedp)
281 {
282         struct bloblist_hdr *hdr = gd->bloblist;
283
284         *basep = map_to_sysmem(gd->bloblist);
285         *sizep = hdr->size;
286         *allocedp = hdr->alloced;
287 }
288
289 static void show_value(const char *prompt, ulong value)
290 {
291         printf("%s:%*s %-5lx  ", prompt, 8 - (int)strlen(prompt), "", value);
292         print_size(value, "\n");
293 }
294
295 void bloblist_show_stats(void)
296 {
297         ulong base, size, alloced;
298
299         bloblist_get_stats(&base, &size, &alloced);
300         printf("base:     %lx\n", base);
301         show_value("size", size);
302         show_value("alloced", alloced);
303         show_value("free", size - alloced);
304 }
305
306 void bloblist_show_list(void)
307 {
308         struct bloblist_hdr *hdr = gd->bloblist;
309         struct bloblist_rec *rec;
310
311         printf("%-8s  %8s  Tag Name\n", "Address", "Size");
312         for (rec = bloblist_first_blob(hdr); rec;
313              rec = bloblist_next_blob(hdr, rec)) {
314                 printf("%08lx  %8x  %3d %s\n",
315                        (ulong)map_to_sysmem((void *)rec + rec->hdr_size),
316                        rec->size, rec->tag, bloblist_tag_name(rec->tag));
317         }
318 }
319
320 int bloblist_init(void)
321 {
322         bool expected;
323         int ret = -ENOENT;
324
325         /**
326          * Wed expect to find an existing bloblist in the first phase of U-Boot
327          * that runs
328          */
329         expected = !u_boot_first_phase();
330         if (expected)
331                 ret = bloblist_check(CONFIG_BLOBLIST_ADDR,
332                                      CONFIG_BLOBLIST_SIZE);
333         if (ret) {
334                 log(LOGC_BLOBLIST, expected ? LOGL_WARNING : LOGL_DEBUG,
335                     "Existing bloblist not found: creating new bloblist\n");
336                 ret = bloblist_new(CONFIG_BLOBLIST_ADDR, CONFIG_BLOBLIST_SIZE,
337                                    0);
338         } else {
339                 log(LOGC_BLOBLIST, LOGL_DEBUG, "Found existing bloblist\n");
340         }
341
342         return ret;
343 }