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