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