blob: 3d0fbbf67d31681d3f9bbabbcaf7f013fd72374f [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
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
Simon Glass02247c12020-01-27 08:49:51 -070062 new_alloced = hdr->alloced + sizeof(*rec) + ALIGN(size, BLOBLIST_ALIGN);
Simon Glass9f407d42018-11-15 18:43:50 -070063 if (new_alloced >= hdr->size) {
64 log(LOGC_BLOBLIST, LOGL_ERR,
Simon Glass02247c12020-01-27 08:49:51 -070065 "Failed to allocate %x bytes size=%x, need size=%x\n",
Simon Glass9f407d42018-11-15 18:43:50 -070066 size, hdr->size, new_alloced);
67 return log_msg_ret("bloblist add", -ENOSPC);
68 }
69 rec = (void *)hdr + hdr->alloced;
70 hdr->alloced = new_alloced;
71
72 rec->tag = tag;
73 rec->hdr_size = sizeof(*rec);
74 rec->size = size;
75 rec->spare = 0;
76 *recp = rec;
77
78 return 0;
79}
80
81static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size)
82{
83 struct bloblist_rec *rec;
84
85 rec = bloblist_findrec(tag);
86 if (rec) {
Simon Glass5b044542020-01-27 08:49:50 -070087 if (size && size != rec->size) {
88 *recp = rec;
Simon Glass9f407d42018-11-15 18:43:50 -070089 return -ESPIPE;
Simon Glass5b044542020-01-27 08:49:50 -070090 }
Simon Glass9f407d42018-11-15 18:43:50 -070091 } else {
92 int ret;
93
94 ret = bloblist_addrec(tag, size, &rec);
95 if (ret)
96 return ret;
97 }
98 *recp = rec;
99
100 return 0;
101}
102
103void *bloblist_find(uint tag, int size)
104{
105 struct bloblist_rec *rec;
106
107 rec = bloblist_findrec(tag);
108 if (!rec)
109 return NULL;
110 if (size && size != rec->size)
111 return NULL;
112
113 return (void *)rec + rec->hdr_size;
114}
115
116void *bloblist_add(uint tag, int size)
117{
118 struct bloblist_rec *rec;
119
120 if (bloblist_addrec(tag, size, &rec))
121 return NULL;
122
123 return rec + 1;
124}
125
126int bloblist_ensure_size(uint tag, int size, void **blobp)
127{
128 struct bloblist_rec *rec;
129 int ret;
130
131 ret = bloblist_ensurerec(tag, &rec, size);
132 if (ret)
133 return ret;
134 *blobp = (void *)rec + rec->hdr_size;
135
136 return 0;
137}
138
139void *bloblist_ensure(uint tag, int size)
140{
141 struct bloblist_rec *rec;
142
143 if (bloblist_ensurerec(tag, &rec, size))
144 return NULL;
145
146 return (void *)rec + rec->hdr_size;
147}
148
Simon Glass5b044542020-01-27 08:49:50 -0700149int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp)
150{
151 struct bloblist_rec *rec;
152 int ret;
153
154 ret = bloblist_ensurerec(tag, &rec, *sizep);
155 if (ret == -ESPIPE)
156 *sizep = rec->size;
157 else if (ret)
158 return ret;
159 *blobp = (void *)rec + rec->hdr_size;
160
161 return 0;
162}
163
Simon Glass9f407d42018-11-15 18:43:50 -0700164static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr)
165{
166 struct bloblist_rec *rec;
167 u32 chksum;
168
169 chksum = crc32(0, (unsigned char *)hdr,
170 offsetof(struct bloblist_hdr, chksum));
171 foreach_rec(rec, hdr) {
172 chksum = crc32(chksum, (void *)rec, rec->hdr_size);
173 chksum = crc32(chksum, (void *)rec + rec->hdr_size, rec->size);
174 }
175
176 return chksum;
177}
178
179int bloblist_new(ulong addr, uint size, uint flags)
180{
181 struct bloblist_hdr *hdr;
182
183 if (size < sizeof(*hdr))
184 return log_ret(-ENOSPC);
185 if (addr & (BLOBLIST_ALIGN - 1))
186 return log_ret(-EFAULT);
187 hdr = map_sysmem(addr, size);
188 memset(hdr, '\0', sizeof(*hdr));
189 hdr->version = BLOBLIST_VERSION;
190 hdr->hdr_size = sizeof(*hdr);
191 hdr->flags = flags;
192 hdr->magic = BLOBLIST_MAGIC;
193 hdr->size = size;
194 hdr->alloced = hdr->hdr_size;
195 hdr->chksum = 0;
196 gd->bloblist = hdr;
197
198 return 0;
199}
200
201int bloblist_check(ulong addr, uint size)
202{
203 struct bloblist_hdr *hdr;
204 u32 chksum;
205
206 hdr = map_sysmem(addr, sizeof(*hdr));
207 if (hdr->magic != BLOBLIST_MAGIC)
208 return log_msg_ret("Bad magic", -ENOENT);
209 if (hdr->version != BLOBLIST_VERSION)
210 return log_msg_ret("Bad version", -EPROTONOSUPPORT);
211 if (size && hdr->size != size)
212 return log_msg_ret("Bad size", -EFBIG);
213 chksum = bloblist_calc_chksum(hdr);
214 if (hdr->chksum != chksum) {
215 log(LOGC_BLOBLIST, LOGL_ERR, "Checksum %x != %x\n", hdr->chksum,
216 chksum);
217 return log_msg_ret("Bad checksum", -EIO);
218 }
219 gd->bloblist = hdr;
220
221 return 0;
222}
223
224int bloblist_finish(void)
225{
226 struct bloblist_hdr *hdr = gd->bloblist;
227
228 hdr->chksum = bloblist_calc_chksum(hdr);
229
230 return 0;
231}
232
233int bloblist_init(void)
234{
235 bool expected;
236 int ret = -ENOENT;
237
238 /**
239 * Wed expect to find an existing bloblist in the first phase of U-Boot
240 * that runs
241 */
242 expected = !u_boot_first_phase();
243 if (expected)
244 ret = bloblist_check(CONFIG_BLOBLIST_ADDR,
245 CONFIG_BLOBLIST_SIZE);
246 if (ret) {
247 log(LOGC_BLOBLIST, expected ? LOGL_WARNING : LOGL_DEBUG,
248 "Existing bloblist not found: creating new bloblist\n");
249 ret = bloblist_new(CONFIG_BLOBLIST_ADDR, CONFIG_BLOBLIST_SIZE,
250 0);
251 } else {
252 log(LOGC_BLOBLIST, LOGL_DEBUG, "Found existing bloblist\n");
253 }
254
255 return ret;
256}