colibri_imx6: fix video stdout in default environment
[oweals/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) + 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
77         /* Zero the record data */
78         memset(rec + 1, '\0', rec->size);
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) {
90                 if (size && size != rec->size) {
91                         *recp = rec;
92                         return -ESPIPE;
93                 }
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
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
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 }