diff options
Diffstat (limited to 'roms/skiboot/libflash/libffs.c')
-rw-r--r-- | roms/skiboot/libflash/libffs.c | 842 |
1 files changed, 842 insertions, 0 deletions
diff --git a/roms/skiboot/libflash/libffs.c b/roms/skiboot/libflash/libffs.c new file mode 100644 index 000000000..fe808d118 --- /dev/null +++ b/roms/skiboot/libflash/libffs.c @@ -0,0 +1,842 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2013-2019 IBM Corp. */ + +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifndef __SKIBOOT__ +#include <sys/types.h> +#include <unistd.h> +#endif + +#include "ffs.h" + +#define __unused __attribute__((unused)) +#define HDR_ENTRIES_NUM 30 + +struct ffs_handle { + struct ffs_hdr hdr; /* Converted header */ + uint32_t toc_offset; + uint32_t max_size; + /* The converted header knows how big this is */ + struct __ffs_hdr *cache; + struct blocklevel_device *bl; +}; + +static uint32_t ffs_checksum(void* data, size_t size) +{ + uint32_t i, csum = 0; + + for (i = csum = 0; i < (size/4); i++) + csum ^= ((uint32_t *)data)[i]; + return csum; +} + +/* Helper functions for typesafety and size safety */ +static uint32_t ffs_hdr_checksum(struct __ffs_hdr *hdr) +{ + return ffs_checksum(hdr, sizeof(struct __ffs_hdr)); +} + +static uint32_t ffs_entry_checksum(struct __ffs_entry *ent) +{ + return ffs_checksum(ent, sizeof(struct __ffs_entry)); +} + +static size_t ffs_hdr_raw_size(int num_entries) +{ + return sizeof(struct __ffs_hdr) + num_entries * sizeof(struct __ffs_entry); +} + +static int ffs_num_entries(struct ffs_hdr *hdr) +{ + if (hdr->count == 0) + FL_DBG("%s returned zero!\n", __func__); + return hdr->count; +} + +static int ffs_check_convert_header(struct ffs_hdr *dst, struct __ffs_hdr *src) +{ + if (be32_to_cpu(src->magic) != FFS_MAGIC) + return FFS_ERR_BAD_MAGIC; + dst->version = be32_to_cpu(src->version); + if (dst->version != FFS_VERSION_1) + return FFS_ERR_BAD_VERSION; + if (ffs_hdr_checksum(src) != 0) + return FFS_ERR_BAD_CKSUM; + if (be32_to_cpu(src->entry_size) != sizeof(struct __ffs_entry)) + return FFS_ERR_BAD_SIZE; + if ((be32_to_cpu(src->entry_size) * be32_to_cpu(src->entry_count)) > + (be32_to_cpu(src->block_size) * be32_to_cpu(src->size))) + return FLASH_ERR_PARM_ERROR; + + dst->block_size = be32_to_cpu(src->block_size); + dst->size = be32_to_cpu(src->size) * dst->block_size; + dst->block_count = be32_to_cpu(src->block_count); + dst->entries_size = be32_to_cpu(src->entry_count); + + return 0; +} + +static int ffs_entry_user_to_flash(struct ffs_hdr *hdr __unused, + struct __ffs_entry_user *dst, struct ffs_entry_user *src) +{ + memset(dst, 0, sizeof(struct __ffs_entry_user)); + dst->datainteg = cpu_to_be16(src->datainteg); + dst->vercheck = src->vercheck; + dst->miscflags = src->miscflags; + + return 0; +} + +static int ffs_entry_user_to_cpu(struct ffs_hdr *hdr __unused, + struct ffs_entry_user *dst, struct __ffs_entry_user *src) +{ + memset(dst, 0, sizeof(struct ffs_entry_user)); + dst->datainteg = be16_to_cpu(src->datainteg); + dst->vercheck = src->vercheck; + dst->miscflags = src->miscflags; + + return 0; +} + +static int ffs_entry_to_flash(struct ffs_hdr *hdr, + struct __ffs_entry *dst, struct ffs_entry *src) +{ + int rc, index; + + if (!hdr || !dst || !src) + return -1; + + for (index = 0; index < hdr->count && hdr->entries[index] != src; index++); + + if (index == hdr->count) + return FFS_ERR_PART_NOT_FOUND; + index++; /* On flash indexes start at 1 */ + /* + * So that the checksum gets calculated correctly at least the + * dst->checksum must be zero before calling ffs_entry_checksum() + * memset()ting the entire struct to zero is probably wise as it + * appears the reserved fields are always zero. + */ + memset(dst, 0, sizeof(*dst)); + + memcpy(dst->name, src->name, sizeof(dst->name)); + dst->name[FFS_PART_NAME_MAX] = '\0'; + dst->base = cpu_to_be32(src->base / hdr->block_size); + dst->size = cpu_to_be32(src->size / hdr->block_size); + dst->pid = cpu_to_be32(src->pid); + dst->id = cpu_to_be32(index); + dst->type = cpu_to_be32(src->type); /* TODO: Check that it is valid? */ + dst->flags = cpu_to_be32(src->flags); + dst->actual = cpu_to_be32(src->actual); + rc = ffs_entry_user_to_flash(hdr, &dst->user, &src->user); + dst->checksum = ffs_entry_checksum(dst); + + return rc; +} + +static int ffs_entry_to_cpu(struct ffs_hdr *hdr, + struct ffs_entry *dst, struct __ffs_entry *src) +{ + int rc; + + if (ffs_entry_checksum(src) != 0) + return FFS_ERR_BAD_CKSUM; + + memcpy(dst->name, src->name, sizeof(dst->name)); + dst->name[FFS_PART_NAME_MAX] = '\0'; + dst->base = be32_to_cpu(src->base) * hdr->block_size; + dst->size = be32_to_cpu(src->size) * hdr->block_size; + dst->actual = be32_to_cpu(src->actual); + dst->pid = be32_to_cpu(src->pid); + dst->type = be32_to_cpu(src->type); /* TODO: Check that it is valid? */ + dst->flags = be32_to_cpu(src->flags); + rc = ffs_entry_user_to_cpu(hdr, &dst->user, &src->user); + + return rc; +} + +char *ffs_entry_user_to_string(struct ffs_entry_user *user) +{ + char *ret; + + if (!user) + return NULL; + + ret = strdup("----------"); + if (!ret) + return NULL; + + if (user->datainteg & FFS_ENRY_INTEG_ECC) + ret[0] = 'E'; + + if (user->vercheck & FFS_VERCHECK_SHA512V) + ret[1] = 'L'; + + if (user->vercheck & FFS_VERCHECK_SHA512EC) + ret[2] = 'I'; + + if (user->miscflags & FFS_MISCFLAGS_PRESERVED) + ret[3] = 'P'; + + if (user->miscflags & FFS_MISCFLAGS_READONLY) + ret[4] = 'R'; + + if (user->miscflags & FFS_MISCFLAGS_BACKUP) + ret[5] = 'B'; + + if (user->miscflags & FFS_MISCFLAGS_REPROVISION) + ret[6] = 'F'; + + if (user->miscflags & FFS_MISCFLAGS_GOLDEN) + ret[7] = 'G'; + + if (user->miscflags & FFS_MISCFLAGS_CLEARECC) + ret[8] = 'C'; + + if (user->miscflags & FFS_MISCFLAGS_VOLATILE) + ret[9] = 'V'; + + return ret; +} + +int ffs_string_to_entry_user(const char *flags, int nflags, + struct ffs_entry_user *user) +{ + int i; + + if (!user || !flags) + return FLASH_ERR_PARM_ERROR; + + memset(user, 0, sizeof(struct ffs_entry_user)); + for (i = 0; i < nflags; i++) { + switch (flags[i]) { + case 'E': + user->datainteg |= FFS_ENRY_INTEG_ECC; + break; + case 'L': + user->vercheck |= FFS_VERCHECK_SHA512V; + break; + case 'I': + user->vercheck |= FFS_VERCHECK_SHA512EC; + break; + case 'P': + user->miscflags |= FFS_MISCFLAGS_PRESERVED; + break; + case 'R': + user->miscflags |= FFS_MISCFLAGS_READONLY; + break; + case 'B': + user->miscflags |= FFS_MISCFLAGS_BACKUP; + break; + case 'F': + user->miscflags |= FFS_MISCFLAGS_REPROVISION; + break; + case 'G': + user->miscflags |= FFS_MISCFLAGS_GOLDEN; + break; + case 'C': + user->miscflags |= FFS_MISCFLAGS_CLEARECC; + break; + case 'V': + user->miscflags |= FFS_MISCFLAGS_VOLATILE; + break; + default: + FL_DBG("Unknown flag '%c'\n", flags[i]); + return FLASH_ERR_PARM_ERROR; + } + } + + return 0; +} + +bool has_flag(struct ffs_entry *ent, uint16_t flag) +{ + return ((ent->user.miscflags & flag) != 0); +} + +static struct ffs_entry *__ffs_entry_get(struct ffs_handle *ffs, uint32_t index) +{ + if (index >= ffs->hdr.count) + return NULL; + return ffs->hdr.entries[index]; +} + +struct ffs_entry *ffs_entry_get(struct ffs_handle *ffs, uint32_t index) +{ + struct ffs_entry *ret = __ffs_entry_get(ffs, index); + if (ret) + ret->ref++; + return ret; +} + +struct ffs_entry *ffs_entry_put(struct ffs_entry *ent) +{ + if (!ent) + return NULL; + + ent->ref--; + if (ent->ref == 0) { + free(ent); + ent = NULL; + } + + return ent; +} + +bool has_ecc(struct ffs_entry *ent) +{ + return ((ent->user.datainteg & FFS_ENRY_INTEG_ECC) != 0); +} + +int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl, + struct ffs_handle **ffs, bool mark_ecc) +{ + struct __ffs_hdr blank_hdr; + struct __ffs_hdr raw_hdr; + struct ffs_handle *f; + uint64_t total_size; + int rc, i; + + if (!ffs || !bl) + return FLASH_ERR_PARM_ERROR; + *ffs = NULL; + + rc = blocklevel_get_info(bl, NULL, &total_size, NULL); + if (rc) { + FL_ERR("FFS: Error %d retrieving flash info\n", rc); + return rc; + } + if (total_size > UINT_MAX) + return FLASH_ERR_VERIFY_FAILURE; + if ((offset + max_size) < offset) + return FLASH_ERR_PARM_ERROR; + + if ((max_size > total_size)) + return FLASH_ERR_PARM_ERROR; + + /* Read flash header */ + rc = blocklevel_read(bl, offset, &raw_hdr, sizeof(raw_hdr)); + if (rc) { + FL_ERR("FFS: Error %d reading flash header\n", rc); + return rc; + } + + /* + * Flash controllers can get deconfigured or otherwise upset, when this + * happens they return all 0xFF bytes. + * An __ffs_hdr consisting of all 0xFF cannot be valid and it would be + * nice to drop a hint to the user to help with debugging. This will + * help quickly differentiate between flash corruption and standard + * type 'reading from the wrong place' errors vs controller errors or + * reading erased data. + */ + memset(&blank_hdr, UINT_MAX, sizeof(struct __ffs_hdr)); + if (memcmp(&blank_hdr, &raw_hdr, sizeof(struct __ffs_hdr)) == 0) { + FL_ERR("FFS: Reading the flash has returned all 0xFF.\n"); + FL_ERR(" Are you reading erased flash?\n"); + FL_ERR(" Is something else using the flash controller?\n"); + return FLASH_ERR_BAD_READ; + } + + /* Allocate ffs_handle structure and start populating */ + f = calloc(1, sizeof(*f)); + if (!f) + return FLASH_ERR_MALLOC_FAILED; + + f->toc_offset = offset; + f->max_size = max_size; + f->bl = bl; + + /* Convert and check flash header */ + rc = ffs_check_convert_header(&f->hdr, &raw_hdr); + if (rc) { + FL_INF("FFS: Flash header not found. Code: %d\n", rc); + goto out; + } + + /* Check header is sane */ + if ((f->hdr.block_count * f->hdr.block_size) > max_size) { + rc = FLASH_ERR_PARM_ERROR; + FL_ERR("FFS: Flash header exceeds max flash size\n"); + goto out; + } + + f->hdr.entries = calloc(f->hdr.entries_size, sizeof(struct ffs_entry *)); + + /* + * Grab the entire partition header + */ + /* Check for overflow or a silly size */ + if (!f->hdr.size || f->hdr.size % f->hdr.block_size != 0) { + rc = FLASH_ERR_MALLOC_FAILED; + FL_ERR("FFS: Cache size overflow (0x%x * 0x%x)\n", + f->hdr.block_size, f->hdr.size); + goto out; + } + + FL_DBG("FFS: Partition map size: 0x%x\n", f->hdr.size); + + /* Allocate cache */ + f->cache = malloc(f->hdr.size); + if (!f->cache) { + rc = FLASH_ERR_MALLOC_FAILED; + goto out; + } + + /* Read the cached map */ + rc = blocklevel_read(bl, offset, f->cache, f->hdr.size); + if (rc) { + FL_ERR("FFS: Error %d reading flash partition map\n", rc); + goto out; + } + + for (i = 0; i < f->hdr.entries_size; i++) { + struct ffs_entry *ent = calloc(1, sizeof(struct ffs_entry)); + if (!ent) { + rc = FLASH_ERR_MALLOC_FAILED; + goto out; + } + + f->hdr.entries[f->hdr.count++] = ent; + ent->ref = 1; + rc = ffs_entry_to_cpu(&f->hdr, ent, &f->cache->entries[i]); + if (rc) { + FL_DBG("FFS: Failed checksum for partition %s\n", + f->cache->entries[i].name); + goto out; + } + + if (mark_ecc && has_ecc(ent)) { + rc = blocklevel_ecc_protect(bl, ent->base, ent->size); + if (rc) { + FL_ERR("Failed to blocklevel_ecc_protect(0x%08x, 0x%08x)\n", + ent->base, ent->size); + goto out; + } + } + } + +out: + if (rc == 0) + *ffs = f; + else + ffs_close(f); + + return rc; +} + +static void __hdr_free(struct ffs_hdr *hdr) +{ + int i; + + if (!hdr) + return; + + for (i = 0; i < hdr->count; i++) + ffs_entry_put(hdr->entries[i]); + free(hdr->entries); +} + +void ffs_hdr_free(struct ffs_hdr *hdr) +{ + __hdr_free(hdr); + free(hdr); +} + +void ffs_close(struct ffs_handle *ffs) +{ + __hdr_free(&ffs->hdr); + + if (ffs->cache) + free(ffs->cache); + + free(ffs); +} + +int ffs_lookup_part(struct ffs_handle *ffs, const char *name, + uint32_t *part_idx) +{ + struct ffs_entry **ents = ffs->hdr.entries; + int i; + + for (i = 0; + i < ffs->hdr.count && + strncmp(name, ents[i]->name, FFS_PART_NAME_MAX); + i++); + + if (i == ffs->hdr.count) + return FFS_ERR_PART_NOT_FOUND; + + if (part_idx) + *part_idx = i; + return 0; +} + +int ffs_part_info(struct ffs_handle *ffs, uint32_t part_idx, + char **name, uint32_t *start, + uint32_t *total_size, uint32_t *act_size, bool *ecc) +{ + struct ffs_entry *ent; + char *n; + + ent = __ffs_entry_get(ffs, part_idx); + if (!ent) + return FFS_ERR_PART_NOT_FOUND; + + if (start) + *start = ent->base; + if (total_size) + *total_size = ent->size; + if (act_size) + *act_size = ent->actual; + if (ecc) + *ecc = has_ecc(ent); + + if (name) { + n = calloc(1, FFS_PART_NAME_MAX + 1); + if (!n) + return FLASH_ERR_MALLOC_FAILED; + memcpy(n, ent->name, FFS_PART_NAME_MAX); + *name = n; + } + return 0; +} + +/* + * There are quite a few ways one might consider two ffs_handles to be the + * same. For the purposes of this function we are trying to detect a fairly + * specific scenario: + * Consecutive calls to ffs_next_side() may succeed but have gone circular. + * It is possible that the OTHER_SIDE partition in one TOC actually points + * back to the TOC to first ffs_handle. + * This function compares for this case, therefore the requirements are + * simple, the underlying blocklevel_devices must be the same along with + * the toc_offset and the max_size. + */ +bool ffs_equal(struct ffs_handle *one, struct ffs_handle *two) +{ + return (!one && !two) || (one && two && one->bl == two->bl + && one->toc_offset == two->toc_offset + && one->max_size == two->max_size); +} + +int ffs_next_side(struct ffs_handle *ffs, struct ffs_handle **new_ffs, + bool mark_ecc) +{ + int rc; + uint32_t index, offset, max_size; + + if (!ffs || !new_ffs) + return FLASH_ERR_PARM_ERROR; + + *new_ffs = NULL; + + rc = ffs_lookup_part(ffs, "OTHER_SIDE", &index); + if (rc) + return rc; + + rc = ffs_part_info(ffs, index, NULL, &offset, &max_size, NULL, NULL); + if (rc) + return rc; + + return ffs_init(offset, max_size, ffs->bl, new_ffs, mark_ecc); +} + +int ffs_entry_add(struct ffs_hdr *hdr, struct ffs_entry *entry) +{ + const char *smallest_name; + uint32_t smallest_base, toc_base; + int i; + + FL_DBG("LIBFFS: Adding '%s' at 0x%08x..0x%08x\n", + entry->name, entry->base, entry->base + entry->size); + + if (hdr->count == 0) { + FL_DBG("LIBFFS: Adding an entry to an empty header\n"); + hdr->entries[hdr->count++] = entry; + } + if (entry->base + entry->size > hdr->block_size * hdr->block_count) + return FFS_ERR_BAD_PART_SIZE; + + smallest_base = entry->base; + smallest_name = entry->name; + toc_base = 0; + /* + * TODO: This may have assumed entries was sorted + */ + for (i = 0; i < hdr->count; i++) { + struct ffs_entry *ent = hdr->entries[i]; + + /* Don't allow same names to differ only by case */ + if (strncasecmp(entry->name, ent->name, FFS_PART_NAME_MAX) == 0) + return FFS_ERR_BAD_PART_NAME; + + if (entry->base >= ent->base && entry->base < ent->base + ent->size) + return FFS_ERR_BAD_PART_BASE; + + if (entry->base + entry->size > ent->base && + entry->base + entry->size < ent->base + ent->size) + return FFS_ERR_BAD_PART_SIZE; + + if (entry->actual > entry->size) + return FFS_ERR_BAD_PART_SIZE; + + if (entry->pid != FFS_PID_TOPLEVEL) + return FFS_ERR_BAD_PART_PID; + + /* First partition is the partition table */ + if (i == 0) { + toc_base = ent->base; + } else { + /* + * We're looking for the partition directly + * after the toc to make sure we don't + * overflow onto it. + */ + if (ent->base < smallest_base && ent->base > toc_base) { + smallest_base = ent->base; + smallest_name = ent->name; + } + } + } + /* If the smallest base is before the TOC, don't worry */ + if (smallest_base > toc_base && (hdr->count + 1) * sizeof(struct __ffs_entry) + + sizeof(struct __ffs_hdr) + toc_base > smallest_base) { + fprintf(stderr, "Adding partition '%s' would cause partition '%s' at " + "0x%08x to overlap with the header\n", entry->name, smallest_name, + smallest_base); + return FFS_ERR_BAD_PART_BASE; + } + + if (hdr->count == hdr->entries_size) { + struct ffs_entry **old = hdr->entries; + + hdr->entries = realloc(hdr->entries, + (HDR_ENTRIES_NUM + hdr->entries_size) * sizeof(struct ffs_entry *)); + if (!hdr->entries) { + hdr->entries = old; + return FLASH_ERR_MALLOC_FAILED; + } + hdr->entries_size += HDR_ENTRIES_NUM; + } + entry->ref++; + hdr->entries[hdr->count++] = entry; + + return 0; +} + +int ffs_hdr_finalise(struct blocklevel_device *bl, struct ffs_hdr *hdr) +{ + int num_entries, i, rc = 0; + struct __ffs_hdr *real_hdr; + + num_entries = ffs_num_entries(hdr); + + /* A TOC shouldn't have zero partitions */ + if (num_entries == 0) + return FFS_ERR_BAD_SIZE; + + real_hdr = malloc(ffs_hdr_raw_size(num_entries)); + if (!real_hdr) + return FLASH_ERR_MALLOC_FAILED; + + /* + * So that the checksum gets calculated correctly at least the + * real_hdr->checksum must be zero before calling ffs_hdr_checksum() + * memset()ting the entire struct to zero is probably wise as it + * appears the reserved fields are always zero. + */ + memset(real_hdr, 0, sizeof(*real_hdr)); + + hdr->part->size = ffs_hdr_raw_size(num_entries) + hdr->block_size; + /* + * So actual is in bytes. ffs_entry_to_flash() don't do the + * block_size division that we're relying on + */ + hdr->part->actual = (hdr->part->size / hdr->block_size) * hdr->block_size; + real_hdr->magic = cpu_to_be32(FFS_MAGIC); + real_hdr->version = cpu_to_be32(hdr->version); + real_hdr->size = cpu_to_be32(hdr->part->size / hdr->block_size); + real_hdr->entry_size = cpu_to_be32(sizeof(struct __ffs_entry)); + real_hdr->entry_count = cpu_to_be32(num_entries); + real_hdr->block_size = cpu_to_be32(hdr->block_size); + real_hdr->block_count = cpu_to_be32(hdr->block_count); + real_hdr->checksum = ffs_hdr_checksum(real_hdr); + + for (i = 0; i < hdr->count; i++) { + rc = ffs_entry_to_flash(hdr, real_hdr->entries + i, hdr->entries[i]); + if (rc) { + fprintf(stderr, "Couldn't format all entries for new TOC\n"); + goto out; + } + } + + /* Don't really care if this fails */ + blocklevel_erase(bl, hdr->part->base, hdr->size); + rc = blocklevel_write(bl, hdr->part->base, real_hdr, + ffs_hdr_raw_size(num_entries)); + if (rc) + goto out; + +out: + free(real_hdr); + return rc; +} + +int ffs_entry_user_set(struct ffs_entry *ent, struct ffs_entry_user *user) +{ + if (!ent || !user) + return -1; + + /* + * Don't allow the user to specify anything we dont't know about. + * Rationale: This is the library providing access to the FFS structures. + * If the consumer of the library knows more about FFS structures then + * questions need to be asked. + * The other possibility is that they've unknowningly supplied invalid + * flags, we should tell them. + */ + if (user->chip) + return -1; + if (user->compresstype) + return -1; + if (user->datainteg & ~(FFS_ENRY_INTEG_ECC)) + return -1; + if (user->vercheck & ~(FFS_VERCHECK_SHA512V | FFS_VERCHECK_SHA512EC)) + return -1; + if (user->miscflags & ~(FFS_MISCFLAGS_PRESERVED | FFS_MISCFLAGS_BACKUP | + FFS_MISCFLAGS_READONLY | FFS_MISCFLAGS_REPROVISION | + FFS_MISCFLAGS_VOLATILE | FFS_MISCFLAGS_GOLDEN | + FFS_MISCFLAGS_CLEARECC)) + return -1; + + memcpy(&ent->user, user, sizeof(*user)); + return 0; +} + +struct ffs_entry_user ffs_entry_user_get(struct ffs_entry *ent) +{ + struct ffs_entry_user user = { 0 }; + + if (ent) + memcpy(&user, &ent->user, sizeof(user)); + + return user; +} + +int ffs_entry_new(const char *name, uint32_t base, uint32_t size, struct ffs_entry **r) +{ + struct ffs_entry *ret; + + ret = calloc(1, sizeof(*ret)); + if (!ret) + return FLASH_ERR_MALLOC_FAILED; + + strncpy(ret->name, name, FFS_PART_NAME_MAX); + ret->name[FFS_PART_NAME_MAX] = '\0'; + ret->base = base; + ret->size = size; + ret->actual = size; + ret->pid = FFS_PID_TOPLEVEL; + ret->type = FFS_TYPE_DATA; + ret->ref = 1; + + *r = ret; + return 0; +} + +int ffs_entry_set_act_size(struct ffs_entry *ent, uint32_t actual_size) +{ + if (!ent) + return -1; + + if (actual_size > ent->size) + return FFS_ERR_BAD_PART_SIZE; + + ent->actual = actual_size; + + return 0; +} + +int ffs_hdr_new(uint32_t block_size, uint32_t block_count, + struct ffs_entry **e, struct ffs_hdr **r) +{ + struct ffs_hdr *ret; + struct ffs_entry *part_table; + int rc; + + ret = calloc(1, sizeof(*ret)); + if (!ret) + return FLASH_ERR_MALLOC_FAILED; + + ret->version = FFS_VERSION_1; + ret->block_size = block_size; + ret->block_count = block_count; + ret->entries = calloc(HDR_ENTRIES_NUM, sizeof(struct ffs_entry *)); + ret->entries_size = HDR_ENTRIES_NUM; + + if (!e || !(*e)) { + /* Don't know how big it will be, ffs_hdr_finalise() will fix */ + rc = ffs_entry_new("part", 0, 0, &part_table); + if (rc) { + free(ret); + return rc; + } + if (e) + *e = part_table; + } else { + part_table = *e; + } + + /* If the user still holds a ref to e, then inc the refcount */ + if (e) + part_table->ref++; + + ret->part = part_table; + + part_table->pid = FFS_PID_TOPLEVEL; + part_table->type = FFS_TYPE_PARTITION; + part_table->flags = FFS_FLAGS_PROTECTED; + + ret->entries[0] = part_table; + ret->count = 1; + + *r = ret; + + return 0; +} + +int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx, + uint32_t act_size) +{ + struct ffs_entry *ent; + struct __ffs_entry raw_ent; + uint32_t offset; + int rc; + + ent = __ffs_entry_get(ffs, part_idx); + if (!ent) { + FL_DBG("FFS: Entry not found\n"); + return FFS_ERR_PART_NOT_FOUND; + } + offset = ffs->toc_offset + ffs_hdr_raw_size(part_idx); + FL_DBG("FFS: part index %d at offset 0x%08x\n", + part_idx, offset); + + if (ent->actual == act_size) { + FL_DBG("FFS: ent->actual alrady matches: 0x%08x==0x%08x\n", + act_size, ent->actual); + return 0; + } + ent->actual = act_size; + + rc = ffs_entry_to_flash(&ffs->hdr, &raw_ent, ent); + if (rc) + return rc; + + return blocklevel_smart_write(ffs->bl, offset, &raw_ent, sizeof(struct __ffs_entry)); +} |