cbfs: Unify the two header loaders
[platform/kernel/u-boot.git] / fs / cbfs / cbfs.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4  */
5
6 #include <common.h>
7 #include <cbfs.h>
8 #include <malloc.h>
9 #include <asm/byteorder.h>
10
11 /* Offset of master header from the start of a coreboot ROM */
12 #define MASTER_HDR_OFFSET       0x38
13
14 static const u32 good_magic = 0x4f524243;
15 static const u8 good_file_magic[] = "LARCHIVE";
16
17 struct cbfs_priv {
18         bool initialized;
19         struct cbfs_header header;
20         struct cbfs_cachenode *file_cache;
21         enum cbfs_result result;
22 };
23
24 static struct cbfs_priv cbfs_s;
25
26 const char *file_cbfs_error(void)
27 {
28         switch (cbfs_s.result) {
29         case CBFS_SUCCESS:
30                 return "Success";
31         case CBFS_NOT_INITIALIZED:
32                 return "CBFS not initialized";
33         case CBFS_BAD_HEADER:
34                 return "Bad CBFS header";
35         case CBFS_BAD_FILE:
36                 return "Bad CBFS file";
37         case CBFS_FILE_NOT_FOUND:
38                 return "File not found";
39         default:
40                 return "Unknown";
41         }
42 }
43
44 enum cbfs_result cbfs_get_result(void)
45 {
46         return cbfs_s.result;
47 }
48
49 /* Do endian conversion on the CBFS header structure. */
50 static void swap_header(struct cbfs_header *dest, struct cbfs_header *src)
51 {
52         dest->magic = be32_to_cpu(src->magic);
53         dest->version = be32_to_cpu(src->version);
54         dest->rom_size = be32_to_cpu(src->rom_size);
55         dest->boot_block_size = be32_to_cpu(src->boot_block_size);
56         dest->align = be32_to_cpu(src->align);
57         dest->offset = be32_to_cpu(src->offset);
58 }
59
60 /* Do endian conversion on a CBFS file header. */
61 static void swap_file_header(struct cbfs_fileheader *dest,
62                              const struct cbfs_fileheader *src)
63 {
64         memcpy(&dest->magic, &src->magic, sizeof(dest->magic));
65         dest->len = be32_to_cpu(src->len);
66         dest->type = be32_to_cpu(src->type);
67         dest->attributes_offset = be32_to_cpu(src->attributes_offset);
68         dest->offset = be32_to_cpu(src->offset);
69 }
70
71 /*
72  * Given a starting position in memory, scan forward, bounded by a size, and
73  * find the next valid CBFS file. No memory is allocated by this function. The
74  * caller is responsible for allocating space for the new file structure.
75  *
76  * @param start         The location in memory to start from.
77  * @param size          The size of the memory region to search.
78  * @param align         The alignment boundaries to check on.
79  * @param new_node      A pointer to the file structure to load.
80  * @param used          A pointer to the count of of bytes scanned through,
81  *                      including the file if one is found.
82  *
83  * @return 0 if a file is found, -ENOENT if one isn't, -EBADF if a bad header
84  *      is found.
85  */
86 static int file_cbfs_next_file(struct cbfs_priv *priv, u8 *start, int size,
87                                int align, struct cbfs_cachenode *new_node,
88                                int *used)
89 {
90         struct cbfs_fileheader header;
91
92         *used = 0;
93
94         while (size >= align) {
95                 const struct cbfs_fileheader *file_header =
96                         (const struct cbfs_fileheader *)start;
97                 u32 name_len;
98                 u32 step;
99
100                 /* Check if there's a file here. */
101                 if (memcmp(good_file_magic, &file_header->magic,
102                            sizeof(file_header->magic))) {
103                         *used += align;
104                         size -= align;
105                         start += align;
106                         continue;
107                 }
108
109                 swap_file_header(&header, file_header);
110                 if (header.offset < sizeof(struct cbfs_fileheader)) {
111                         priv->result = CBFS_BAD_FILE;
112                         return -EBADF;
113                 }
114                 new_node->next = NULL;
115                 new_node->type = header.type;
116                 new_node->data = start + header.offset;
117                 new_node->data_length = header.len;
118                 name_len = header.offset - sizeof(struct cbfs_fileheader);
119                 new_node->name = (char *)file_header +
120                                 sizeof(struct cbfs_fileheader);
121                 new_node->name_length = name_len;
122                 new_node->attributes_offset = header.attributes_offset;
123
124                 step = header.len;
125                 if (step % align)
126                         step = step + align - step % align;
127
128                 *used += step;
129                 return 0;
130         }
131
132         return -ENOENT;
133 }
134
135 /* Look through a CBFS instance and copy file metadata into regular memory. */
136 static int file_cbfs_fill_cache(struct cbfs_priv *priv, u8 *start, u32 size,
137                                 u32 align)
138 {
139         struct cbfs_cachenode *cache_node;
140         struct cbfs_cachenode *new_node;
141         struct cbfs_cachenode **cache_tail = &priv->file_cache;
142
143         /* Clear out old information. */
144         cache_node = priv->file_cache;
145         while (cache_node) {
146                 struct cbfs_cachenode *old_node = cache_node;
147                 cache_node = cache_node->next;
148                 free(old_node);
149         }
150         priv->file_cache = NULL;
151
152         while (size >= align) {
153                 int used;
154                 int ret;
155
156                 new_node = (struct cbfs_cachenode *)
157                                 malloc(sizeof(struct cbfs_cachenode));
158                 if (!new_node)
159                         return -ENOMEM;
160                 ret = file_cbfs_next_file(priv, start, size, align, new_node,
161                                           &used);
162
163                 if (ret < 0) {
164                         free(new_node);
165                         if (ret == -ENOENT)
166                                 break;
167                         return ret;
168                 }
169                 *cache_tail = new_node;
170                 cache_tail = &new_node->next;
171
172                 size -= used;
173                 start += used;
174         }
175         priv->result = CBFS_SUCCESS;
176
177         return 0;
178 }
179
180 /**
181  * load_header() - Load the CBFS header
182  *
183  * Get the CBFS header out of the ROM and do endian conversion.
184  *
185  * @priv: Private data, which is inited by this function
186  * @addr: Address of CBFS header in memory-mapped SPI flash
187  * @return 0 if OK, -ENXIO if the header is bad
188  */
189 static int load_header(struct cbfs_priv *priv, ulong addr)
190 {
191         struct cbfs_header *header = &priv->header;
192         struct cbfs_header *header_in_rom;
193
194         memset(priv, '\0', sizeof(*priv));
195         header_in_rom = (struct cbfs_header *)addr;
196         swap_header(header, header_in_rom);
197
198         if (header->magic != good_magic || header->offset >
199                         header->rom_size - header->boot_block_size) {
200                 priv->result = CBFS_BAD_HEADER;
201                 return -ENXIO;
202         }
203
204         return 0;
205 }
206
207 /**
208  * file_cbfs_load_header() - Get the CBFS header out of the ROM, given the end
209  *
210  * @priv: Private data, which is inited by this function
211  * @end_of_rom: Address of the last byte of the ROM (typically 0xffffffff)
212  * @return 0 if OK, -ENXIO if the header is bad
213  */
214 static int file_cbfs_load_header(struct cbfs_priv *priv, ulong end_of_rom)
215 {
216         int offset = *(u32 *)(end_of_rom - 3);
217
218         return load_header(priv, end_of_rom + offset + 1);
219 }
220
221 /**
222  * cbfs_load_header_ptr() - Get the CBFS header out of the ROM, given the base
223  *
224  * @priv: Private data, which is inited by this function
225  * @base: Address of the first byte of the ROM (e.g. 0xff000000)
226  * @return 0 if OK, -ENXIO if the header is bad
227  */
228 static int cbfs_load_header_ptr(struct cbfs_priv *priv, ulong base)
229 {
230         return load_header(priv, base + MASTER_HDR_OFFSET);
231 }
232
233 static void cbfs_init(struct cbfs_priv *priv, ulong end_of_rom)
234 {
235         u8 *start_of_rom;
236
237         if (file_cbfs_load_header(priv, end_of_rom))
238                 return;
239
240         start_of_rom = (u8 *)(end_of_rom + 1 - priv->header.rom_size);
241
242         file_cbfs_fill_cache(priv, start_of_rom, priv->header.rom_size,
243                              priv->header.align);
244         if (priv->result == CBFS_SUCCESS)
245                 priv->initialized = true;
246 }
247
248 void file_cbfs_init(ulong end_of_rom)
249 {
250         cbfs_init(&cbfs_s, end_of_rom);
251 }
252
253 int cbfs_init_mem(ulong base, ulong size, struct cbfs_priv **privp)
254 {
255         struct cbfs_priv priv_s, *priv = &priv_s;
256         int ret;
257
258         /*
259          * Use a local variable to start with until we know that the CBFS is
260          * valid.
261          */
262         ret = cbfs_load_header_ptr(priv, base);
263         if (ret)
264                 return ret;
265
266         file_cbfs_fill_cache(priv, (u8 *)base, priv->header.rom_size,
267                              priv->header.align);
268         if (priv->result != CBFS_SUCCESS)
269                 return -EINVAL;
270
271         priv->initialized = true;
272         priv = malloc(sizeof(priv_s));
273         if (!priv)
274                 return -ENOMEM;
275         memcpy(priv, &priv_s, sizeof(priv_s));
276         *privp = priv;
277
278         return 0;
279 }
280
281 const struct cbfs_header *file_cbfs_get_header(void)
282 {
283         struct cbfs_priv *priv = &cbfs_s;
284
285         if (priv->initialized) {
286                 priv->result = CBFS_SUCCESS;
287                 return &priv->header;
288         } else {
289                 priv->result = CBFS_NOT_INITIALIZED;
290                 return NULL;
291         }
292 }
293
294 const struct cbfs_cachenode *file_cbfs_get_first(void)
295 {
296         struct cbfs_priv *priv = &cbfs_s;
297
298         if (!priv->initialized) {
299                 priv->result = CBFS_NOT_INITIALIZED;
300                 return NULL;
301         } else {
302                 priv->result = CBFS_SUCCESS;
303                 return priv->file_cache;
304         }
305 }
306
307 void file_cbfs_get_next(const struct cbfs_cachenode **file)
308 {
309         struct cbfs_priv *priv = &cbfs_s;
310
311         if (!priv->initialized) {
312                 priv->result = CBFS_NOT_INITIALIZED;
313                 *file = NULL;
314                 return;
315         }
316
317         if (*file)
318                 *file = (*file)->next;
319         priv->result = CBFS_SUCCESS;
320 }
321
322 const struct cbfs_cachenode *cbfs_find_file(struct cbfs_priv *priv,
323                                             const char *name)
324 {
325         struct cbfs_cachenode *cache_node = priv->file_cache;
326
327         if (!priv->initialized) {
328                 priv->result = CBFS_NOT_INITIALIZED;
329                 return NULL;
330         }
331
332         while (cache_node) {
333                 if (!strcmp(name, cache_node->name))
334                         break;
335                 cache_node = cache_node->next;
336         }
337         if (!cache_node)
338                 priv->result = CBFS_FILE_NOT_FOUND;
339         else
340                 priv->result = CBFS_SUCCESS;
341
342         return cache_node;
343 }
344
345 const struct cbfs_cachenode *file_cbfs_find(const char *name)
346 {
347         return cbfs_find_file(&cbfs_s, name);
348 }
349
350 const struct cbfs_cachenode *file_cbfs_find_uncached(ulong end_of_rom,
351                                                      const char *name)
352 {
353         struct cbfs_priv *priv = &cbfs_s;
354         u8 *start;
355         u32 size;
356         u32 align;
357         static struct cbfs_cachenode node;
358
359         if (file_cbfs_load_header(priv, end_of_rom))
360                 return NULL;
361
362         start = (u8 *)(end_of_rom + 1 - priv->header.rom_size);
363         size = priv->header.rom_size;
364         align = priv->header.align;
365
366         while (size >= align) {
367                 int ret;
368                 int used;
369
370                 ret = file_cbfs_next_file(priv, start, size, align, &node,
371                                           &used);
372                 if (ret == -ENOENT)
373                         break;
374                 else if (ret)
375                         return NULL;
376                 if (!strcmp(name, node.name))
377                         return &node;
378
379                 size -= used;
380                 start += used;
381         }
382         cbfs_s.result = CBFS_FILE_NOT_FOUND;
383         return NULL;
384 }
385
386 const char *file_cbfs_name(const struct cbfs_cachenode *file)
387 {
388         cbfs_s.result = CBFS_SUCCESS;
389
390         return file->name;
391 }
392
393 u32 file_cbfs_size(const struct cbfs_cachenode *file)
394 {
395         cbfs_s.result = CBFS_SUCCESS;
396
397         return file->data_length;
398 }
399
400 u32 file_cbfs_type(const struct cbfs_cachenode *file)
401 {
402         cbfs_s.result = CBFS_SUCCESS;
403
404         return file->type;
405 }
406
407 long file_cbfs_read(const struct cbfs_cachenode *file, void *buffer,
408                     unsigned long maxsize)
409 {
410         u32 size;
411
412         size = file->data_length;
413         if (maxsize && size > maxsize)
414                 size = maxsize;
415
416         memcpy(buffer, file->data, size);
417         cbfs_s.result = CBFS_SUCCESS;
418
419         return size;
420 }