uboot/fs/cbfs/cbfs.c
<<
>>
Prefs
   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
  12enum cbfs_result file_cbfs_result;
  13
  14const 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
  33static const u32 good_magic = 0x4f524243;
  34static const u8 good_file_magic[] = "LARCHIVE";
  35
  36
  37static int initialized;
  38static struct cbfs_header cbfs_header;
  39static struct cbfs_cachenode *file_cache;
  40
  41/* Do endian conversion on the CBFS header structure. */
  42static 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. */
  53static 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 */
  77static 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. */
 126static 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. */
 167static 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
 184void 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
 200const 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
 211const 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
 222void 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
 235const 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
 257const 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
 293const char *file_cbfs_name(const struct cbfs_cachenode *file)
 294{
 295        file_cbfs_result = CBFS_SUCCESS;
 296        return file->name;
 297}
 298
 299u32 file_cbfs_size(const struct cbfs_cachenode *file)
 300{
 301        file_cbfs_result = CBFS_SUCCESS;
 302        return file->data_length;
 303}
 304
 305u32 file_cbfs_type(const struct cbfs_cachenode *file)
 306{
 307        file_cbfs_result = CBFS_SUCCESS;
 308        return file->type;
 309}
 310
 311long 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