blob: 173f28d8ec9ec9339bc7e537ab3b4ed2ff1f538e [file] [log] [blame]
Simon Glass9f407d42018-11-15 18:43:50 -07001// 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>
Simon Glass3db71102019-11-14 12:57:16 -070012#include <u-boot/crc.h>
Simon Glass9f407d42018-11-15 18:43:50 -070013
Simon Glass751b7c72020-09-19 18:49:28 -060014/*
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
Simon Glass9f407d42018-11-15 18:43:50 -070028DECLARE_GLOBAL_DATA_PTR;
29
Simon Glass4aed2272020-09-19 18:49:26 -060030static 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
38const 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
46static struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr)
Simon Glass9f407d42018-11-15 18:43:50 -070047{
48 if (hdr->alloced <= hdr->hdr_size)
49 return NULL;
50 return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size);
51}
52
Simon Glass4aed2272020-09-19 18:49:26 -060053static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr,
54 struct bloblist_rec *rec)
Simon Glass9f407d42018-11-15 18:43:50 -070055{
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
70static 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
86static int bloblist_addrec(uint tag, int size, struct bloblist_rec **recp)
87{
88 struct bloblist_hdr *hdr = gd->bloblist;
89 struct bloblist_rec *rec;
Simon Glass751b7c72020-09-19 18:49:28 -060090 int data_start, new_alloced;
Simon Glass9f407d42018-11-15 18:43:50 -070091
Simon Glass751b7c72020-09-19 18:49:28 -060092 /* Figure out where the new data will start */
93 data_start = hdr->alloced + sizeof(*rec);
94 data_start = ALIGN(data_start, BLOBLIST_ALIGN);
95
96 /* Calculate the new allocated total */
97 new_alloced = data_start + ALIGN(size, BLOBLIST_ALIGN);
Simon Glass9f407d42018-11-15 18:43:50 -070098 if (new_alloced >= hdr->size) {
99 log(LOGC_BLOBLIST, LOGL_ERR,
Simon Glass02247c12020-01-27 08:49:51 -0700100 "Failed to allocate %x bytes size=%x, need size=%x\n",
Simon Glass9f407d42018-11-15 18:43:50 -0700101 size, hdr->size, new_alloced);
102 return log_msg_ret("bloblist add", -ENOSPC);
103 }
104 rec = (void *)hdr + hdr->alloced;
Simon Glass9f407d42018-11-15 18:43:50 -0700105
106 rec->tag = tag;
Simon Glass751b7c72020-09-19 18:49:28 -0600107 rec->hdr_size = data_start - hdr->alloced;
Simon Glass9f407d42018-11-15 18:43:50 -0700108 rec->size = size;
109 rec->spare = 0;
Simon Glassb83994d2020-01-27 08:49:52 -0700110
111 /* Zero the record data */
Simon Glass751b7c72020-09-19 18:49:28 -0600112 memset((void *)rec + rec->hdr_size, '\0', rec->size);
113
114 hdr->alloced = new_alloced;
Simon Glass9f407d42018-11-15 18:43:50 -0700115 *recp = rec;
116
117 return 0;
118}
119
120static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size)
121{
122 struct bloblist_rec *rec;
123
124 rec = bloblist_findrec(tag);
125 if (rec) {
Simon Glass5b044542020-01-27 08:49:50 -0700126 if (size && size != rec->size) {
127 *recp = rec;
Simon Glass9f407d42018-11-15 18:43:50 -0700128 return -ESPIPE;
Simon Glass5b044542020-01-27 08:49:50 -0700129 }
Simon Glass9f407d42018-11-15 18:43:50 -0700130 } else {
131 int ret;
132
133 ret = bloblist_addrec(tag, size, &rec);
134 if (ret)
135 return ret;
136 }
137 *recp = rec;
138
139 return 0;
140}
141
142void *bloblist_find(uint tag, int size)
143{
144 struct bloblist_rec *rec;
145
146 rec = bloblist_findrec(tag);
147 if (!rec)
148 return NULL;
149 if (size && size != rec->size)
150 return NULL;
151
152 return (void *)rec + rec->hdr_size;
153}
154
155void *bloblist_add(uint tag, int size)
156{
157 struct bloblist_rec *rec;
158
159 if (bloblist_addrec(tag, size, &rec))
160 return NULL;
161
Simon Glass751b7c72020-09-19 18:49:28 -0600162 return (void *)rec + rec->hdr_size;
Simon Glass9f407d42018-11-15 18:43:50 -0700163}
164
165int bloblist_ensure_size(uint tag, int size, void **blobp)
166{
167 struct bloblist_rec *rec;
168 int ret;
169
170 ret = bloblist_ensurerec(tag, &rec, size);
171 if (ret)
172 return ret;
173 *blobp = (void *)rec + rec->hdr_size;
174
175 return 0;
176}
177
178void *bloblist_ensure(uint tag, int size)
179{
180 struct bloblist_rec *rec;
181
182 if (bloblist_ensurerec(tag, &rec, size))
183 return NULL;
184
185 return (void *)rec + rec->hdr_size;
186}
187
Simon Glass5b044542020-01-27 08:49:50 -0700188int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp)
189{
190 struct bloblist_rec *rec;
191 int ret;
192
193 ret = bloblist_ensurerec(tag, &rec, *sizep);
194 if (ret == -ESPIPE)
195 *sizep = rec->size;
196 else if (ret)
197 return ret;
198 *blobp = (void *)rec + rec->hdr_size;
199
200 return 0;
201}
202
Simon Glass9f407d42018-11-15 18:43:50 -0700203static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr)
204{
205 struct bloblist_rec *rec;
206 u32 chksum;
207
208 chksum = crc32(0, (unsigned char *)hdr,
209 offsetof(struct bloblist_hdr, chksum));
210 foreach_rec(rec, hdr) {
211 chksum = crc32(chksum, (void *)rec, rec->hdr_size);
212 chksum = crc32(chksum, (void *)rec + rec->hdr_size, rec->size);
213 }
214
215 return chksum;
216}
217
218int bloblist_new(ulong addr, uint size, uint flags)
219{
220 struct bloblist_hdr *hdr;
221
222 if (size < sizeof(*hdr))
223 return log_ret(-ENOSPC);
224 if (addr & (BLOBLIST_ALIGN - 1))
225 return log_ret(-EFAULT);
226 hdr = map_sysmem(addr, size);
227 memset(hdr, '\0', sizeof(*hdr));
228 hdr->version = BLOBLIST_VERSION;
229 hdr->hdr_size = sizeof(*hdr);
230 hdr->flags = flags;
231 hdr->magic = BLOBLIST_MAGIC;
232 hdr->size = size;
233 hdr->alloced = hdr->hdr_size;
234 hdr->chksum = 0;
235 gd->bloblist = hdr;
236
237 return 0;
238}
239
240int bloblist_check(ulong addr, uint size)
241{
242 struct bloblist_hdr *hdr;
243 u32 chksum;
244
245 hdr = map_sysmem(addr, sizeof(*hdr));
246 if (hdr->magic != BLOBLIST_MAGIC)
247 return log_msg_ret("Bad magic", -ENOENT);
248 if (hdr->version != BLOBLIST_VERSION)
249 return log_msg_ret("Bad version", -EPROTONOSUPPORT);
250 if (size && hdr->size != size)
251 return log_msg_ret("Bad size", -EFBIG);
252 chksum = bloblist_calc_chksum(hdr);
253 if (hdr->chksum != chksum) {
254 log(LOGC_BLOBLIST, LOGL_ERR, "Checksum %x != %x\n", hdr->chksum,
255 chksum);
256 return log_msg_ret("Bad checksum", -EIO);
257 }
258 gd->bloblist = hdr;
259
260 return 0;
261}
262
263int bloblist_finish(void)
264{
265 struct bloblist_hdr *hdr = gd->bloblist;
266
267 hdr->chksum = bloblist_calc_chksum(hdr);
268
269 return 0;
270}
271
Simon Glass4aed2272020-09-19 18:49:26 -0600272void bloblist_get_stats(ulong *basep, ulong *sizep, ulong *allocedp)
273{
274 struct bloblist_hdr *hdr = gd->bloblist;
275
276 *basep = map_to_sysmem(gd->bloblist);
277 *sizep = hdr->size;
278 *allocedp = hdr->alloced;
279}
280
281static void show_value(const char *prompt, ulong value)
282{
283 printf("%s:%*s %-5lx ", prompt, 8 - (int)strlen(prompt), "", value);
284 print_size(value, "\n");
285}
286
287void bloblist_show_stats(void)
288{
289 ulong base, size, alloced;
290
291 bloblist_get_stats(&base, &size, &alloced);
292 printf("base: %lx\n", base);
293 show_value("size", size);
294 show_value("alloced", alloced);
295 show_value("free", size - alloced);
296}
297
298void bloblist_show_list(void)
299{
300 struct bloblist_hdr *hdr = gd->bloblist;
301 struct bloblist_rec *rec;
302
303 printf("%-8s %8s Tag Name\n", "Address", "Size");
304 for (rec = bloblist_first_blob(hdr); rec;
305 rec = bloblist_next_blob(hdr, rec)) {
306 printf("%08lx %8x %3d %s\n",
307 (ulong)map_to_sysmem((void *)rec + rec->hdr_size),
308 rec->size, rec->tag, bloblist_tag_name(rec->tag));
309 }
310}
311
Simon Glass9f407d42018-11-15 18:43:50 -0700312int bloblist_init(void)
313{
314 bool expected;
315 int ret = -ENOENT;
316
317 /**
318 * Wed expect to find an existing bloblist in the first phase of U-Boot
319 * that runs
320 */
321 expected = !u_boot_first_phase();
322 if (expected)
323 ret = bloblist_check(CONFIG_BLOBLIST_ADDR,
324 CONFIG_BLOBLIST_SIZE);
325 if (ret) {
326 log(LOGC_BLOBLIST, expected ? LOGL_WARNING : LOGL_DEBUG,
327 "Existing bloblist not found: creating new bloblist\n");
328 ret = bloblist_new(CONFIG_BLOBLIST_ADDR, CONFIG_BLOBLIST_SIZE,
329 0);
330 } else {
331 log(LOGC_BLOBLIST, LOGL_DEBUG, "Found existing bloblist\n");
332 }
333
334 return ret;
335}