xref: /openbmc/u-boot/fs/cbfs/cbfs.c (revision 12e06fe0)
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 }
337