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