/* */ #include #include #include #include #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); }