]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - fs/cbfs/cbfs.c
Merge 'u-boot-atmel/master' into 'u-boot-arm/master'
[karo-tx-uboot.git] / fs / cbfs / cbfs.c
1 /*
2  * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17  * MA 02111-1307 USA
18  */
19
20 #include <cbfs.h>
21 #include <malloc.h>
22 #include <asm/byteorder.h>
23
24 enum cbfs_result file_cbfs_result;
25
26 const char *file_cbfs_error(void)
27 {
28         switch (file_cbfs_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
45 static const u32 good_magic = 0x4f524243;
46 static const u8 good_file_magic[] = "LARCHIVE";
47
48
49 static int initialized;
50 static struct cbfs_header cbfs_header;
51 static struct cbfs_cachenode *file_cache;
52
53 /* Do endian conversion on the CBFS header structure. */
54 static void swap_header(struct cbfs_header *dest, struct cbfs_header *src)
55 {
56         dest->magic = be32_to_cpu(src->magic);
57         dest->version = be32_to_cpu(src->version);
58         dest->rom_size = be32_to_cpu(src->rom_size);
59         dest->boot_block_size = be32_to_cpu(src->boot_block_size);
60         dest->align = be32_to_cpu(src->align);
61         dest->offset = be32_to_cpu(src->offset);
62 }
63
64 /* Do endian conversion on a CBFS file header. */
65 static void swap_file_header(struct cbfs_fileheader *dest,
66                              const struct cbfs_fileheader *src)
67 {
68         memcpy(&dest->magic, &src->magic, sizeof(dest->magic));
69         dest->len = be32_to_cpu(src->len);
70         dest->type = be32_to_cpu(src->type);
71         dest->checksum = be32_to_cpu(src->checksum);
72         dest->offset = be32_to_cpu(src->offset);
73 }
74
75 /*
76  * Given a starting position in memory, scan forward, bounded by a size, and
77  * find the next valid CBFS file. No memory is allocated by this function. The
78  * caller is responsible for allocating space for the new file structure.
79  *
80  * @param start         The location in memory to start from.
81  * @param size          The size of the memory region to search.
82  * @param align         The alignment boundaries to check on.
83  * @param newNode       A pointer to the file structure to load.
84  * @param used          A pointer to the count of of bytes scanned through,
85  *                      including the file if one is found.
86  *
87  * @return 1 if a file is found, 0 if one isn't.
88  */
89 static int file_cbfs_next_file(u8 *start, u32 size, u32 align,
90                                struct cbfs_cachenode *newNode, u32 *used)
91 {
92         struct cbfs_fileheader header;
93
94         *used = 0;
95
96         while (size >= align) {
97                 const struct cbfs_fileheader *fileHeader =
98                         (const struct cbfs_fileheader *)start;
99                 u32 name_len;
100                 u32 step;
101
102                 /* Check if there's a file here. */
103                 if (memcmp(good_file_magic, &(fileHeader->magic),
104                                 sizeof(fileHeader->magic))) {
105                         *used += align;
106                         size -= align;
107                         start += align;
108                         continue;
109                 }
110
111                 swap_file_header(&header, fileHeader);
112                 if (header.offset < sizeof(const struct cbfs_cachenode *) ||
113                                 header.offset > header.len) {
114                         file_cbfs_result = CBFS_BAD_FILE;
115                         return -1;
116                 }
117                 newNode->next = NULL;
118                 newNode->type = header.type;
119                 newNode->data = start + header.offset;
120                 newNode->data_length = header.len;
121                 name_len = header.offset - sizeof(struct cbfs_cachenode *);
122                 newNode->name = (char *)fileHeader +
123                                 sizeof(struct cbfs_cachenode *);
124                 newNode->name_length = name_len;
125                 newNode->checksum = header.checksum;
126
127                 step = header.len;
128                 if (step % align)
129                         step = step + align - step % align;
130
131                 *used += step;
132                 return 1;
133         }
134         return 0;
135 }
136
137 /* Look through a CBFS instance and copy file metadata into regular memory. */
138 static void file_cbfs_fill_cache(u8 *start, u32 size, u32 align)
139 {
140         struct cbfs_cachenode *cache_node;
141         struct cbfs_cachenode *newNode;
142         struct cbfs_cachenode **cache_tail = &file_cache;
143
144         /* Clear out old information. */
145         cache_node = file_cache;
146         while (cache_node) {
147                 struct cbfs_cachenode *oldNode = cache_node;
148                 cache_node = cache_node->next;
149                 free(oldNode);
150         }
151         file_cache = NULL;
152
153         while (size >= align) {
154                 int result;
155                 u32 used;
156
157                 newNode = (struct cbfs_cachenode *)
158                                 malloc(sizeof(struct cbfs_cachenode));
159                 result = file_cbfs_next_file(start, size, align,
160                         newNode, &used);
161
162                 if (result < 0) {
163                         free(newNode);
164                         return;
165                 } else if (result == 0) {
166                         free(newNode);
167                         break;
168                 }
169                 *cache_tail = newNode;
170                 cache_tail = &newNode->next;
171
172                 size -= used;
173                 start += used;
174         }
175         file_cbfs_result = CBFS_SUCCESS;
176 }
177
178 /* Get the CBFS header out of the ROM and do endian conversion. */
179 static int file_cbfs_load_header(uintptr_t end_of_rom,
180                                  struct cbfs_header *header)
181 {
182         struct cbfs_header *header_in_rom;
183
184         header_in_rom = (struct cbfs_header *)(uintptr_t)
185                         *(u32 *)(end_of_rom - 3);
186         swap_header(header, header_in_rom);
187
188         if (header->magic != good_magic || header->offset >
189                         header->rom_size - header->boot_block_size) {
190                 file_cbfs_result = CBFS_BAD_HEADER;
191                 return 1;
192         }
193         return 0;
194 }
195
196 void file_cbfs_init(uintptr_t end_of_rom)
197 {
198         u8 *start_of_rom;
199         initialized = 0;
200
201         if (file_cbfs_load_header(end_of_rom, &cbfs_header))
202                 return;
203
204         start_of_rom = (u8 *)(end_of_rom + 1 - cbfs_header.rom_size);
205
206         file_cbfs_fill_cache(start_of_rom + cbfs_header.offset,
207                              cbfs_header.rom_size, cbfs_header.align);
208         if (file_cbfs_result == CBFS_SUCCESS)
209                 initialized = 1;
210 }
211
212 const struct cbfs_header *file_cbfs_get_header(void)
213 {
214         if (initialized) {
215                 file_cbfs_result = CBFS_SUCCESS;
216                 return &cbfs_header;
217         } else {
218                 file_cbfs_result = CBFS_NOT_INITIALIZED;
219                 return NULL;
220         }
221 }
222
223 const struct cbfs_cachenode *file_cbfs_get_first(void)
224 {
225         if (!initialized) {
226                 file_cbfs_result = CBFS_NOT_INITIALIZED;
227                 return NULL;
228         } else {
229                 file_cbfs_result = CBFS_SUCCESS;
230                 return file_cache;
231         }
232 }
233
234 void file_cbfs_get_next(const struct cbfs_cachenode **file)
235 {
236         if (!initialized) {
237                 file_cbfs_result = CBFS_NOT_INITIALIZED;
238                 file = NULL;
239                 return;
240         }
241
242         if (*file)
243                 *file = (*file)->next;
244         file_cbfs_result = CBFS_SUCCESS;
245 }
246
247 const struct cbfs_cachenode *file_cbfs_find(const char *name)
248 {
249         struct cbfs_cachenode *cache_node = file_cache;
250
251         if (!initialized) {
252                 file_cbfs_result = CBFS_NOT_INITIALIZED;
253                 return NULL;
254         }
255
256         while (cache_node) {
257                 if (!strcmp(name, cache_node->name))
258                         break;
259                 cache_node = cache_node->next;
260         }
261         if (!cache_node)
262                 file_cbfs_result = CBFS_FILE_NOT_FOUND;
263         else
264                 file_cbfs_result = CBFS_SUCCESS;
265
266         return cache_node;
267 }
268
269 const struct cbfs_cachenode *file_cbfs_find_uncached(uintptr_t end_of_rom,
270                                                      const char *name)
271 {
272         u8 *start;
273         u32 size;
274         u32 align;
275         static struct cbfs_cachenode node;
276
277         if (file_cbfs_load_header(end_of_rom, &cbfs_header))
278                 return NULL;
279
280         start = (u8 *)(end_of_rom + 1 - cbfs_header.rom_size);
281         size = cbfs_header.rom_size;
282         align = cbfs_header.align;
283
284         while (size >= align) {
285                 int result;
286                 u32 used;
287
288                 result = file_cbfs_next_file(start, size, align, &node, &used);
289
290                 if (result < 0)
291                         return NULL;
292                 else if (result == 0)
293                         break;
294
295                 if (!strcmp(name, node.name))
296                         return &node;
297
298                 size -= used;
299                 start += used;
300         }
301         file_cbfs_result = CBFS_FILE_NOT_FOUND;
302         return NULL;
303 }
304
305 const char *file_cbfs_name(const struct cbfs_cachenode *file)
306 {
307         file_cbfs_result = CBFS_SUCCESS;
308         return file->name;
309 }
310
311 u32 file_cbfs_size(const struct cbfs_cachenode *file)
312 {
313         file_cbfs_result = CBFS_SUCCESS;
314         return file->data_length;
315 }
316
317 u32 file_cbfs_type(const struct cbfs_cachenode *file)
318 {
319         file_cbfs_result = CBFS_SUCCESS;
320         return file->type;
321 }
322
323 long file_cbfs_read(const struct cbfs_cachenode *file, void *buffer,
324                     unsigned long maxsize)
325 {
326         u32 size;
327
328         size = file->data_length;
329         if (maxsize && size > maxsize)
330                 size = maxsize;
331
332         memcpy(buffer, file->data, size);
333
334         file_cbfs_result = CBFS_SUCCESS;
335         return size;
336 }