Add core support for a bloblist to convey data from SPL
[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
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 }