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