cbfs: Move declarations above functions
[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 enum cbfs_result file_cbfs_result;
12 static const u32 good_magic = 0x4f524243;
13 static const u8 good_file_magic[] = "LARCHIVE";
14 static int initialized;
15 static struct cbfs_header cbfs_header;
16 static struct cbfs_cachenode *file_cache;
17
18 const char *file_cbfs_error(void)
19 {
20         switch (file_cbfs_result) {
21         case CBFS_SUCCESS:
22                 return "Success";
23         case CBFS_NOT_INITIALIZED:
24                 return "CBFS not initialized";
25         case CBFS_BAD_HEADER:
26                 return "Bad CBFS header";
27         case CBFS_BAD_FILE:
28                 return "Bad CBFS file";
29         case CBFS_FILE_NOT_FOUND:
30                 return "File not found";
31         default:
32                 return "Unknown";
33         }
34 }
35
36 /* Do endian conversion on the CBFS header structure. */
37 static void swap_header(struct cbfs_header *dest, struct cbfs_header *src)
38 {
39         dest->magic = be32_to_cpu(src->magic);
40         dest->version = be32_to_cpu(src->version);
41         dest->rom_size = be32_to_cpu(src->rom_size);
42         dest->boot_block_size = be32_to_cpu(src->boot_block_size);
43         dest->align = be32_to_cpu(src->align);
44         dest->offset = be32_to_cpu(src->offset);
45 }
46
47 /* Do endian conversion on a CBFS file header. */
48 static void swap_file_header(struct cbfs_fileheader *dest,
49                              const struct cbfs_fileheader *src)
50 {
51         memcpy(&dest->magic, &src->magic, sizeof(dest->magic));
52         dest->len = be32_to_cpu(src->len);
53         dest->type = be32_to_cpu(src->type);
54         dest->attributes_offset = be32_to_cpu(src->attributes_offset);
55         dest->offset = be32_to_cpu(src->offset);
56 }
57
58 /*
59  * Given a starting position in memory, scan forward, bounded by a size, and
60  * find the next valid CBFS file. No memory is allocated by this function. The
61  * caller is responsible for allocating space for the new file structure.
62  *
63  * @param start         The location in memory to start from.
64  * @param size          The size of the memory region to search.
65  * @param align         The alignment boundaries to check on.
66  * @param newNode       A pointer to the file structure to load.
67  * @param used          A pointer to the count of of bytes scanned through,
68  *                      including the file if one is found.
69  *
70  * @return 1 if a file is found, 0 if one isn't.
71  */
72 static int file_cbfs_next_file(u8 *start, u32 size, u32 align,
73                                struct cbfs_cachenode *newNode, u32 *used)
74 {
75         struct cbfs_fileheader header;
76
77         *used = 0;
78
79         while (size >= align) {
80                 const struct cbfs_fileheader *fileHeader =
81                         (const struct cbfs_fileheader *)start;
82                 u32 name_len;
83                 u32 step;
84
85                 /* Check if there's a file here. */
86                 if (memcmp(good_file_magic, &(fileHeader->magic),
87                                 sizeof(fileHeader->magic))) {
88                         *used += align;
89                         size -= align;
90                         start += align;
91                         continue;
92                 }
93
94                 swap_file_header(&header, fileHeader);
95                 if (header.offset < sizeof(struct cbfs_fileheader)) {
96                         file_cbfs_result = CBFS_BAD_FILE;
97                         return -1;
98                 }
99                 newNode->next = NULL;
100                 newNode->type = header.type;
101                 newNode->data = start + header.offset;
102                 newNode->data_length = header.len;
103                 name_len = header.offset - sizeof(struct cbfs_fileheader);
104                 newNode->name = (char *)fileHeader +
105                                 sizeof(struct cbfs_fileheader);
106                 newNode->name_length = name_len;
107                 newNode->attributes_offset = header.attributes_offset;
108
109                 step = header.len;
110                 if (step % align)
111                         step = step + align - step % align;
112
113                 *used += step;
114                 return 1;
115         }
116         return 0;
117 }
118
119 /* Look through a CBFS instance and copy file metadata into regular memory. */
120 static void file_cbfs_fill_cache(u8 *start, u32 size, u32 align)
121 {
122         struct cbfs_cachenode *cache_node;
123         struct cbfs_cachenode *newNode;
124         struct cbfs_cachenode **cache_tail = &file_cache;
125
126         /* Clear out old information. */
127         cache_node = file_cache;
128         while (cache_node) {
129                 struct cbfs_cachenode *oldNode = cache_node;
130                 cache_node = cache_node->next;
131                 free(oldNode);
132         }
133         file_cache = NULL;
134
135         while (size >= align) {
136                 int result;
137                 u32 used;
138
139                 newNode = (struct cbfs_cachenode *)
140                                 malloc(sizeof(struct cbfs_cachenode));
141                 result = file_cbfs_next_file(start, size, align,
142                         newNode, &used);
143
144                 if (result < 0) {
145                         free(newNode);
146                         return;
147                 } else if (result == 0) {
148                         free(newNode);
149                         break;
150                 }
151                 *cache_tail = newNode;
152                 cache_tail = &newNode->next;
153
154                 size -= used;
155                 start += used;
156         }
157         file_cbfs_result = CBFS_SUCCESS;
158 }
159
160 /* Get the CBFS header out of the ROM and do endian conversion. */
161 static int file_cbfs_load_header(uintptr_t end_of_rom,
162                                  struct cbfs_header *header)
163 {
164         struct cbfs_header *header_in_rom;
165         int32_t offset = *(u32 *)(end_of_rom - 3);
166
167         header_in_rom = (struct cbfs_header *)(end_of_rom + offset + 1);
168         swap_header(header, header_in_rom);
169
170         if (header->magic != good_magic || header->offset >
171                         header->rom_size - header->boot_block_size) {
172                 file_cbfs_result = CBFS_BAD_HEADER;
173                 return 1;
174         }
175         return 0;
176 }
177
178 void file_cbfs_init(uintptr_t end_of_rom)
179 {
180         u8 *start_of_rom;
181         initialized = 0;
182
183         if (file_cbfs_load_header(end_of_rom, &cbfs_header))
184                 return;
185
186         start_of_rom = (u8 *)(end_of_rom + 1 - cbfs_header.rom_size);
187
188         file_cbfs_fill_cache(start_of_rom, cbfs_header.rom_size,
189                              cbfs_header.align);
190         if (file_cbfs_result == CBFS_SUCCESS)
191                 initialized = 1;
192 }
193
194 const struct cbfs_header *file_cbfs_get_header(void)
195 {
196         if (initialized) {
197                 file_cbfs_result = CBFS_SUCCESS;
198                 return &cbfs_header;
199         } else {
200                 file_cbfs_result = CBFS_NOT_INITIALIZED;
201                 return NULL;
202         }
203 }
204
205 const struct cbfs_cachenode *file_cbfs_get_first(void)
206 {
207         if (!initialized) {
208                 file_cbfs_result = CBFS_NOT_INITIALIZED;
209                 return NULL;
210         } else {
211                 file_cbfs_result = CBFS_SUCCESS;
212                 return file_cache;
213         }
214 }
215
216 void file_cbfs_get_next(const struct cbfs_cachenode **file)
217 {
218         if (!initialized) {
219                 file_cbfs_result = CBFS_NOT_INITIALIZED;
220                 file = NULL;
221                 return;
222         }
223
224         if (*file)
225                 *file = (*file)->next;
226         file_cbfs_result = CBFS_SUCCESS;
227 }
228
229 const struct cbfs_cachenode *file_cbfs_find(const char *name)
230 {
231         struct cbfs_cachenode *cache_node = file_cache;
232
233         if (!initialized) {
234                 file_cbfs_result = CBFS_NOT_INITIALIZED;
235                 return NULL;
236         }
237
238         while (cache_node) {
239                 if (!strcmp(name, cache_node->name))
240                         break;
241                 cache_node = cache_node->next;
242         }
243         if (!cache_node)
244                 file_cbfs_result = CBFS_FILE_NOT_FOUND;
245         else
246                 file_cbfs_result = CBFS_SUCCESS;
247
248         return cache_node;
249 }
250
251 const struct cbfs_cachenode *file_cbfs_find_uncached(uintptr_t end_of_rom,
252                                                      const char *name)
253 {
254         u8 *start;
255         u32 size;
256         u32 align;
257         static struct cbfs_cachenode node;
258
259         if (file_cbfs_load_header(end_of_rom, &cbfs_header))
260                 return NULL;
261
262         start = (u8 *)(end_of_rom + 1 - cbfs_header.rom_size);
263         size = cbfs_header.rom_size;
264         align = cbfs_header.align;
265
266         while (size >= align) {
267                 int result;
268                 u32 used;
269
270                 result = file_cbfs_next_file(start, size, align, &node, &used);
271
272                 if (result < 0)
273                         return NULL;
274                 else if (result == 0)
275                         break;
276
277                 if (!strcmp(name, node.name))
278                         return &node;
279
280                 size -= used;
281                 start += used;
282         }
283         file_cbfs_result = CBFS_FILE_NOT_FOUND;
284         return NULL;
285 }
286
287 const char *file_cbfs_name(const struct cbfs_cachenode *file)
288 {
289         file_cbfs_result = CBFS_SUCCESS;
290         return file->name;
291 }
292
293 u32 file_cbfs_size(const struct cbfs_cachenode *file)
294 {
295         file_cbfs_result = CBFS_SUCCESS;
296         return file->data_length;
297 }
298
299 u32 file_cbfs_type(const struct cbfs_cachenode *file)
300 {
301         file_cbfs_result = CBFS_SUCCESS;
302         return file->type;
303 }
304
305 long file_cbfs_read(const struct cbfs_cachenode *file, void *buffer,
306                     unsigned long maxsize)
307 {
308         u32 size;
309
310         size = file->data_length;
311         if (maxsize && size > maxsize)
312                 size = maxsize;
313
314         memcpy(buffer, file->data, size);
315
316         file_cbfs_result = CBFS_SUCCESS;
317         return size;
318 }