xref: /openbmc/u-boot/common/bloblist.c (revision c83c436d)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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