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