1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
9 #include <asm/byteorder.h>
11 enum cbfs_result file_cbfs_result
;
13 const char *file_cbfs_error(void)
15 switch (file_cbfs_result
) {
18 case CBFS_NOT_INITIALIZED
:
19 return "CBFS not initialized";
21 return "Bad CBFS header";
23 return "Bad CBFS file";
24 case CBFS_FILE_NOT_FOUND
:
25 return "File not found";
32 static const u32 good_magic
= 0x4f524243;
33 static const u8 good_file_magic
[] = "LARCHIVE";
36 static int initialized
;
37 static struct cbfs_header cbfs_header
;
38 static struct cbfs_cachenode
*file_cache
;
40 /* Do endian conversion on the CBFS header structure. */
41 static void swap_header(struct cbfs_header
*dest
, struct cbfs_header
*src
)
43 dest
->magic
= be32_to_cpu(src
->magic
);
44 dest
->version
= be32_to_cpu(src
->version
);
45 dest
->rom_size
= be32_to_cpu(src
->rom_size
);
46 dest
->boot_block_size
= be32_to_cpu(src
->boot_block_size
);
47 dest
->align
= be32_to_cpu(src
->align
);
48 dest
->offset
= be32_to_cpu(src
->offset
);
51 /* Do endian conversion on a CBFS file header. */
52 static void swap_file_header(struct cbfs_fileheader
*dest
,
53 const struct cbfs_fileheader
*src
)
55 memcpy(&dest
->magic
, &src
->magic
, sizeof(dest
->magic
));
56 dest
->len
= be32_to_cpu(src
->len
);
57 dest
->type
= be32_to_cpu(src
->type
);
58 dest
->attributes_offset
= be32_to_cpu(src
->attributes_offset
);
59 dest
->offset
= be32_to_cpu(src
->offset
);
63 * Given a starting position in memory, scan forward, bounded by a size, and
64 * find the next valid CBFS file. No memory is allocated by this function. The
65 * caller is responsible for allocating space for the new file structure.
67 * @param start The location in memory to start from.
68 * @param size The size of the memory region to search.
69 * @param align The alignment boundaries to check on.
70 * @param newNode A pointer to the file structure to load.
71 * @param used A pointer to the count of of bytes scanned through,
72 * including the file if one is found.
74 * @return 1 if a file is found, 0 if one isn't.
76 static int file_cbfs_next_file(u8
*start
, u32 size
, u32 align
,
77 struct cbfs_cachenode
*newNode
, u32
*used
)
79 struct cbfs_fileheader header
;
83 while (size
>= align
) {
84 const struct cbfs_fileheader
*fileHeader
=
85 (const struct cbfs_fileheader
*)start
;
89 /* Check if there's a file here. */
90 if (memcmp(good_file_magic
, &(fileHeader
->magic
),
91 sizeof(fileHeader
->magic
))) {
98 swap_file_header(&header
, fileHeader
);
99 if (header
.offset
< sizeof(struct cbfs_fileheader
)) {
100 file_cbfs_result
= CBFS_BAD_FILE
;
103 newNode
->next
= NULL
;
104 newNode
->type
= header
.type
;
105 newNode
->data
= start
+ header
.offset
;
106 newNode
->data_length
= header
.len
;
107 name_len
= header
.offset
- sizeof(struct cbfs_fileheader
);
108 newNode
->name
= (char *)fileHeader
+
109 sizeof(struct cbfs_fileheader
);
110 newNode
->name_length
= name_len
;
111 newNode
->attributes_offset
= header
.attributes_offset
;
115 step
= step
+ align
- step
% align
;
123 /* Look through a CBFS instance and copy file metadata into regular memory. */
124 static void file_cbfs_fill_cache(u8
*start
, u32 size
, u32 align
)
126 struct cbfs_cachenode
*cache_node
;
127 struct cbfs_cachenode
*newNode
;
128 struct cbfs_cachenode
**cache_tail
= &file_cache
;
130 /* Clear out old information. */
131 cache_node
= file_cache
;
133 struct cbfs_cachenode
*oldNode
= cache_node
;
134 cache_node
= cache_node
->next
;
139 while (size
>= align
) {
143 newNode
= (struct cbfs_cachenode
*)
144 malloc(sizeof(struct cbfs_cachenode
));
145 result
= file_cbfs_next_file(start
, size
, align
,
151 } else if (result
== 0) {
155 *cache_tail
= newNode
;
156 cache_tail
= &newNode
->next
;
161 file_cbfs_result
= CBFS_SUCCESS
;
164 /* Get the CBFS header out of the ROM and do endian conversion. */
165 static int file_cbfs_load_header(uintptr_t end_of_rom
,
166 struct cbfs_header
*header
)
168 struct cbfs_header
*header_in_rom
;
169 int32_t offset
= *(u32
*)(end_of_rom
- 3);
171 header_in_rom
= (struct cbfs_header
*)(end_of_rom
+ offset
+ 1);
172 swap_header(header
, header_in_rom
);
174 if (header
->magic
!= good_magic
|| header
->offset
>
175 header
->rom_size
- header
->boot_block_size
) {
176 file_cbfs_result
= CBFS_BAD_HEADER
;
182 void file_cbfs_init(uintptr_t end_of_rom
)
187 if (file_cbfs_load_header(end_of_rom
, &cbfs_header
))
190 start_of_rom
= (u8
*)(end_of_rom
+ 1 - cbfs_header
.rom_size
);
192 file_cbfs_fill_cache(start_of_rom
, cbfs_header
.rom_size
,
194 if (file_cbfs_result
== CBFS_SUCCESS
)
198 const struct cbfs_header
*file_cbfs_get_header(void)
201 file_cbfs_result
= CBFS_SUCCESS
;
204 file_cbfs_result
= CBFS_NOT_INITIALIZED
;
209 const struct cbfs_cachenode
*file_cbfs_get_first(void)
212 file_cbfs_result
= CBFS_NOT_INITIALIZED
;
215 file_cbfs_result
= CBFS_SUCCESS
;
220 void file_cbfs_get_next(const struct cbfs_cachenode
**file
)
223 file_cbfs_result
= CBFS_NOT_INITIALIZED
;
229 *file
= (*file
)->next
;
230 file_cbfs_result
= CBFS_SUCCESS
;
233 const struct cbfs_cachenode
*file_cbfs_find(const char *name
)
235 struct cbfs_cachenode
*cache_node
= file_cache
;
238 file_cbfs_result
= CBFS_NOT_INITIALIZED
;
243 if (!strcmp(name
, cache_node
->name
))
245 cache_node
= cache_node
->next
;
248 file_cbfs_result
= CBFS_FILE_NOT_FOUND
;
250 file_cbfs_result
= CBFS_SUCCESS
;
255 const struct cbfs_cachenode
*file_cbfs_find_uncached(uintptr_t end_of_rom
,
261 static struct cbfs_cachenode node
;
263 if (file_cbfs_load_header(end_of_rom
, &cbfs_header
))
266 start
= (u8
*)(end_of_rom
+ 1 - cbfs_header
.rom_size
);
267 size
= cbfs_header
.rom_size
;
268 align
= cbfs_header
.align
;
270 while (size
>= align
) {
274 result
= file_cbfs_next_file(start
, size
, align
, &node
, &used
);
278 else if (result
== 0)
281 if (!strcmp(name
, node
.name
))
287 file_cbfs_result
= CBFS_FILE_NOT_FOUND
;
291 const char *file_cbfs_name(const struct cbfs_cachenode
*file
)
293 file_cbfs_result
= CBFS_SUCCESS
;
297 u32
file_cbfs_size(const struct cbfs_cachenode
*file
)
299 file_cbfs_result
= CBFS_SUCCESS
;
300 return file
->data_length
;
303 u32
file_cbfs_type(const struct cbfs_cachenode
*file
)
305 file_cbfs_result
= CBFS_SUCCESS
;
309 long file_cbfs_read(const struct cbfs_cachenode
*file
, void *buffer
,
310 unsigned long maxsize
)
314 size
= file
->data_length
;
315 if (maxsize
&& size
> maxsize
)
318 memcpy(buffer
, file
->data
, size
);
320 file_cbfs_result
= CBFS_SUCCESS
;