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