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