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 file_cbfs_result = CBFS_BAD_FILE; 101 return -1; 102 } 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->checksum = header.checksum; 112 113 step = header.len; 114 if (step % align) 115 step = step + align - step % align; 116 117 *used += step; 118 return 1; 119 } 120 return 0; 121 } 122 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) 125 { 126 struct cbfs_cachenode *cache_node; 127 struct cbfs_cachenode *newNode; 128 struct cbfs_cachenode **cache_tail = &file_cache; 129 130 /* Clear out old information. */ 131 cache_node = file_cache; 132 while (cache_node) { 133 struct cbfs_cachenode *oldNode = cache_node; 134 cache_node = cache_node->next; 135 free(oldNode); 136 } 137 file_cache = NULL; 138 139 while (size >= align) { 140 int result; 141 u32 used; 142 143 newNode = (struct cbfs_cachenode *) 144 malloc(sizeof(struct cbfs_cachenode)); 145 result = file_cbfs_next_file(start, size, align, 146 newNode, &used); 147 148 if (result < 0) { 149 free(newNode); 150 return; 151 } else if (result == 0) { 152 free(newNode); 153 break; 154 } 155 *cache_tail = newNode; 156 cache_tail = &newNode->next; 157 158 size -= used; 159 start += used; 160 } 161 file_cbfs_result = CBFS_SUCCESS; 162 } 163 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) 167 { 168 struct cbfs_header *header_in_rom; 169 int32_t offset = *(u32 *)(end_of_rom - 3); 170 171 header_in_rom = (struct cbfs_header *)(end_of_rom + offset + 1); 172 swap_header(header, header_in_rom); 173 174 if (header->magic != good_magic || header->offset > 175 header->rom_size - header->boot_block_size) { 176 file_cbfs_result = CBFS_BAD_HEADER; 177 return 1; 178 } 179 return 0; 180 } 181 182 void file_cbfs_init(uintptr_t end_of_rom) 183 { 184 u8 *start_of_rom; 185 initialized = 0; 186 187 if (file_cbfs_load_header(end_of_rom, &cbfs_header)) 188 return; 189 190 start_of_rom = (u8 *)(end_of_rom + 1 - cbfs_header.rom_size); 191 192 file_cbfs_fill_cache(start_of_rom, cbfs_header.rom_size, 193 cbfs_header.align); 194 if (file_cbfs_result == CBFS_SUCCESS) 195 initialized = 1; 196 } 197 198 const struct cbfs_header *file_cbfs_get_header(void) 199 { 200 if (initialized) { 201 file_cbfs_result = CBFS_SUCCESS; 202 return &cbfs_header; 203 } else { 204 file_cbfs_result = CBFS_NOT_INITIALIZED; 205 return NULL; 206 } 207 } 208 209 const struct cbfs_cachenode *file_cbfs_get_first(void) 210 { 211 if (!initialized) { 212 file_cbfs_result = CBFS_NOT_INITIALIZED; 213 return NULL; 214 } else { 215 file_cbfs_result = CBFS_SUCCESS; 216 return file_cache; 217 } 218 } 219 220 void file_cbfs_get_next(const struct cbfs_cachenode **file) 221 { 222 if (!initialized) { 223 file_cbfs_result = CBFS_NOT_INITIALIZED; 224 file = NULL; 225 return; 226 } 227 228 if (*file) 229 *file = (*file)->next; 230 file_cbfs_result = CBFS_SUCCESS; 231 } 232 233 const struct cbfs_cachenode *file_cbfs_find(const char *name) 234 { 235 struct cbfs_cachenode *cache_node = file_cache; 236 237 if (!initialized) { 238 file_cbfs_result = CBFS_NOT_INITIALIZED; 239 return NULL; 240 } 241 242 while (cache_node) { 243 if (!strcmp(name, cache_node->name)) 244 break; 245 cache_node = cache_node->next; 246 } 247 if (!cache_node) 248 file_cbfs_result = CBFS_FILE_NOT_FOUND; 249 else 250 file_cbfs_result = CBFS_SUCCESS; 251 252 return cache_node; 253 } 254 255 const struct cbfs_cachenode *file_cbfs_find_uncached(uintptr_t end_of_rom, 256 const char *name) 257 { 258 u8 *start; 259 u32 size; 260 u32 align; 261 static struct cbfs_cachenode node; 262 263 if (file_cbfs_load_header(end_of_rom, &cbfs_header)) 264 return NULL; 265 266 start = (u8 *)(end_of_rom + 1 - cbfs_header.rom_size); 267 size = cbfs_header.rom_size; 268 align = cbfs_header.align; 269 270 while (size >= align) { 271 int result; 272 u32 used; 273 274 result = file_cbfs_next_file(start, size, align, &node, &used); 275 276 if (result < 0) 277 return NULL; 278 else if (result == 0) 279 break; 280 281 if (!strcmp(name, node.name)) 282 return &node; 283 284 size -= used; 285 start += used; 286 } 287 file_cbfs_result = CBFS_FILE_NOT_FOUND; 288 return NULL; 289 } 290 291 const char *file_cbfs_name(const struct cbfs_cachenode *file) 292 { 293 file_cbfs_result = CBFS_SUCCESS; 294 return file->name; 295 } 296 297 u32 file_cbfs_size(const struct cbfs_cachenode *file) 298 { 299 file_cbfs_result = CBFS_SUCCESS; 300 return file->data_length; 301 } 302 303 u32 file_cbfs_type(const struct cbfs_cachenode *file) 304 { 305 file_cbfs_result = CBFS_SUCCESS; 306 return file->type; 307 } 308 309 long file_cbfs_read(const struct cbfs_cachenode *file, void *buffer, 310 unsigned long maxsize) 311 { 312 u32 size; 313 314 size = file->data_length; 315 if (maxsize && size > maxsize) 316 size = maxsize; 317 318 memcpy(buffer, file->data, size); 319 320 file_cbfs_result = CBFS_SUCCESS; 321 return size; 322 } 323