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