lib/flash: Add support for platform versions
authorSamuel Mendoza-Jonas <sam.mj@au1.ibm.com>
Mon, 14 Dec 2015 23:02:31 +0000 (10:02 +1100)
committerSam Mendoza-Jonas <sam@mendozajonas.com>
Tue, 9 Feb 2016 02:40:15 +0000 (13:40 +1100)
Add basic libflash support to read the VERSION partition on BMC
machines. This adds a dependency on the libflash shared library from
Skiboot.
The MTD partition is accessed through the libflash helper functions.
Once read into a buffer, the VERSION partition is a simple list of
newline-terminated version strings. Some BMC platforms may have two
'sides' to flash - these are interpreted as the "current" and "other"
sides depending on which one the current Petitboot is running from.

Signed-off-by: Sam Mendoza-Jonas <sam@mendozajonas.com>
lib/Makefile.am
lib/flash/config.h [new file with mode: 0644]
lib/flash/flash.c [new file with mode: 0644]
lib/flash/flash.h [new file with mode: 0644]

index a2421a52f498c0e69363c996eaa334d4dc4e0f7b..a3ae943861ba4f89f1b2230083e853f13800fbde 100644 (file)
@@ -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 (file)
index 0000000..a132a01
--- /dev/null
@@ -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 (file)
index 0000000..1056c63
--- /dev/null
@@ -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 (file)
index 0000000..d163d31
--- /dev/null
@@ -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 */