1 /* 2 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 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(const struct cbfs_cachenode *) || 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_cachenode *); 109 newNode->name = (char *)fileHeader + 110 sizeof(struct cbfs_cachenode *); 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 171 header_in_rom = (struct cbfs_header *)(uintptr_t) 172 *(u32 *)(end_of_rom - 3); 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