diff options
-rw-r--r-- | lib/Makefile.am | 19 | ||||
-rw-r--r-- | lib/flash/config.h | 19 | ||||
-rw-r--r-- | lib/flash/flash.c | 213 | ||||
-rw-r--r-- | lib/flash/flash.h | 23 |
4 files changed, 273 insertions, 1 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index a2421a5..a3ae943 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -48,4 +48,21 @@ lib_libpbcore_la_SOURCES = \ lib/url/url.c \ lib/url/url.h \ lib/util/util.c \ - lib/util/util.h + lib/util/util.h \ + lib/flash/config.h \ + lib/flash/flash.h + +if ENABLE_MTD +lib_libpbcore_la_SOURCES += \ + lib/flash/flash.c + +lib_libpbcore_la_CPPFLAGS += \ + $(AM_CPPFLAGS) + +lib_libpbcore_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -l:libflash.so + +lib_libpbcore_la_SOURCES += \ + lib/flash/flash.c +endif diff --git a/lib/flash/config.h b/lib/flash/config.h new file mode 100644 index 0000000..a132a01 --- /dev/null +++ b/lib/flash/config.h @@ -0,0 +1,19 @@ +/* For CCAN */ + +#include <endian.h> +#include <byteswap.h> + +#define HAVE_TYPEOF 1 +#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1 + + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define HAVE_BIG_ENDIAN 0 +#define HAVE_LITTLE_ENDIAN 1 +#else +#define HAVE_BIG_ENDIAN 1 +#define HAVE_LITTLE_ENDIAN 0 +#endif + +#define HAVE_BYTESWAP_H 1 +#define HAVE_BSWAP_64 1 diff --git a/lib/flash/flash.c b/lib/flash/flash.c new file mode 100644 index 0000000..1056c63 --- /dev/null +++ b/lib/flash/flash.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2015 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> +#include <errno.h> +#include <asm/byteorder.h> + +#include <log/log.h> +#include <talloc/talloc.h> +#include <flash/flash.h> +#include <ccan/endian/endian.h> + +#include <libflash/arch_flash.h> +#include <libflash/blocklevel.h> +#include <libflash/libflash.h> +#include <libflash/libffs.h> +#include <libflash/file.h> +#include <libflash/ecc.h> + + +struct flash_info { + /* Device information */ + struct blocklevel_device *bl; + struct ffs_handle *ffs; + uint32_t size; /* raw size of partition */ + const char *path; + bool ecc; + uint32_t erase_granule; + + /* Partition information */ + uint32_t attr_part_idx; + uint32_t attr_data_pos; + uint32_t attr_data_len; /* Includes ECC bytes */ + + /* 'Other Side' info if present */ + struct flash_info *other_side; + +}; + +static int partition_info(struct flash_info *info, const char *partition) +{ + int rc; + + rc = ffs_lookup_part(info->ffs, partition, &info->attr_part_idx); + if (rc) { + pb_log("Failed to find %s partition\n", partition); + return rc; + } + + return ffs_part_info(info->ffs, info->attr_part_idx, NULL, + &info->attr_data_pos, &info->attr_data_len, + NULL, &info->ecc); +} + +static struct flash_info *flash_setup_buffer(void *ctx, const char *partition) +{ + struct flash_info *info; + struct ffs_handle *tmp = NULL; + int rc = 0; + + if (!partition) + return NULL; + + info = talloc_zero(ctx, struct flash_info); + if (!info) + return NULL; + + rc = arch_flash_init(&info->bl, NULL, true); + if (rc) { + pb_log("Failed to init mtd device\n"); + return NULL; + } + + rc = blocklevel_get_info(info->bl, &info->path, &info->size, + &info->erase_granule); + if (rc) { + pb_log("Failed to retrieve blocklevel info\n"); + return NULL; + } + + rc = ffs_init(0, info->size, info->bl, &info->ffs, 1); + if (rc) { + pb_log("%s: Failed to init ffs\n", __func__); + goto out; + } + + rc = partition_info(info, partition); + if (rc) { + pb_log("Failed to retrieve partition info\n"); + goto out; + } + + /* Check if there is a second flash side. If there is not, or + * we fail to recognise it, ignore it and continue */ + ffs_next_side(info->ffs, &tmp, info->ecc); + if (tmp && ffs_equal(info->ffs, tmp) == false) { + pb_debug("Other side present on MTD device\n"); + info->other_side = talloc_zero(info, struct flash_info); + info->other_side->ffs = tmp; + info->other_side->bl = info->bl; + info->other_side->size = info->size; + info->other_side->path = info->path; + info->other_side->ecc = info->ecc; + info->other_side->erase_granule = info->erase_granule; + rc = partition_info(info->other_side, partition); + if (rc) + pb_log("Failed to retrieve partition info " + "for other side - ignoring\n"); + } + + pb_debug("%s Details\n", partition); + pb_debug("\tName\t\t%s\n", info->path); + pb_debug("\tFlash Size\t%u\n", info->size); + pb_debug("\tGranule\t\t%u\n", info->erase_granule); + pb_debug("\tECC\t\t%s\n", info->ecc ? "Protected" : "Unprotected"); + pb_debug("\tCurrent Side info:\n"); + pb_debug("\tIndex\t\t%u\n", info->attr_part_idx); + pb_debug("\tOffset\t\t%u\n", info->attr_data_pos); + pb_debug("\tPart. Size\t%u\n", info->attr_data_len); + if (info->other_side) { + pb_debug("\tOther Side info:\n"); + pb_debug("\tIndex\t\t%u\n", info->other_side->attr_part_idx); + pb_debug("\tOffset\t\t%u\n", info->other_side->attr_data_pos); + pb_debug("\tPart. Size\t%u\n", info->other_side->attr_data_len); + } + + return info; +out: + arch_flash_close(info->bl, NULL); + talloc_free(info); + return NULL; +} + +int flash_parse_version(void *ctx, char ***versions, bool current) +{ + char *saveptr, *tok, **tmp, *buffer; + const char *delim = "\n"; + struct flash_info *info, *cur_info; + uint32_t len; + int rc, n = 0; + + saveptr = tok = NULL; + tmp = NULL; + + info = flash_setup_buffer(ctx, "VERSION"); + if (!info) + return 0; + + if (!current && !info->other_side) + return 0; + + cur_info = current ? info->other_side : info; + + len = cur_info->attr_data_len - ecc_size(cur_info->attr_data_len); + buffer = talloc_array(cur_info, char, len); + if (!buffer) { + pb_log("%s: Failed to init buffer!\n", __func__); + goto out; + } + + rc = blocklevel_read(cur_info->bl, cur_info->attr_data_pos, + buffer, len); + if (rc) { + pb_log("Failed to read VERSION partition\n"); + goto out; + } + + /* open-power-platform */ + tok = strtok_r(buffer, delim, &saveptr); + if (tok) { + tmp = talloc_realloc(ctx, tmp, char *, n + 1); + if (!tmp) { + pb_log("%s: Failed to allocate memory\n", __func__); + goto out; + } + tmp[n++] = talloc_strdup(ctx, tok); + } + + tok = strtok_r(NULL, delim, &saveptr); + while (tok) { + /* Ignore leading tab from subsequent lines */ + tmp = talloc_realloc(ctx, tmp, char *, n + 1); + if (!tmp) { + pb_log("%s: Failed to reallocate memory\n", __func__); + n = 0; + goto out; + } + tmp[n++] = talloc_strdup(ctx, tok + 1); + tok = strtok_r(NULL, delim, &saveptr); + } + +out: + pb_debug("%d version strings read from %s side\n", + n, current ? "current" : "other"); + arch_flash_close(info->bl, NULL); + talloc_free(info); + *versions = tmp; + return n; +} diff --git a/lib/flash/flash.h b/lib/flash/flash.h new file mode 100644 index 0000000..d163d31 --- /dev/null +++ b/lib/flash/flash.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef FLASH_H +#define FLASH_H + +#include <flash/config.h> +#include <types/types.h> + +int flash_parse_version(void *ctx, char ***versions, bool current); + +#endif /* FLASH_H */ |