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