blob: b4cf169b05a3d2ed9773df3dfb61818597ab861e [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>
12
13DECLARE_GLOBAL_DATA_PTR;
14
15struct 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
22struct 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
39static 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
55static 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
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) {
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
101void *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
114void *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
124int 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
137void *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
147static 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
162int 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
184int 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
207int 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
216int 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}