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