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