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