summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-07-28 07:22:40 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2014-08-07 06:13:52 +0200
commit97e81f2d78f30fb4936f0f6fe52b317d8dbc9881 (patch)
treeb260ea65a17293c2e826ce03930052106bcfc618 /common
parent7b0d00c46566f1abe261c79efa63baa88ae55e47 (diff)
downloadbarebox-97e81f2d78f30fb4936f0f6fe52b317d8dbc9881.tar.gz
barebox-97e81f2d78f30fb4936f0f6fe52b317d8dbc9881.tar.xz
Add support for metadata in barebox images
It's often useful to get some information about a barebox image before starting or flashing it. This patch introduces barebox Image MetaData (IMD). When enabled a barebox image will contain a list of tags containing the desired information. We have tags for: - the barebox release (2014.07.0-00160-g035de50-dirty) - the build timestamp (#741 Mon Jul 28 15:08:54 CEST 2014) - the board model the image is intended for - the device tree toplevel compatible property Also there is an additional generic key-value store which stores parameters for which no dedicated tag exists. In this patch it is used for the memory size an image supports. Since there is no fixed offset in a barebox image which can be used for storing the information, the metadata is stored somewhere in the image and found by iterating over the image. This works for most image types, but obviously not for SoC images which are encoded or encrypted in some way. There is a 'imd' tool compiled from the same sources for barebox, for the compile host and for the target, so the metadata information is available whereever needed. For device tree boards the model and of_compatible tags are automatically generated. Example output of the imd tool for a Phytec phyFLEX image: build: #889 Wed Jul 30 16:08:54 CEST 2014 release: 2014.07.0-00167-g6b2070d-dirty parameter: memsize=1024 of_compatible: phytec,imx6x-pbab01 phytec,imx6dl-pfla02 fsl,imx6dl model: Phytec phyFLEX-i.MX6 Duallite Carrier-Board Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'common')
-rw-r--r--common/Kconfig7
-rw-r--r--common/Makefile4
-rw-r--r--common/imd-barebox.c25
-rw-r--r--common/imd.c322
4 files changed, 357 insertions, 1 deletions
diff --git a/common/Kconfig b/common/Kconfig
index bba7f159c1..7ca60f2344 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -457,6 +457,13 @@ config BLSPEC
on a device and it allows the Operating System to install / update
kernels.
+config IMD
+ bool "barebox metadata support"
+
+config IMD_TARGET
+ bool "build bareboximd target tool"
+ depends on IMD
+
config KERNEL_INSTALL_TARGET
bool
depends on !SANDBOX
diff --git a/common/Makefile b/common/Makefile
index 204241c919..64c26be96f 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -43,7 +43,9 @@ obj-$(CONFIG_RESET_SOURCE) += reset_source.o
obj-$(CONFIG_SHELL_HUSH) += hush.o
obj-$(CONFIG_SHELL_SIMPLE) += parser.o
obj-$(CONFIG_UIMAGE) += image.o uimage.o
-obj-$(CONFIG_MENUTREE) += menutree.o
+obj-$(CONFIG_MENUTREE) += menutree.o
+lwl-$(CONFIG_IMD) += imd-barebox.o
+obj-$(CONFIG_IMD) += imd.o
quiet_cmd_pwd_h = PWDH $@
ifdef CONFIG_PASSWORD
diff --git a/common/imd-barebox.c b/common/imd-barebox.c
new file mode 100644
index 0000000000..e9cd37d83e
--- /dev/null
+++ b/common/imd-barebox.c
@@ -0,0 +1,25 @@
+#include <common.h>
+#include <image-metadata.h>
+#include <generated/compile.h>
+#include <generated/utsrelease.h>
+
+/*
+ * Mark a imd entry as used so that the linker cannot
+ * throw it away.
+ */
+void imd_used(const void *used)
+{
+}
+
+struct imd_header imd_start_header
+__BAREBOX_IMD_SECTION(.barebox_imd_start) = {
+ .type = cpu_to_le32(IMD_TYPE_START),
+};
+
+struct imd_header imd_end_header
+__BAREBOX_IMD_SECTION(.barebox_imd_end) = {
+ .type = cpu_to_le32(IMD_TYPE_END),
+};
+
+BAREBOX_IMD_TAG_STRING(imd_build_tag, IMD_TYPE_BUILD, UTS_VERSION, 1);
+BAREBOX_IMD_TAG_STRING(imd_release_tag, IMD_TYPE_RELEASE, UTS_RELEASE, 1);
diff --git a/common/imd.c b/common/imd.c
new file mode 100644
index 0000000000..2c837d6f25
--- /dev/null
+++ b/common/imd.c
@@ -0,0 +1,322 @@
+/*
+ * (C) Copyright 2014 Sascha Hauer, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+#ifdef __BAREBOX__
+#include <common.h>
+#include <image-metadata.h>
+#include <libfile.h>
+#include <getopt.h>
+#include <malloc.h>
+#include <fs.h>
+#endif
+
+/*
+ * imd_next - return a pointer to the next metadata field.
+ * @imd The current metadata field
+ */
+struct imd_header *imd_next(struct imd_header *imd)
+{
+ int length;
+
+ length = imd_read_length(imd);
+ length = ALIGN(length, 4);
+ length += 8;
+
+ return (void *)imd + length;
+}
+
+struct imd_header *imd_find_type(struct imd_header *imd, uint32_t type)
+{
+ imd_for_each(imd, imd)
+ if (imd_read_type(imd) == type)
+ return imd;
+
+ return NULL;
+}
+
+static int imd_next_validate(void *buf, int bufsize, int start_ofs)
+{
+ int length, size;
+ struct imd_header *imd = buf + start_ofs;
+
+ size = bufsize - start_ofs;
+
+ if (size < 8) {
+ debug("trunkated tag at offset %dd\n", start_ofs);
+ return -EINVAL;
+ }
+
+ length = imd_read_length(imd);
+ length = ALIGN(length, 4);
+ length += 8;
+
+ if (size < length) {
+ debug("tag at offset %d with size %d exceeds bufsize %d\n",
+ start_ofs, size, bufsize);
+ return -EINVAL;
+ }
+
+ debug("tag at offset %d has length %d\n", start_ofs, length);
+
+ return length;
+}
+
+static int imd_validate_tags(void *buf, int bufsize, int start_ofs)
+{
+ int ret;
+ struct imd_header *imd = buf + start_ofs;
+
+ while (1) {
+ uint32_t type;
+
+ ret = imd_next_validate(buf, bufsize, start_ofs);
+ if (ret < 0) {
+ debug("Invalid tag at offset %d\n", start_ofs);
+ return -EINVAL;
+ }
+
+ imd = (void *)imd + ret;
+ start_ofs += ret;
+
+ type = imd_read_type(imd);
+
+ if (!imd_type_valid(type)) {
+ debug("Invalid: tag: 0x%08x\n", type);
+ return -EINVAL;
+ }
+
+ if (type == IMD_TYPE_END)
+ return 0;
+ }
+}
+
+/*
+ * imd_search_validate - find valid metadata in a buffer
+ * @buf the buffer
+ * @size buffer size
+ * @start The returned pointer to the metadata
+ *
+ * This iterates over a buffer and searches for metadata. The metadata
+ * is checked for consistency (length fields not exceeding buffer and
+ * presence of end header) and returned in @start. The returned pointer
+ * is only valid when 0 is returned. The returned data may be unaligned.
+ */
+static int imd_search_validate(void *buf, int size, struct imd_header **start)
+{
+ int start_ofs = 0;
+ int i, ret;
+
+ for (i = start_ofs; i < size - 32; i++) {
+ uint32_t type;
+
+ type = imd_read_le32(buf + i);
+
+ if (type != IMD_TYPE_START)
+ continue;
+
+ debug("Start tag found at offset %d\n", i);
+
+ ret = imd_validate_tags(buf, size, i);
+ if (!ret) {
+ *start = buf + i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+struct imd_type_names {
+ uint32_t type;
+ const char *name;
+};
+
+static struct imd_type_names imd_types[] = {
+ {
+ .type = IMD_TYPE_RELEASE,
+ .name = "release",
+ }, {
+ .type = IMD_TYPE_BUILD,
+ .name = "build",
+ }, {
+ .type = IMD_TYPE_MODEL,
+ .name = "model",
+ }, {
+ .type = IMD_TYPE_PARAMETER,
+ .name = "parameter",
+ }, {
+ .type = IMD_TYPE_OF_COMPATIBLE,
+ .name = "of_compatible",
+ },
+};
+
+static const char *imd_type_to_name(uint32_t type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imd_types); i++)
+ if (imd_types[i].type == type)
+ return imd_types[i].name;
+
+ return "unknown";
+}
+
+static uint32_t imd_name_to_type(const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imd_types); i++)
+ if (!strcmp(imd_types[i].name, name))
+ return imd_types[i].type;
+
+ return IMD_TYPE_INVALID;
+}
+
+static char *imd_string_data(struct imd_entry_string *imd_string, int index)
+{
+ int i, total = 0, l = 0;
+ int len = imd_read_length(&imd_string->header);
+ char *p = imd_string->data;
+
+ for (i = 0; total < len; total += l, p += l) {
+ l = strlen(p) + 1;
+ if (i++ == index)
+ return p;
+ }
+
+ return NULL;
+}
+
+static char *imd_concat_strings(struct imd_entry_string *imd_string)
+{
+ int i, len = imd_read_length(&imd_string->header);
+ char *str;
+
+ str = malloc(len);
+ if (!str)
+ return NULL;
+
+ memcpy(str, imd_string->data, len);
+
+ for (i = 0; i < len - 1; i++)
+ if (str[i] == 0)
+ str[i] = ' ';
+
+ return str;
+}
+
+int imd_command_verbose;
+
+int imd_command(int argc, char *argv[])
+{
+ int ret, opt, strno = -1;
+ void *buf;
+ size_t size;
+ uint32_t type = IMD_TYPE_INVALID;
+ struct imd_header *imd_start, *imd;
+ const char *filename;
+ const char *variable_name = NULL;
+ char *str;
+
+ imd_command_verbose = 0;
+
+ while ((opt = getopt(argc, argv, "vt:s:n:")) > 0) {
+ switch(opt) {
+ case 't':
+ type = imd_name_to_type(optarg);
+ if (type == IMD_TYPE_INVALID) {
+ fprintf(stderr, "no such type: %s\n", optarg);
+ return -ENOSYS;
+ }
+ break;
+ case 's':
+ variable_name = optarg;
+ break;
+ case 'v':
+ imd_command_verbose = 1;
+ break;
+ case 'n':
+ strno = simple_strtoul(optarg, NULL, 0);
+ break;
+ default:
+ return -ENOSYS;
+ }
+ }
+
+ if (optind == argc) {
+ fprintf(stderr, "No image given\n");
+ return -ENOSYS;
+ }
+
+ filename = argv[optind];
+
+ ret = read_file_2(filename, &size, &buf, 0x100000);
+ if (ret && ret != -EFBIG)
+ return -errno;
+
+ ret = imd_search_validate(buf, size, &imd_start);
+ if (ret)
+ return ret;
+
+ if (type == IMD_TYPE_INVALID) {
+ imd_for_each(imd_start, imd) {
+ uint32_t type = imd_read_type(imd);
+
+ if (imd_is_string(type)) {
+ struct imd_entry_string *imd_string =
+ (struct imd_entry_string *)imd;
+
+ str = imd_concat_strings(imd_string);
+
+ printf("%s: %s\n", imd_type_to_name(type), str);
+ } else {
+ debug("Unknown tag 0x%08x\n", type);
+ }
+ }
+ } else {
+ imd = imd_find_type(imd_start, type);
+ if (!imd) {
+ debug("No tag of type 0x%08x found\n", type);
+ return -ENODATA;
+ }
+
+ if (imd_is_string(type)) {
+ struct imd_entry_string *imd_string =
+ (struct imd_entry_string *)imd;
+
+ if (strno >= 0)
+ str = imd_string_data(imd_string, strno);
+ else
+ str = imd_concat_strings(imd_string);
+
+ if (!str)
+ return -ENODATA;
+
+ if (variable_name)
+ imd_command_setenv(variable_name, str);
+ else
+ printf("%s\n", str);
+
+ if (strno < 0)
+ free(str);
+ } else {
+ printf("tag 0x%08x present\n", type);
+ }
+ }
+
+ return 0;
+}