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