blob: 3599ffa75c0d0599bd02806c5ee19e00f4c6fd85 [file] [log] [blame]
Simon Glass712bd2d2018-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 Glass48b6c6b2019-11-14 12:57:16 -070012#include <u-boot/crc.h>
Simon Glass712bd2d2018-11-15 18:43:50 -070013
14DECLARE_GLOBAL_DATA_PTR;
15
16struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr)
17{
18 if (hdr->alloced <= hdr->hdr_size)
19 return NULL;
20 return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size);
21}
22
23struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr,
24 struct bloblist_rec *rec)
25{
26 ulong offset;
27
28 offset = (void *)rec - (void *)hdr;
29 offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN);
30 if (offset >= hdr->alloced)
31 return NULL;
32 return (struct bloblist_rec *)((void *)hdr + offset);
33}
34
35#define foreach_rec(_rec, _hdr) \
36 for (_rec = bloblist_first_blob(_hdr); \
37 _rec; \
38 _rec = bloblist_next_blob(_hdr, _rec))
39
40static struct bloblist_rec *bloblist_findrec(uint tag)
41{
42 struct bloblist_hdr *hdr = gd->bloblist;
43 struct bloblist_rec *rec;
44
45 if (!hdr)
46 return NULL;
47
48 foreach_rec(rec, hdr) {
49 if (rec->tag == tag)
50 return rec;
51 }
52
53 return NULL;
54}
55
56static int bloblist_addrec(uint tag, int size, struct bloblist_rec **recp)
57{
58 struct bloblist_hdr *hdr = gd->bloblist;
59 struct bloblist_rec *rec;
60 int new_alloced;
61
62 new_alloced = hdr->alloced + sizeof(*rec) +
63 ALIGN(size, BLOBLIST_ALIGN);
64 if (new_alloced >= hdr->size) {
65 log(LOGC_BLOBLIST, LOGL_ERR,
66 "Failed to allocate %x bytes size=%x, need size>=%x\n",
67 size, hdr->size, new_alloced);
68 return log_msg_ret("bloblist add", -ENOSPC);
69 }
70 rec = (void *)hdr + hdr->alloced;
71 hdr->alloced = new_alloced;
72
73 rec->tag = tag;
74 rec->hdr_size = sizeof(*rec);
75 rec->size = size;
76 rec->spare = 0;
77 *recp = rec;
78
79 return 0;
80}
81
82static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size)
83{
84 struct bloblist_rec *rec;
85
86 rec = bloblist_findrec(tag);
87 if (rec) {
Simon Glass1e0304e2020-01-27 08:49:50 -070088 if (size && size != rec->size) {
89 *recp = rec;
Simon Glass712bd2d2018-11-15 18:43:50 -070090 return -ESPIPE;
Simon Glass1e0304e2020-01-27 08:49:50 -070091 }
Simon Glass712bd2d2018-11-15 18:43:50 -070092 } else {
93 int ret;
94
95 ret = bloblist_addrec(tag, size, &rec);
96 if (ret)
97 return ret;
98 }
99 *recp = rec;
100
101 return 0;
102}
103
104void *bloblist_find(uint tag, int size)
105{
106 struct bloblist_rec *rec;
107
108 rec = bloblist_findrec(tag);
109 if (!rec)
110 return NULL;
111 if (size && size != rec->size)
112 return NULL;
113
114 return (void *)rec + rec->hdr_size;
115}
116
117void *bloblist_add(uint tag, int size)
118{
119 struct bloblist_rec *rec;
120
121 if (bloblist_addrec(tag, size, &rec))
122 return NULL;
123
124 return rec + 1;
125}
126
127int bloblist_ensure_size(uint tag, int size, void **blobp)
128{
129 struct bloblist_rec *rec;
130 int ret;
131
132 ret = bloblist_ensurerec(tag, &rec, size);
133 if (ret)
134 return ret;
135 *blobp = (void *)rec + rec->hdr_size;
136
137 return 0;
138}
139
140void *bloblist_ensure(uint tag, int size)
141{
142 struct bloblist_rec *rec;
143
144 if (bloblist_ensurerec(tag, &rec, size))
145 return NULL;
146
147 return (void *)rec + rec->hdr_size;
148}
149
Simon Glass1e0304e2020-01-27 08:49:50 -0700150int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp)
151{
152 struct bloblist_rec *rec;
153 int ret;
154
155 ret = bloblist_ensurerec(tag, &rec, *sizep);
156 if (ret == -ESPIPE)
157 *sizep = rec->size;
158 else if (ret)
159 return ret;
160 *blobp = (void *)rec + rec->hdr_size;
161
162 return 0;
163}
164
Simon Glass712bd2d2018-11-15 18:43:50 -0700165static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr)
166{
167 struct bloblist_rec *rec;
168 u32 chksum;
169
170 chksum = crc32(0, (unsigned char *)hdr,
171 offsetof(struct bloblist_hdr, chksum));
172 foreach_rec(rec, hdr) {
173 chksum = crc32(chksum, (void *)rec, rec->hdr_size);
174 chksum = crc32(chksum, (void *)rec + rec->hdr_size, rec->size);
175 }
176
177 return chksum;
178}
179
180int bloblist_new(ulong addr, uint size, uint flags)
181{
182 struct bloblist_hdr *hdr;
183
184 if (size < sizeof(*hdr))
185 return log_ret(-ENOSPC);
186 if (addr & (BLOBLIST_ALIGN - 1))
187 return log_ret(-EFAULT);
188 hdr = map_sysmem(addr, size);
189 memset(hdr, '\0', sizeof(*hdr));
190 hdr->version = BLOBLIST_VERSION;
191 hdr->hdr_size = sizeof(*hdr);
192 hdr->flags = flags;
193 hdr->magic = BLOBLIST_MAGIC;
194 hdr->size = size;
195 hdr->alloced = hdr->hdr_size;
196 hdr->chksum = 0;
197 gd->bloblist = hdr;
198
199 return 0;
200}
201
202int bloblist_check(ulong addr, uint size)
203{
204 struct bloblist_hdr *hdr;
205 u32 chksum;
206
207 hdr = map_sysmem(addr, sizeof(*hdr));
208 if (hdr->magic != BLOBLIST_MAGIC)
209 return log_msg_ret("Bad magic", -ENOENT);
210 if (hdr->version != BLOBLIST_VERSION)
211 return log_msg_ret("Bad version", -EPROTONOSUPPORT);
212 if (size && hdr->size != size)
213 return log_msg_ret("Bad size", -EFBIG);
214 chksum = bloblist_calc_chksum(hdr);
215 if (hdr->chksum != chksum) {
216 log(LOGC_BLOBLIST, LOGL_ERR, "Checksum %x != %x\n", hdr->chksum,
217 chksum);
218 return log_msg_ret("Bad checksum", -EIO);
219 }
220 gd->bloblist = hdr;
221
222 return 0;
223}
224
225int bloblist_finish(void)
226{
227 struct bloblist_hdr *hdr = gd->bloblist;
228
229 hdr->chksum = bloblist_calc_chksum(hdr);
230
231 return 0;
232}
233
234int bloblist_init(void)
235{
236 bool expected;
237 int ret = -ENOENT;
238
239 /**
240 * Wed expect to find an existing bloblist in the first phase of U-Boot
241 * that runs
242 */
243 expected = !u_boot_first_phase();
244 if (expected)
245 ret = bloblist_check(CONFIG_BLOBLIST_ADDR,
246 CONFIG_BLOBLIST_SIZE);
247 if (ret) {
248 log(LOGC_BLOBLIST, expected ? LOGL_WARNING : LOGL_DEBUG,
249 "Existing bloblist not found: creating new bloblist\n");
250 ret = bloblist_new(CONFIG_BLOBLIST_ADDR, CONFIG_BLOBLIST_SIZE,
251 0);
252 } else {
253 log(LOGC_BLOBLIST, LOGL_DEBUG, "Found existing bloblist\n");
254 }
255
256 return ret;
257}