diff options
Diffstat (limited to 'libflash/libffs.c')
-rw-r--r-- | libflash/libffs.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/libflash/libffs.c b/libflash/libffs.c new file mode 100644 index 00000000..ef2aa4db --- /dev/null +++ b/libflash/libffs.c @@ -0,0 +1,280 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <ccan/endian/endian.h> + +#include "libffs.h" + +enum ffs_type { + ffs_type_flash, + ffs_type_image, +}; + +struct ffs_handle { + struct ffs_hdr hdr; /* Converted header */ + enum ffs_type type; + struct flash_chip *chip; + uint32_t flash_offset; + uint32_t max_size; + void *cache; + uint32_t cached_size; +}; + +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; +} + +static int ffs_check_convert_header(struct ffs_hdr *dst, struct ffs_hdr *src) +{ + dst->magic = be32_to_cpu(src->magic); + if (dst->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_checksum(src, FFS_HDR_SIZE) != 0) + return FFS_ERR_BAD_CKSUM; + dst->size = be32_to_cpu(src->size); + dst->entry_size = be32_to_cpu(src->entry_size); + dst->entry_count = be32_to_cpu(src->entry_count); + dst->block_size = be32_to_cpu(src->block_size); + dst->block_count = be32_to_cpu(src->block_count); + + return 0; +} + +int ffs_open_flash(struct flash_chip *chip, uint32_t offset, + uint32_t max_size, struct ffs_handle **ffs) +{ + struct ffs_hdr hdr; + struct ffs_handle *f; + uint32_t fl_size, erase_size; + int rc; + + if (!ffs) + return FLASH_ERR_PARM_ERROR; + *ffs = NULL; + + /* Grab some info about our flash chip */ + rc = flash_get_info(chip, NULL, &fl_size, &erase_size); + if (rc) { + FL_ERR("FFS: Error %d retrieving flash info\n", rc); + return rc; + } + if ((offset + max_size) < offset) + return FLASH_ERR_PARM_ERROR; + if ((offset + max_size) > fl_size) + return FLASH_ERR_PARM_ERROR; + + /* Read flash header */ + rc = flash_read(chip, offset, &hdr, sizeof(hdr)); + if (rc) { + FL_ERR("FFS: Error %d reading flash header\n", rc); + return rc; + } + + /* Allocate ffs_handle structure and start populating */ + f = malloc(sizeof(*f)); + if (!f) + return FLASH_ERR_MALLOC_FAILED; + memset(f, 0, sizeof(*f)); + f->type = ffs_type_flash; + f->flash_offset = offset; + f->max_size = max_size ? max_size : (fl_size - offset); + f->chip = chip; + + /* Convert and check flash header */ + rc = ffs_check_convert_header(&f->hdr, &hdr); + if (rc) { + FL_ERR("FFS: Error %d checking flash header\n", rc); + free(f); + return rc; + } + + /* + * Decide how much of the image to grab to get the whole + * partition map. + */ + f->cached_size = f->hdr.block_size * f->hdr.size; + FL_DBG("FFS: Partition map size: 0x%x\n", f->cached_size); + + /* Align to erase size */ + f->cached_size |= (erase_size - 1); + f->cached_size &= ~(erase_size - 1); + FL_DBG("FFS: Aligned to: 0x%x\n", f->cached_size); + + /* Allocate cache */ + f->cache = malloc(f->cached_size); + if (!f->cache) { + free(f); + return FLASH_ERR_MALLOC_FAILED; + } + + /* Read the cached map */ + rc = flash_read(chip, offset, f->cache, f->cached_size); + if (rc) { + FL_ERR("FFS: Error %d reading flash partition map\n", rc); + free(f); + } + if (rc == 0) + *ffs = f; + return rc; +} + +#if 0 /* XXX TODO: For FW updates so we can copy nvram around */ +int ffs_open_image(void *image, uint32_t size, uint32_t offset, + struct ffs_handle **ffs) +{ +} +#endif + +void ffs_close(struct ffs_handle *ffs) +{ + if (ffs->cache) + free(ffs->cache); + free(ffs); +} + +static struct ffs_entry *ffs_get_part(struct ffs_handle *ffs, uint32_t index, + uint32_t *out_offset) +{ + uint32_t esize = ffs->hdr.entry_size; + uint32_t offset = FFS_HDR_SIZE + index * esize; + + if (index > ffs->hdr.entry_count) + return NULL; + if (out_offset) + *out_offset = offset; + return (struct ffs_entry *)(ffs->cache + offset); +} + +static int ffs_check_convert_entry(struct ffs_entry *dst, struct ffs_entry *src) +{ + if (ffs_checksum(src, FFS_ENTRY_SIZE) != 0) + return FFS_ERR_BAD_CKSUM; + memcpy(dst->name, src->name, sizeof(dst->name)); + dst->base = be32_to_cpu(src->base); + dst->size = be32_to_cpu(src->size); + dst->pid = be32_to_cpu(src->pid); + dst->id = be32_to_cpu(src->id); + dst->type = be32_to_cpu(src->type); + dst->flags = be32_to_cpu(src->flags); + dst->actual = be32_to_cpu(src->actual); + + return 0; +} + +int ffs_lookup_part(struct ffs_handle *ffs, const char *name, + uint32_t *part_idx) +{ + struct ffs_entry ent; + uint32_t i; + int rc; + + /* Lookup the requested partition */ + for (i = 0; i < ffs->hdr.entry_count; i++) { + struct ffs_entry *src_ent = ffs_get_part(ffs, i, NULL); + rc = ffs_check_convert_entry(&ent, src_ent); + if (rc) { + FL_ERR("FFS: Bad entry %d in partition map\n", i); + continue; + } + if (!strncmp(name, ent.name, sizeof(ent.name))) + break; + } + if (i >= ffs->hdr.entry_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) +{ + struct ffs_entry *raw_ent; + struct ffs_entry ent; + char *n; + int rc; + + if (part_idx >= ffs->hdr.entry_count) + return FFS_ERR_PART_NOT_FOUND; + + raw_ent = ffs_get_part(ffs, part_idx, NULL); + if (!raw_ent) + return FFS_ERR_PART_NOT_FOUND; + + rc = ffs_check_convert_entry(&ent, raw_ent); + if (rc) { + FL_ERR("FFS: Bad entry %d in partition map\n", part_idx); + return rc; + } + if (start) + *start = ent.base * ffs->hdr.block_size; + if (total_size) + *total_size = ent.size * ffs->hdr.block_size; + if (act_size) + *act_size = ent.actual; + if (name) { + n = malloc(PART_NAME_MAX + 1); + memset(n, 0, PART_NAME_MAX + 1); + strncpy(n, ent.name, PART_NAME_MAX); + *name = n; + } + return 0; +} + +int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx, + uint32_t act_size) +{ + struct ffs_entry *ent; + uint32_t offset; + + if (part_idx >= ffs->hdr.entry_count) { + FL_DBG("FFS: Entry out of bound\n"); + return FFS_ERR_PART_NOT_FOUND; + } + + ent = ffs_get_part(ffs, part_idx, &offset); + if (!ent) { + FL_DBG("FFS: Entry not found\n"); + return FFS_ERR_PART_NOT_FOUND; + } + FL_DBG("FFS: part index %d at offset 0x%08x\n", + part_idx, offset); + + if (ent->actual == cpu_to_be32(act_size)) { + FL_DBG("FFS: ent->actual alrady matches: 0x%08x==0x%08x\n", + cpu_to_be32(act_size), ent->actual); + return 0; + } + ent->actual = cpu_to_be32(act_size); + ent->checksum = ffs_checksum(ent, FFS_ENTRY_SIZE_CSUM); + if (!ffs->chip) + return 0; + return flash_smart_write(ffs->chip, offset, ent, FFS_ENTRY_SIZE); +} |