aboutsummaryrefslogtreecommitdiff
path: root/external/gard/gard.c
diff options
context:
space:
mode:
Diffstat (limited to 'external/gard/gard.c')
-rw-r--r--external/gard/gard.c777
1 files changed, 777 insertions, 0 deletions
diff --git a/external/gard/gard.c b/external/gard/gard.c
new file mode 100644
index 0000000..c43e05a
--- /dev/null
+++ b/external/gard/gard.c
@@ -0,0 +1,777 @@
+/* Copyright 2013-2015 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 <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include <mtd/mtd-abi.h>
+
+#include <getopt.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include <libflash/file_flash.h>
+#include <libflash/ecc.h>
+
+#include "gard.h"
+
+#define FDT_ACTIVE_FLASH_PATH "/proc/device-tree/chosen/ibm,system-flash"
+#define SYSFS_MTD_PATH "/sys/class/mtd/"
+#define FLASH_GARD_PART "GUARD"
+
+struct gard_ctx {
+ int fd;
+ bool readonly;
+ bool ecc;
+ uint32_t f_size;
+ uint32_t f_pos;
+
+ uint32_t gard_part_idx;
+ uint32_t gard_data_pos;
+ uint32_t gard_data_len;
+
+ struct spi_flash_ctrl *fct;
+ struct flash_chip *fch;
+ struct ffs_handle *ffs;
+};
+
+/*
+ * Return the size of a struct gard_ctx depending on if the buffer contains
+ * ECC bits
+ */
+static inline size_t sizeof_gard(struct gard_ctx *ctx)
+{
+ return ctx->ecc ? ECC_BUFFER_SIZE(sizeof(struct gard_record)) : sizeof(struct gard_record);
+}
+
+static void show_flash_err(int rc)
+{
+ switch (rc) {
+ case FFS_ERR_BAD_MAGIC:
+ fprintf(stderr, "libffs bad magic\n");
+ break;
+ case FFS_ERR_BAD_VERSION:
+ fprintf(stderr, "libffs bad version\n");
+ break;
+ case FFS_ERR_BAD_CKSUM:
+ fprintf(stderr, "libffs bad check sum\n");
+ break;
+ case FFS_ERR_PART_NOT_FOUND:
+ fprintf(stderr, "libffs flash partition not found\n");
+ break;
+ /* ------- */
+ case FLASH_ERR_MALLOC_FAILED:
+ fprintf(stderr, "libflash malloc failed\n");
+ break;
+ case FLASH_ERR_CHIP_UNKNOWN:
+ fprintf(stderr, "libflash unknown flash chip\n");
+ break;
+ case FLASH_ERR_PARM_ERROR:
+ fprintf(stderr, "libflash parameter error\n");
+ break;
+ case FLASH_ERR_ERASE_BOUNDARY:
+ fprintf(stderr, "libflash erase boundary error\n");
+ break;
+ case FLASH_ERR_WREN_TIMEOUT:
+ fprintf(stderr, "libflash WREN timeout\n");
+ break;
+ case FLASH_ERR_WIP_TIMEOUT:
+ fprintf(stderr, "libflash WIP timeout\n");
+ break;
+ case FLASH_ERR_VERIFY_FAILURE:
+ fprintf(stderr, "libflash verification failure\n");
+ break;
+ case FLASH_ERR_4B_NOT_SUPPORTED:
+ fprintf(stderr, "libflash 4byte mode not supported\n");
+ break;
+ case FLASH_ERR_CTRL_CONFIG_MISMATCH:
+ fprintf(stderr, "libflash control config mismatch\n");
+ break;
+ case FLASH_ERR_CHIP_ER_NOT_SUPPORTED:
+ fprintf(stderr, "libflash chip not supported\n");
+ break;
+ case FLASH_ERR_CTRL_CMD_UNSUPPORTED:
+ fprintf(stderr, "libflash unsupported control command\n");
+ break;
+ case FLASH_ERR_CTRL_TIMEOUT:
+ fprintf(stderr, "libflash control timeout\n");
+ break;
+ case FLASH_ERR_ECC_INVALID:
+ fprintf(stderr, "libflash ecc invalid\n");
+ break;
+ default:
+ fprintf(stderr, "A libflash/libffs error has occured %d\n", rc);
+ }
+}
+
+static const char *target_type_to_str(enum target_type t)
+{
+ switch (t) {
+ case TYPE_NA:
+ return "Not applicable";
+ case TYPE_SYS:
+ return "System";
+ case TYPE_NODE:
+ return "Node";
+ case TYPE_DIMM:
+ return "Dimm";
+ case TYPE_MEMBUF:
+ return "Memory Buffer";
+ case TYPE_PROC:
+ return "Processor";
+ case TYPE_EX:
+ return "EX";
+ case TYPE_CORE:
+ return "Core";
+ case TYPE_L2:
+ return "L2 cache";
+ case TYPE_L3:
+ return "L3 cache";
+ case TYPE_L4:
+ return "L4 cache";
+ case TYPE_MCS:
+ return "MSC";
+ case TYPE_MBA:
+ return "MBA";
+ case TYPE_XBUS:
+ return "XBUS";
+ case TYPE_ABUS:
+ return "ABUS";
+ case TYPE_PCI:
+ return "PCI";
+ case TYPE_DPSS:
+ return "DPSS";
+ case TYPE_APSS:
+ return "APSS";
+ case TYPE_OCC:
+ return "OCC";
+ case TYPE_PSI:
+ return "PSI";
+ case TYPE_FSP:
+ return "FSP";
+ case TYPE_PNOR:
+ return "PNOR";
+ case TYPE_OSC:
+ return "OSC";
+ case TYPE_TODCLK:
+ return "Time of day clock";
+ case TYPE_CONTROL_NODE:
+ return "Control Node";
+ case TYPE_OSCREFCLK:
+ return "OSC Ref Clock";
+ case TYPE_OSCPCICLK:
+ return "OSC PCI Clock";
+ case TYPE_REFCLKENDPT:
+ return "Ref Clock";
+ case TYPE_PCICLKENDPT:
+ return "PCI Clock";
+ case TYPE_NX:
+ return "NX";
+ case TYPE_PORE:
+ return "PORE";
+ case TYPE_PCIESWITCH:
+ return "PCIE Switch";
+ case TYPE_CAPP:
+ return "CAPP";
+ case TYPE_FSI:
+ return "FSI";
+ case TYPE_TEST_FAIL:
+ return "Test Fail";
+ case TYPE_LAST_IN_RANGE:
+ return "Last";
+ }
+ return "Unknown";
+}
+
+static const char *path_type_to_str(enum path_type t)
+{
+ switch (t) {
+ case PATH_NA:
+ return "not applicable";
+ case PATH_AFFINITY:
+ return "affinity";
+ case PATH_PHYSICAL:
+ return "physical";
+ case PATH_DEVICE:
+ return "device";
+ case PATH_POWER:
+ return "power";
+ }
+ return "Unknown";
+}
+
+static bool get_dev_attr(const char *dev, const char *attr_file, uint32_t *attr)
+{
+ char dev_path[PATH_MAX] = SYSFS_MTD_PATH;
+ /*
+ * Needs to be large enough to hold at most uint32_t represented as a
+ * string in hex with leading 0x
+ */
+ char attr_buf[10];
+ int fd, rc;
+
+ /*
+ * sizeof(dev_path) - (strlen(dev_path) + 1) is the remaining space in
+ * dev_path, + 1 to account for the '\0'. As strncat could write n+1 bytes
+ * to dev_path the correct calulcation for n is:
+ * (sizeof(dev_path) - (strlen(dev_path) + 1) - 1)
+ */
+ strncat(dev_path, dev, (sizeof(dev_path) - (strlen(dev_path) + 1) - 1));
+ strncat(dev_path, "/", (sizeof(dev_path) - (strlen(dev_path) + 1) - 1));
+ strncat(dev_path, attr_file, (sizeof(dev_path) - (strlen(dev_path) + 1) - 1));
+ fd = open(dev_path, O_RDONLY);
+ if (fd == -1)
+ goto out;
+
+ rc = read(fd, attr_buf, sizeof(attr_buf));
+ close(fd);
+ if (rc == -1)
+ goto out;
+
+ if (attr)
+ *attr = strtol(attr_buf, NULL, 0);
+
+ return 0;
+
+out:
+ fprintf(stderr, "Couldn't get MTD device attribute '%s' from '%s'\n", dev, attr_file);
+ return -1;
+}
+
+static int open_from_dev(struct gard_ctx *ctx, const char *fdt_flash_path)
+{
+ struct dirent **namelist;
+ char fdt_node_path[PATH_MAX];
+ int count, i, rc, fd;
+ bool done;
+
+ if (!fdt_flash_path)
+ return -1;
+
+ fd = open(fdt_flash_path, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Couldn't open '%s' FDT attribute to determine which flash device to use\n",
+ fdt_flash_path);
+ return -1;
+ }
+
+ rc = read(fd, fdt_node_path, sizeof(fdt_node_path));
+ close(fd);
+ if (rc == -1) {
+ fprintf(stderr, "Couldn't read flash FDT node from '%s'\n", fdt_flash_path);
+ return -1;
+ }
+
+ count = scandir(SYSFS_MTD_PATH, &namelist, NULL, alphasort);
+ if (count == -1) {
+ fprintf(stderr, "Couldn't scan '%s' for MTD devices\n", SYSFS_MTD_PATH);
+ return -1;
+ }
+
+ rc = 0;
+ done = false;
+ for (i = 0; i < count; i++) {
+ struct dirent *dirent;
+ char dev_path[PATH_MAX] = SYSFS_MTD_PATH;
+ char fdt_node_path_tmp[PATH_MAX];
+
+ dirent = namelist[i];
+ if (dirent->d_name[0] == '.' || rc || done) {
+ free(namelist[i]);
+ continue;
+ }
+
+ strncat(dev_path, dirent->d_name, sizeof(dev_path) - strlen(dev_path) - 2);
+ strncat(dev_path, "/device/of_node", sizeof(dev_path) - strlen(dev_path) - 2);
+
+ rc = readlink(dev_path, fdt_node_path_tmp, sizeof(fdt_node_path_tmp) - 1);
+ if (rc == -1) {
+ /*
+ * This might fail because it could not exist if the system has flash
+ * devices that present as mtd but don't have corresponding FDT
+ * nodes, just continue silently.
+ */
+ free(namelist[i]);
+ /* Should still try the next dir so reset rc */
+ rc = 0;
+ continue;
+ }
+ fdt_node_path_tmp[rc] = '\0';
+
+ if (strstr(fdt_node_path_tmp, fdt_node_path)) {
+ uint32_t flags, size;
+
+ /*
+ * size and flags could perhaps have be gotten another way but this
+ * method is super unlikely to fail so it will do.
+ */
+
+ /* Check to see if device is writeable */
+ rc = get_dev_attr(dirent->d_name, "flags", &flags);
+ if (rc) {
+ free(namelist[i]);
+ continue;
+ }
+
+ /* Get the size of the mtd device while we're at it */
+ rc = get_dev_attr(dirent->d_name, "size", &size);
+ if (rc) {
+ free(namelist[i]);
+ continue;
+ }
+
+ strcpy(dev_path, "/dev/");
+ strncat(dev_path, dirent->d_name, sizeof(dev_path) - strlen(dev_path) - 2);
+ ctx->readonly = !(flags && MTD_WRITEABLE);
+ ctx->f_size = size;
+ ctx->fd = open(dev_path, ctx->readonly ? O_RDONLY : O_RDWR);
+ if (ctx->fd == -1) {
+ fprintf(stderr, "Couldn't open MTD device '%s' for %s as the system flash device\n",
+ dev_path, ctx->readonly ? "reading" : "read/write");
+ rc = -1;
+ }
+ done = true;
+ }
+
+ free(namelist[i]);
+ }
+ free(namelist);
+
+ if (!done)
+ fprintf(stderr, "Couldn't find '%s' corresponding MTD\n", fdt_flash_path);
+
+ /* explicit negative value so as to not return a libflash code */
+ return done ? rc : -1;
+}
+
+static int open_from_file(struct gard_ctx *ctx, const char *filename)
+{
+ struct stat sbuf;
+ int rc;
+
+ rc = stat(filename, &sbuf);
+ if (rc == -1) {
+ fprintf(stderr, "Couldn't stat '%s' to use as flash data\n", filename);
+ return -1;
+ }
+
+ ctx->fd = open(filename, O_RDWR);
+ if (ctx->fd == -1) {
+ fprintf(stderr, "Couldn't open '%s' to use as flash data\n", filename);
+ return -1;
+ }
+
+ ctx->readonly = 0;
+ ctx->f_size = sbuf.st_size;
+ return 0;
+}
+
+static int do_iterate(struct gard_ctx *ctx,
+ int (*func)(struct gard_ctx *ctx, int pos,
+ struct gard_record *gard, void *priv),
+ void *priv)
+{
+ int rc = 0;
+ unsigned int i;
+ struct gard_record gard, null_gard;
+
+ memset(&null_gard, INT_MAX, sizeof(gard));
+ for (i = 0; i * sizeof_gard(ctx) < ctx->gard_data_len && rc == 0; i++) {
+ memset(&gard, 0, sizeof(gard));
+
+ rc = flash_read_corrected(ctx->fch, ctx->gard_data_pos +
+ (i * sizeof_gard(ctx)), &gard,
+ sizeof(gard), ctx->ecc);
+
+ /* It isn't super clear what constitutes the end, this should do */
+ if (rc || memcmp(&gard, &null_gard, sizeof(gard)) == 0)
+ break;
+
+ rc = func(ctx, i, &gard, priv);
+ }
+
+ return rc;
+}
+
+static int get_largest_pos_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
+{
+ if (!priv)
+ return -1;
+
+ *(int *)priv = pos;
+
+ return 0;
+}
+
+static int get_largest_pos(struct gard_ctx *ctx)
+{
+ int rc, largest = -1;
+
+ rc = do_iterate(ctx, &get_largest_pos_i, &largest);
+ if (rc)
+ return -1;
+
+ return largest;
+}
+
+static int do_list_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
+{
+ if (!gard)
+ return -1;
+
+ printf("| %08x | %08x | %-15s |\n", be32toh(gard->record_id),
+ be32toh(gard->errlog_eid), path_type_to_str(gard->target_id.type_size >> PATH_TYPE_SHIFT));
+
+ return 0;
+}
+
+static int do_list(struct gard_ctx *ctx, int argc, char **argv)
+{
+ int rc;
+
+ /* No entries */
+ if (get_largest_pos(ctx) == -1) {
+ printf("No GARD entries to display\n");
+ rc = 0;
+ } else {
+ printf("| ID | Error | Type |\n");
+ printf("+---------------------------------------+\n");
+ rc = do_iterate(ctx, &do_list_i, NULL);
+ printf("+=======================================+\n");
+ }
+
+ return rc;
+}
+
+static int do_show_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
+{
+ uint32_t id;
+
+ if (!priv || !gard)
+ return -1;
+
+ id = *(uint32_t *)priv;
+
+ if (be32toh(gard->record_id) == id) {
+ unsigned int count, i;
+
+ printf("Record ID: 0x%08x\n", id);
+ printf("========================\n");
+ printf("Error ID: 0x%08x\n", be32toh(gard->errlog_eid));
+ printf("Error Type: 0x%02x\n", gard->error_type);
+ printf("Res Recovery: 0x%02x\n", gard->resource_recovery);
+ printf("Path Type: %s\n", path_type_to_str(gard->target_id.type_size >> PATH_TYPE_SHIFT));
+ count = gard->target_id.type_size & PATH_ELEMENTS_MASK;
+ for (i = 0; i < count && i < MAX_PATH_ELEMENTS; i++)
+ printf("%*c%s, Instance #%d\n", i + 1, '>', target_type_to_str(gard->target_id.path_elements[i].target_type),
+ gard->target_id.path_elements[i].instance);
+ }
+
+ return 0;
+}
+
+static int do_show(struct gard_ctx *ctx, int argc, char **argv)
+{
+ uint32_t id;
+ int rc;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s option requires a GARD record\n", argv[0]);
+ return -1;
+ }
+
+ id = strtoul(argv[1], NULL, 16);
+
+ rc = do_iterate(ctx, &do_show_i, &id);
+
+ return rc;
+}
+
+static int do_clear_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, void *priv)
+{
+ int largest, rc = 0;
+ char *buf;
+ struct gard_record null_gard;
+
+ if (!gard || !ctx)
+ return -1;
+
+ memset(&null_gard, INT_MAX, sizeof(null_gard));
+
+ if (!priv) {
+ if (pos != 0)
+ /* We erased everything on the first iteration, don't bother */
+ return 0;
+
+ printf("Erasing the entire gard partition...");
+ rc = flash_erase(ctx->fch, ctx->gard_data_pos, ctx->gard_data_len);
+ if (rc) {
+ fprintf(stderr, "\nCouldn't erase flash partition at 0x%08x for size %u\n",
+ ctx->gard_data_pos, ctx->gard_data_len);
+ return rc;
+ }
+ printf("done\n");
+
+ largest = 0;
+ } else if (be32toh(gard->record_id) == *(uint32_t *)priv) {
+ largest = get_largest_pos(ctx);
+ if (largest < 0 || pos > largest) {
+ /* Something went horribly wrong */
+ fprintf(stderr, "largest index out of range %d\n", largest);
+ return -1;
+ }
+
+ if (pos < largest) {
+ /* We're not clearing the last record, shift all the records up */
+ int buf_len = ((largest - pos) * sizeof(struct gard_record));
+ int buf_pos = ctx->gard_data_pos + ((pos + 1) * sizeof_gard(ctx));
+ buf = malloc(buf_len);
+ if (!buf)
+ return -ENOMEM;
+
+ rc = flash_read_corrected(ctx->fch, buf_pos, buf, buf_len, ctx->ecc);
+ if (rc) {
+ free(buf);
+ fprintf(stderr, "Couldn't read from flash at 0x%08x for len 0x%08x\n", buf_pos, buf_len);
+ return rc;
+ }
+
+ rc = flash_smart_write_corrected(ctx->fch, buf_pos - sizeof_gard(ctx), buf, buf_len, ctx->ecc);
+ free(buf);
+ if (rc) {
+ fprintf(stderr, "Couldn't write to flash at 0x%08lx for len 0x%08x\n",
+ buf_pos - sizeof_gard(ctx), buf_len);
+ return rc;
+ }
+ }
+
+ printf("Cleared gard record with id ID 0x%08x\n", be32toh(gard->record_id));
+ }
+
+ /* Now wipe the last record */
+ rc = flash_smart_write_corrected(ctx->fch, ctx->gard_data_pos + (largest * sizeof_gard(ctx)),
+ &null_gard, sizeof(null_gard), ctx->ecc);
+
+ return rc;
+}
+
+static int do_clear(struct gard_ctx *ctx, int argc, char **argv)
+{
+ int rc;
+ uint32_t id;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s option requires a GARD record or 'all'\n", argv[0]);
+ return -1;
+ }
+
+ if (strncmp(argv[1], "all", strlen("all")) == 0) {
+ rc = do_iterate(ctx, do_clear_i, NULL);
+ } else {
+ id = strtoul(argv[1], NULL, 16);
+ rc = do_iterate(ctx, do_clear_i, &id);
+ }
+
+ return rc;
+}
+
+__attribute__ ((unused))
+static int do_nop(struct gard_ctx *ctx, int argc, char **argv)
+{
+ fprintf(stderr, "Unimplemented action '%s'\n", argv[0]);
+ return EXIT_SUCCESS;
+}
+
+struct {
+ const char *name;
+ const char *desc;
+ int (*fn)(struct gard_ctx *, int, char **);
+} actions[] = {
+ { "list", "List current GARD records", do_list },
+ { "show", "Show details of a GARD record", do_show },
+ { "clear", "Clear GARD records", do_clear },
+};
+
+static void usage(const char *progname)
+{
+ unsigned int i;
+
+ fprintf(stderr, "Usage: %s [-a -e -f <file> -p] <command> [<args>]\n\n",
+ progname);
+ fprintf(stderr, "-e --ecc\n\tForce reading/writing with ECC bytes.\n\n");
+ fprintf(stderr, "-f --file <file>\n\tDon't search for MTD device,"
+ " read from <file>.\n\n");
+ fprintf(stderr, "-p --part\n\tUsed in conjunction with -f to specify"
+ "that just\n");
+ fprintf(stderr, "\tthe GUARD partition is in <file> and libffs\n");
+ fprintf(stderr, "\tshouldn't be used.\n\n");
+
+
+ fprintf(stderr, "Where <command> is one of:\n\n");
+
+ for (i = 0; i < ARRAY_SIZE(actions); i++) {
+ fprintf(stderr, "\t%-7s\t%s\n",
+ actions[i].name, actions[i].desc);
+ }
+}
+
+static struct option global_options[] = {
+ { "file", required_argument, 0, 'f' },
+ { "part", no_argument, 0, 'p' },
+ { "ecc", no_argument, 0, 'e' },
+ { 0 },
+};
+static const char *global_optstring = "+ef:p";
+
+int main(int argc, char **argv)
+{
+ const char *action, *progname, *filename = NULL;
+ const char *fdt_flash_path = FDT_ACTIVE_FLASH_PATH;
+ struct gard_ctx _ctx, *ctx;
+ int i, rc;
+ bool part = 0;
+ bool ecc = 0;
+
+ progname = argv[0];
+
+ ctx = &_ctx;
+ memset(ctx, 0, sizeof(*ctx));
+
+ /* process global options */
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, global_optstring, global_options,
+ NULL);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'e':
+ ecc = true;
+ break;
+ case 'f':
+ filename = optarg;
+ break;
+ case 'p':
+ part = true;
+ break;
+ case '?':
+ usage(progname);
+ return EXIT_FAILURE;
+ }
+ }
+
+ /*
+ * It doesn't make sense to specify that we have the gard partition but
+ * read from flash
+ */
+ if (part && !filename) {
+ usage(progname);
+ return EXIT_FAILURE;
+ }
+
+ /* do we have a command? */
+ if (optind == argc) {
+ usage(progname);
+ return EXIT_FAILURE;
+ }
+
+ argc -= optind;
+ argv += optind;
+ action = argv[0];
+
+ if (!filename)
+ rc = open_from_dev(ctx, fdt_flash_path);
+ else
+ rc = open_from_file(ctx, filename);
+
+ if (rc)
+ return EXIT_FAILURE;
+
+ ctx->fct = build_flash_ctrl(ctx->fd);
+ if (!ctx->fct)
+ goto out;
+
+ rc = flash_init(ctx->fct, &ctx->fch);
+ if (rc)
+ goto out1;
+
+ if (!part) {
+ rc = ffs_open_flash(ctx->fch, 0, ctx->f_size, &ctx->ffs);
+ if (rc)
+ goto out2;
+
+ rc = ffs_lookup_part(ctx->ffs, FLASH_GARD_PART, &ctx->gard_part_idx);
+ if (rc)
+ goto out3;
+
+ rc = ffs_part_info(ctx->ffs, ctx->gard_part_idx, NULL, &(ctx->gard_data_pos),
+ &(ctx->gard_data_len), NULL, &(ctx->ecc));
+ if (rc)
+ goto out3;
+ } else {
+ ctx->ecc = ecc;
+ ctx->gard_data_pos = 0;
+ ctx->gard_data_len = ctx->f_size;
+ }
+
+ if (ctx->gard_data_len == 0 || ctx->gard_data_len % sizeof(struct gard_record) != 0)
+ /* Just warn for now */
+ fprintf(stderr, "The %s partition doesn't appear to be an exact multiple of"
+ "gard records in size: %lu vs %u (or partition is zero in length)\n",
+ FLASH_GARD_PART, sizeof(struct gard_record), ctx->gard_data_len);
+
+ for (i = 0; i < ARRAY_SIZE(actions); i++) {
+ if (!strcmp(actions[i].name, action)) {
+ rc = actions[i].fn(ctx, argc, argv);
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(actions)) {
+ fprintf(stderr, "%s: '%s' isn't a valid command\n", progname, action);
+ usage(progname);
+ rc = EXIT_FAILURE;
+ }
+
+out3:
+ if (ctx->ffs)
+ ffs_close(ctx->ffs);
+out2:
+ if (ctx->fch)
+ flash_exit(ctx->fch);
+out1:
+ if (ctx->fch)
+ free_flash_ctrl(ctx->fct);
+out:
+ close(ctx->fd);
+
+ if (rc > 0) {
+ show_flash_err(rc);
+ if (filename && rc == FFS_ERR_BAD_MAGIC)
+ fprintf(stderr, "Maybe you didn't give a full flash image file?\nDid you mean '--part'?\n");
+ }
+ return rc;
+}