gatesgarth migration: Add support for libubootenv

This is a replacement for u-boot-fw-utils. Adapt the u-boot-fw-utils patches
to this new library and include the same logic as the u-boot-fw-utils recipe
(generate and install the U-Boot environment configuration file in the rootfs)

For now, remove the u-boot-default-env dependency, since the package doesn't
exist in zeus.

https://jira.digi.com/browse/DEL-7410

Signed-off-by: Gabriel Valcazar <gabriel.valcazar@digi.com>
This commit is contained in:
Gabriel Valcazar 2021-04-09 13:39:57 +02:00 committed by Arturo Buzarra
parent 93445f8a0b
commit 887fcae052
6 changed files with 1024 additions and 0 deletions

View File

@ -0,0 +1,606 @@
From 58977c4d6709f6ce4dc444e844b3665b031c556f Mon Sep 17 00:00:00 2001
From: Gabriel Valcazar <gabriel.valcazar@digi.com>
Date: Wed, 7 Apr 2021 09:15:07 +0200
Subject: [PATCH 1/3] Implement support for environment encryption by CAAM
Use the md5sum of HWID words (on the device tree) as key modifier. This is
based on the u-boot-fw-utils implementation of the CAAM encryption support.
Port a subset of U-Boot's md5 implementation.
https://jira.digi.com/browse/DEL-7410
https://jira.digi.com/browse/DEL-7185
https://jira.digi.com/browse/DEL-2836
Signed-off-by: Diaz de Grenu, Jose <Jose.DiazdeGrenu@digi.com>
Signed-off-by: Gonzalo Ruiz <Gonzalo.Ruiz@digi.com>
Signed-off-by: Hector Palacios <hector.palacios@digi.com>
Signed-off-by: Gabriel Valcazar <gabriel.valcazar@digi.com>
---
src/CMakeLists.txt | 2 +
src/caam_keyblob.h | 42 +++++++
src/md5.c | 275 ++++++++++++++++++++++++++++++++++++++++++++
src/md5.h | 24 ++++
src/uboot_env.c | 131 +++++++++++++++++++++
src/uboot_private.h | 4 +
6 files changed, 478 insertions(+)
create mode 100644 src/caam_keyblob.h
create mode 100644 src/md5.c
create mode 100644 src/md5.h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a04dd11..659594a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,8 @@
cmake_minimum_required (VERSION 2.6)
# Sources and private headers
SET(libubootenv_SOURCES
+ md5.c
+ md5.h
uboot_env.c
uboot_private.h
)
diff --git a/src/caam_keyblob.h b/src/caam_keyblob.h
new file mode 100644
index 0000000..e313e87
--- /dev/null
+++ b/src/caam_keyblob.h
@@ -0,0 +1,42 @@
+/*
+ * CAAM public-level include definitions for the key blob
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ */
+
+#ifndef CAAM_KEYBLOB_H
+#define CAAM_KEYBLOB_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct caam_kb_data {
+ char *rawkey;
+ size_t rawkey_len;
+ char *keyblob;
+ size_t keyblob_len;
+ char *keymod;
+ size_t keymod_len;
+};
+
+#define CAAM_KB_MAGIC 'I'
+
+/**
+ * DOC: CAAM_KB_ENCRYPT - generate a key blob from raw key
+ *
+ * Takes an caam_kb_data struct and returns it with the key blob
+ */
+#define CAAM_KB_ENCRYPT _IOWR(CAAM_KB_MAGIC, 0, struct caam_kb_data)
+
+/**
+ * DOC: CAAM_KB_DECRYPT - get keys from a key blob
+ *
+ * Takes an caam_kb_data struct and returns it with the raw key.
+ */
+#define CAAM_KB_DECRYPT _IOWR(CAAM_KB_MAGIC, 1, struct caam_kb_data)
+
+#ifndef GENMEM_KEYMOD_LEN
+#define GENMEM_KEYMOD_LEN 16
+#endif
+
+#endif /* CAAM_KEYBLOB_H */
diff --git a/src/md5.c b/src/md5.c
new file mode 100644
index 0000000..47ae8bf
--- /dev/null
+++ b/src/md5.c
@@ -0,0 +1,275 @@
+/*
+ * This file was transplanted with slight modifications from Linux sources
+ * (fs/cifs/md5.c) into U-Boot by Bartlomiej Sieka <tur@semihalf.com>.
+ */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code slightly modified to fit into Samba by
+ abartlet@samba.org Jun 2001
+ and to fit the cifs vfs by
+ Steve French sfrench@us.ibm.com */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "md5.h"
+
+static void
+MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32_t t;
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void
+MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void
+MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ register uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memmove(p, buf, len);
+ return;
+ }
+ memmove(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memmove(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memmove(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void
+MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned int count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ctx->in32[14] = ctx->bits[0];
+ ctx->in32[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memmove(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ * Calculate and store in 'output' the MD5 digest of 'len' bytes at
+ * 'input'. 'output' must have enough space to hold 16 bytes.
+ */
+void
+md5 (unsigned char *input, int len, unsigned char output[16])
+{
+ struct MD5Context context;
+
+ MD5Init(&context);
+ MD5Update(&context, input, len);
+ MD5Final(output, &context);
+}
diff --git a/src/md5.h b/src/md5.h
new file mode 100644
index 0000000..02a9a9d
--- /dev/null
+++ b/src/md5.h
@@ -0,0 +1,24 @@
+/*
+ * This file was transplanted with slight modifications from Linux sources
+ * (fs/cifs/md5.h) into U-Boot by Bartlomiej Sieka <tur@semihalf.com>.
+ */
+
+#ifndef _MD5_H
+#define _MD5_H
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ union {
+ unsigned char in[64];
+ uint32_t in32[16];
+ };
+};
+
+/*
+ * Calculate and store in 'output' the MD5 digest of 'len' bytes at
+ * 'input'. 'output' must have enough space to hold 16 bytes.
+ */
+void md5 (unsigned char *input, int len, unsigned char output[16]);
+
+#endif /* _MD5_H */
diff --git a/src/uboot_env.c b/src/uboot_env.c
index c9a900f..53b3576 100644
--- a/src/uboot_env.c
+++ b/src/uboot_env.c
@@ -33,11 +33,21 @@
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <zlib.h>
+#include <arpa/inet.h>
#include <mtd/mtd-user.h>
#include <mtd/ubi-user.h>
+#include "caam_keyblob.h"
+#include "md5.h"
#include "uboot_private.h"
+/*
+ * The BLOB includes a random AES-256 key (32 bytes) and a
+ * Message Authentication Code (MAC) (16 bytes)
+ */
+#define BLOB_OVERHEAD 48
+#define CAAM_KEY_DEV "/dev/caam_kb"
+
#define UBI_MAX_VOLUME 128
#define DEVICE_MTD_NAME "/dev/mtd"
@@ -840,6 +850,105 @@ static int set_obsolete_flag(struct uboot_flash_env *dev)
return ret;
}
+static int is_env_encrypted(void)
+{
+ const char *dt_prop = "/proc/device-tree/digi,uboot-env,encrypted";
+
+ return access(dt_prop, F_OK) != -1;
+}
+
+#define MAX_HWID_WORDS 4
+static int env_caam_get_keymod(unsigned char output[16])
+{
+ int i;
+ int len;
+ int fd;
+ uint32_t ocotp_hwid[MAX_HWID_WORDS];
+ char dt_prop[32];
+
+ for (i = 0; i < MAX_HWID_WORDS; i++) {
+ sprintf(dt_prop, "/proc/device-tree/digi,hwid_%d", i);
+ if (access(dt_prop, F_OK) != -1) {
+ char buf[sizeof(uint32_t)];
+
+ fd = open(dt_prop, O_RDONLY);
+ if (fd < 0)
+ return fd;
+ len = read(fd, buf, sizeof(uint32_t));
+ if (len < 0) {
+ close(fd);
+ return -1;
+ }
+ ocotp_hwid[i] = ntohl(*(uint32_t *)buf);
+ close(fd);
+ } else {
+ break;
+ }
+ }
+
+ /* Calculate md5sum on the raw HWID array */
+ md5((unsigned char *)(&ocotp_hwid), sizeof(uint32_t) * i, output);
+
+ return 0;
+}
+
+static int env_caam_crypt(char *data, unsigned int size, const int enc)
+{
+ struct caam_kb_data enc_data;
+ int fd;
+ int ret = 0;
+ const int len = size;
+ int ioctl_mode;
+ char *buffer;
+ unsigned char key_modifier[16];
+
+ ret = env_caam_get_keymod(key_modifier);
+ if (ret)
+ return ret;
+
+ enc_data.keymod = (char *)key_modifier;
+ enc_data.keymod_len = sizeof(key_modifier);
+
+ enc_data.keyblob_len = len;
+ enc_data.rawkey_len = len - BLOB_OVERHEAD;
+
+ buffer = malloc(len);
+ if (!buffer) {
+ printf("Could not allocate memory\n");
+ return -1;
+ }
+
+ if (enc) {
+ enc_data.rawkey = data;
+ ioctl_mode = CAAM_KB_ENCRYPT;
+ enc_data.keyblob = buffer;
+ } else {
+ enc_data.keyblob = data;
+ ioctl_mode = CAAM_KB_DECRYPT;
+ enc_data.rawkey = buffer;
+ }
+
+ if ((fd = open(CAAM_KEY_DEV, O_RDWR)) < 0) {
+ ret = fd;
+ goto free;
+ }
+
+ ret = ioctl(fd, ioctl_mode, &enc_data);
+ if (ret) {
+ printf("CAAM_KEY_DEV ioctl failed: %d\n", ret);
+ goto out;
+ }
+
+ memcpy(data, buffer, enc ? len : len - BLOB_OVERHEAD);
+
+out:
+ close(fd);
+free:
+ free(buffer);
+
+ return ret;
+}
+
int libuboot_env_store(struct uboot_ctx *ctx)
{
struct var_entry *entry;
@@ -914,6 +1023,15 @@ int libuboot_env_store(struct uboot_ctx *ctx)
((struct uboot_env_redund *)image)->flags = flags;
}
+ if (ctx->encrypted) {
+ ret = env_caam_crypt(data, ctx->usable_size, 1);
+ if (ret) {
+ fprintf(stderr,
+ "Error: can't encrypt env for flash\n");
+ return ret;
+ }
+ }
+
*(uint32_t *)image = crc32(0, (uint8_t *)data, ctx->size - offsetdata);
copy = ctx->redundant ? (ctx->current ? 0 : 1) : 0;
@@ -978,6 +1096,13 @@ static int libuboot_load(struct uboot_ctx *ctx)
}
crc = *(uint32_t *)(buf[i] + offsetcrc);
dev->crc = crc32(0, (uint8_t *)data, ctx->size - offsetdata);
+ if (ctx->encrypted) {
+ ret = env_caam_crypt((char *)data, ctx->usable_size, 0);
+ if (ret) {
+ fprintf(stderr, "Error: can't decrypt environment\n");
+ return ret;
+ }
+ }
crcenv[i] = dev->crc == crc;
if (ctx->redundant)
dev->flags = *(uint8_t *)(buf[i] + offsetflags);
@@ -1258,6 +1383,11 @@ int libuboot_read_config(struct uboot_ctx *ctx, const char *config)
break;
}
}
+
+ ctx->usable_size = ctx->size - sizeof(uint32_t);
+ if (ctx->redundant)
+ ctx->usable_size -= sizeof(char);
+
if (ndev == 0)
retval = -EINVAL;
@@ -1446,6 +1576,7 @@ int libuboot_initialize(struct uboot_ctx **out,
return -ENOMEM;
ctx->valid = false;
+ ctx->encrypted = is_env_encrypted();
ret = libuboot_configure(ctx, envdevs);
if (ret < 0) {
diff --git a/src/uboot_private.h b/src/uboot_private.h
index 4b7a9f9..22c8c14 100644
--- a/src/uboot_private.h
+++ b/src/uboot_private.h
@@ -111,10 +111,14 @@ LIST_HEAD(vars, var_entry);
struct uboot_ctx {
/** true if the environment is redundant */
bool redundant;
+ /** true if the environment is encrypted */
+ bool encrypted;
/** set to valid after a successful load */
bool valid;
/** size of the environment */
size_t size;
+ /** usable environment size */
+ unsigned int usable_size;
/** devices where environment is stored */
struct uboot_flash_env envdevs[2];
/** Set which device contains the current(last valid) environment */
--
2.17.1

View File

@ -0,0 +1,161 @@
From 00d0ef9a96cf5480146c800c64557f7c88ac9db4 Mon Sep 17 00:00:00 2001
From: Gabriel Valcazar <gabriel.valcazar@digi.com>
Date: Wed, 7 Apr 2021 11:35:15 +0200
Subject: [PATCH 2/3] Implement U-Boot environment access functions
Keep the same function signatures as u-boot-fw-utils, so that all code making
use of this functionality remains compatible. Use the libubootenv
implementation of the fw_printenv and fw_setenv apps as reference.
https://jira.digi.com/browse/DEL-7410
Signed-off-by: Javier Viguera <javier.viguera@digi.com>
Signed-off-by: Jose Diaz de Grenu <Jose.DiazdeGrenu@digi.com>
Signed-off-by: Gonzalo Ruiz <Gonzalo.Ruiz@digi.com>
Signed-off-by: Gabriel Valcazar <gabriel.valcazar@digi.com>
---
src/libuboot.h | 23 ++++++++++++
src/uboot_env.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 119 insertions(+)
diff --git a/src/libuboot.h b/src/libuboot.h
index bfcaeb1..b15969f 100644
--- a/src/libuboot.h
+++ b/src/libuboot.h
@@ -159,6 +159,29 @@ const char *libuboot_getname(void *entry);
*/
const char *libuboot_getvalue(void *entry);
+/*
+ * Get U-Boot's environment variable.
+ *
+ * Params:
+ * 'name' (input) Name of the environment variable
+ * 'value' (output) Pointer to the variable's value
+ * (NULL if not found)
+ *
+ * Return: 0 on sucess, -1 on failure
+ */
+int uboot_getenv(char *name, const char **value);
+
+/*
+ * Set U-Boot's environment variable.
+ *
+ * Params:
+ * 'name' (input) Name of the environment variable
+ * 'value' (input) Value of the environment variable
+ *
+ * Return: 0 on sucess, -1 on failure
+ */
+int uboot_setenv(char *name, char *value);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/uboot_env.c b/src/uboot_env.c
index 53b3576..bcc4af9 100644
--- a/src/uboot_env.c
+++ b/src/uboot_env.c
@@ -1617,3 +1617,99 @@ void libuboot_close(struct uboot_ctx *ctx) {
void libuboot_exit(struct uboot_ctx *ctx) {
free(ctx);
}
+
+static int uboot_common_init(struct uboot_ctx *ctx)
+{
+ const char *cfgfname = "/etc/fw_env.config";
+ const char *defenvfile = "/etc/u-boot-initial-env";
+
+ if (libuboot_read_config(ctx, cfgfname) < 0) {
+ fprintf(stderr, "Configuration file wrong or corrupted\n");
+ return -1;
+ }
+
+ if (libuboot_open(ctx) < 0) {
+ fprintf(stderr, "Cannot read environment, using default\n");
+ if (libuboot_load_file(ctx, defenvfile) < 0) {
+ fprintf(stderr, "Cannot read default environment from file\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Function: uboot_getenv
+ * Description: get U-Boot's environment variable
+ */
+int uboot_getenv(char *name, const char **value)
+{
+ struct uboot_ctx *ctx;
+ int ret = 0;
+
+ ret = libuboot_initialize(&ctx, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot initialize environment\n");
+ goto err;
+ }
+
+ ret = uboot_common_init(ctx);
+ if (ret < 0)
+ goto err;
+
+ *value = libuboot_get_env(ctx, name);
+
+err:
+ libuboot_close(ctx);
+ libuboot_exit(ctx);
+
+ return ret ? -1 : 0;
+}
+
+/*
+ * Function: uboot_setenv
+ * Description: set U-Boot's environment variable
+ */
+int uboot_setenv(char *name, char *value)
+{
+ struct uboot_ctx *ctx;
+ bool need_store = false;
+ const char *curr;
+ int ret = 0;
+
+ ret = libuboot_initialize(&ctx, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot initialize environment\n");
+ goto err;
+ }
+
+ ret = uboot_common_init(ctx);
+ if (ret < 0)
+ goto err;
+
+ curr = libuboot_get_env(ctx, name);
+ if (value == NULL) {
+ if (curr != NULL) {
+ libuboot_set_env(ctx, name, NULL);
+ need_store = true;
+ }
+ } else {
+ if (curr == NULL || strcmp(curr, value) != 0) {
+ libuboot_set_env(ctx, name, value);
+ need_store = true;
+ }
+ }
+
+ if (need_store) {
+ ret = libuboot_env_store(ctx);
+ if (ret)
+ fprintf(stderr, "Error storing the env\n");
+ }
+
+err:
+ libuboot_close(ctx);
+ libuboot_exit(ctx);
+
+ return ret ? -1 : 0;
+}
--
2.17.1

View File

@ -0,0 +1,178 @@
From cd5e40f6dbef29a04cdfabec47fd3674d764f74e Mon Sep 17 00:00:00 2001
From: Gabriel Valcazar <gabriel.valcazar@digi.com>
Date: Wed, 7 Apr 2021 13:47:37 +0200
Subject: [PATCH 3/3] tools: env: add support to set dynamic location of
environment copies
A mechanism was added in U-Boot to set the location of environment copies
dynamically in a shared area. If the config file sets both copies to the same
offset, a function will be called to set the offset of each copy to the first
two good NAND sectors within the specified area.
The config file should contain the sector size and the number of sectors of the
area, like in this example:
# Device name Offset Size Erase-size No.Blocks
/dev/mtd1 0x0 0x20000 0x20000 8
/dev/mtd1 0x0 0x20000 0x20000 8
https://jira.digi.com/browse/DUB-741
https://jira.digi.com/browse/DEL-7410
Signed-off-by: Hector Palacios <hector.palacios@digi.com>
Signed-off-by: Gonzalo Ruiz <Gonzalo.Ruiz@digi.com>
Signed-off-by: Gabriel Valcazar <gabriel.valcazar@digi.com>
---
src/uboot_env.c | 88 +++++++++++++++++++++++++++++++++++++++++++--
src/uboot_private.h | 4 +++
2 files changed, 90 insertions(+), 2 deletions(-)
diff --git a/src/uboot_env.c b/src/uboot_env.c
index bcc4af9..71fef10 100644
--- a/src/uboot_env.c
+++ b/src/uboot_env.c
@@ -431,6 +431,73 @@ static int check_env_device(struct uboot_ctx *ctx, struct uboot_flash_env *dev)
return 0;
}
+static bool set_dynamic_location(struct uboot_ctx *ctx)
+{
+ int fd, i, nsectors, rc;
+ loff_t offset, blocksize, tmp;
+ int dev = 0;
+ int copies = 1;
+ bool ret = false;
+
+ if (ctx->redundant)
+ copies++;
+
+ fd = open(ctx->envdevs[dev].devname, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open %s: %s\n", ctx->envdevs[dev].devname,
+ strerror(errno));
+ goto error;
+ }
+
+ /* Set initial block to start looking for environment */
+ offset = ctx->envdevs[dev].offset;
+ /* Use variables for common values */
+ blocksize = ctx->envdevs[dev].sectorsize;
+ /* Look for the number of sectors specified for the primary copy */
+ nsectors = ctx->envdevs[dev].envsectors;
+
+ for (i = 0; i < nsectors && copies; i++) {
+ rc = 0;
+
+ /*
+ * The implementation of is_nand_badblock() expects dev->fd to
+ * be initialized, but it isn't at this point, so re-implementat
+ * it here. Copy the offset to a temporary variable so the
+ * original offset doesn't get overwritten by the ioctl.
+ */
+ if (ctx->envdevs[dev].mtdinfo.type == MTD_NANDFLASH) {
+ tmp = offset;
+ rc = ioctl(fd, MEMGETBADBLOCK, &tmp);
+ }
+
+ if (rc < 0) {
+ goto error;
+ } else if (!rc) {
+ /*
+ * Set first good block as primary (no matter if it is
+ * the other copy. After all, the 'current' copy is
+ * determined by the active flag.
+ */
+ ctx->envdevs[dev].offset = offset;
+ copies--;
+ dev++;
+ }
+ offset += blocksize;
+ }
+
+ while (copies) {
+ /* No good sectors available. Set offset out of bounds */
+ ctx->envdevs[dev].offset = offset;
+ copies--;
+ dev++;
+ }
+ ret = true;
+
+error:
+ close(fd);
+ return ret;
+}
+
static bool check_compatible_devices(struct uboot_ctx *ctx)
{
if (!ctx->redundant)
@@ -442,6 +509,12 @@ static bool check_compatible_devices(struct uboot_ctx *ctx)
return false;
if (ctx->envdevs[0].envsize != ctx->envdevs[1].envsize)
return false;
+ if (ctx->envdevs[0].offset == ctx->envdevs[1].offset) {
+ ctx->dynamic_env = true;
+ ctx->top_limit = ctx->envdevs[0].offset +
+ (ctx->envdevs[0].envsectors * ctx->envdevs[0].sectorsize);
+ return set_dynamic_location(ctx);
+ }
return true;
}
@@ -485,7 +558,7 @@ static int fileread(struct uboot_flash_env *dev, void *data)
return ret;
}
-static int mtdread(struct uboot_flash_env *dev, void *data)
+static int mtdread(struct uboot_flash_env *dev, void *data, bool is_dynamic, loff_t top_limit)
{
size_t count;
size_t blocksize;
@@ -504,6 +577,17 @@ static int mtdread(struct uboot_flash_env *dev, void *data)
ret = read(dev->fd, data, dev->envsize);
break;
case MTD_NANDFLASH:
+ if (!is_dynamic)
+ top_limit = ((dev->offset / dev->envsize) +
+ dev->envsectors) * dev->envsize;
+
+ if (dev->offset >= top_limit) {
+ /* End of range is reached */
+ fprintf(stderr, "Too few good blocks within range\n");
+ ret = -EIO;
+ break;
+ }
+
if (dev->offset)
if (lseek(dev->fd, dev->offset, SEEK_SET) < 0) {
ret = -EIO;
@@ -579,7 +663,7 @@ static int devread(struct uboot_ctx *ctx, unsigned int copy, void *data)
ret = fileread(dev, data);
break;
case DEVICE_MTD:
- ret = mtdread(dev, data);
+ ret = mtdread(dev, data, ctx->dynamic_env, ctx->top_limit);
break;
case DEVICE_UBI:
ret = ubiread(dev, data);
diff --git a/src/uboot_private.h b/src/uboot_private.h
index 22c8c14..591df20 100644
--- a/src/uboot_private.h
+++ b/src/uboot_private.h
@@ -113,10 +113,14 @@ struct uboot_ctx {
bool redundant;
/** true if the environment is encrypted */
bool encrypted;
+ /** true if the environment is dynamic */
+ bool dynamic_env;
/** set to valid after a successful load */
bool valid;
/** size of the environment */
size_t size;
+ /** top limit of the dynamic environment */
+ loff_t top_limit;
/** usable environment size */
unsigned int usable_size;
/** devices where environment is stored */
--
2.17.1

View File

@ -0,0 +1,7 @@
# Configuration file for fw_(printenv/setenv) utility.
# Up to two entries are valid, in this case the redundant
# environment sector is assumed present.
# Device name Offset Size
/dev/mmcblk0boot1 0x1C0000 0x4000
/dev/mmcblk0boot1 0x1E0000 0x4000

View File

@ -0,0 +1,9 @@
# Configuration file for fw_(printenv/setenv) utility.
# Up to two entries are valid, in this case the redundant
# environment sector is assumed present.
# If both copies are set to the same offset, an automatic mechanism will
# determine the first good sectors where each copy lives, skipping bad blocks.
# Device name Offset Size Erase-size No.Blocks
/dev/##MTDINDEX## ##ENV_OFFSET## ##ENV_SIZE## ##ERASEBLOCK## ##NBLOCKS##
/dev/##MTDINDEX## ##ENV_REDUND_OFFSET## ##ENV_SIZE## ##ERASEBLOCK## ##NBLOCKS##

View File

@ -0,0 +1,63 @@
# Copyright (C) 2021, Digi International Inc.
FILESEXTRAPATHS_prepend := "${THISDIR}/${BPN}:"
SRC_URI += " \
file://${STORAGE_MEDIA}/fw_env.config \
file://0001-Implement-support-for-environment-encryption-by-CAAM.patch \
file://0002-Implement-U-Boot-environment-access-functions.patch \
file://0003-tools-env-add-support-to-set-dynamic-location-of-env.patch \
"
do_install_append() {
install -d ${D}${sysconfdir}
install -m 0644 ${WORKDIR}/${STORAGE_MEDIA}/fw_env.config ${D}${sysconfdir}/
}
pkg_postinst_ontarget_${PN}() {
CONFIG_FILE="/etc/fw_env.config"
MMCDEV="$(sed -ne 's,.*root=/dev/mmcblk\([0-9]\)p.*,\1,g;T;p' /proc/cmdline)"
if [ -n "${MMCDEV}" ]; then
sed -i -e "s,^/dev/mmcblk[^[:blank:]]\+,/dev/mmcblk${MMCDEV},g" ${CONFIG_FILE}
fi
PARTTABLE="/proc/mtd"
MTDINDEX="$(sed -ne "s/\(^mtd[0-9]\+\):.*\<environment\>.*/\1/g;T;p" ${PARTTABLE} 2>/dev/null)"
if [ -n "${MTDINDEX}" ]; then
# Initialize variables for fixed offset values
# (backwards compatible with old U-Boot)
ENV_OFFSET="${UBOOT_ENV_OFFSET}"
ENV_REDUND_OFFSET="${UBOOT_ENV_SIZE}"
ENV_SIZE="${UBOOT_ENV_SIZE}"
ERASEBLOCK=""
NBLOCKS=""
if [ -f "/proc/device-tree/digi,uboot,dynamic-env" ]; then
# Update variables for dynamic environment
# - Both copies starting at the same offset
ENV_REDUND_OFFSET="${UBOOT_ENV_OFFSET}"
# - Calculated erase block size
ERASEBLOCK="$(grep "^${MTDINDEX}:" ${PARTTABLE} | awk '{printf("0x%d",$3)}')"
# - Calculated number of blocks
MTDSIZE="$(grep "^${MTDINDEX}:" ${PARTTABLE} | awk '{printf("0x%d",$2)}')"
NBLOCKS="$(((MTDSIZE - UBOOT_ENV_OFFSET) / ERASEBLOCK))"
# If a range was provided, calculate the number of
# blocks in the range and use that number, unless they
# exceed the total number of blocks available in the
# whole partition.
if [ -n "${UBOOT_ENV_RANGE}" ]; then
RANGE_BLOCKS="$((UBOOT_ENV_RANGE / ERASEBLOCK))"
[ "${RANGE_BLOCKS}" -lt "${NBLOCKS}" ] && NBLOCKS="${RANGE_BLOCKS}"
fi
fi
# Substitute stub with configuration and calculated values
sed -i -e "s/##MTDINDEX##/${MTDINDEX}/g" \
-e "s/##ENV_OFFSET##/${ENV_OFFSET}/g" \
-e "s/##ENV_REDUND_OFFSET##/${ENV_REDUND_OFFSET}/g" \
-e "s/##ENV_SIZE##/${ENV_SIZE}/g" \
-e "s/##ERASEBLOCK##/${ERASEBLOCK}/g" \
-e "s/##NBLOCKS##/${NBLOCKS}/g" \
${CONFIG_FILE}
fi
}