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