|
|
@@ -0,0 +1,282 @@
|
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
|
|
|
+/*
|
|
|
+ * Luxul's firmware container format
|
|
|
+ *
|
|
|
+ * Copyright 2020 Legrand AV Inc.
|
|
|
+ */
|
|
|
+
|
|
|
+#define _GNU_SOURCE
|
|
|
+
|
|
|
+#include <byteswap.h>
|
|
|
+#include <endian.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <libgen.h>
|
|
|
+#include <stddef.h>
|
|
|
+#include <stdint.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <unistd.h>
|
|
|
+
|
|
|
+#if __BYTE_ORDER == __BIG_ENDIAN
|
|
|
+#define cpu_to_le32(x) bswap_32(x)
|
|
|
+#define cpu_to_le16(x) bswap_16(x)
|
|
|
+#define le32_to_cpu(x) bswap_32(x)
|
|
|
+#define le16_to_cpu(x) bswap_16(x)
|
|
|
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
|
|
+#define cpu_to_le32(x) (x)
|
|
|
+#define cpu_to_le16(x) (x)
|
|
|
+#define le32_to_cpu(x) (x)
|
|
|
+#define le16_to_cpu(x) (x)
|
|
|
+#endif
|
|
|
+
|
|
|
+#define min(a, b) \
|
|
|
+ ({ \
|
|
|
+ __typeof__ (a) _a = (a); \
|
|
|
+ __typeof__ (b) _b = (b); \
|
|
|
+ _a < _b ? _a : _b; \
|
|
|
+ })
|
|
|
+
|
|
|
+#define max(a, b) \
|
|
|
+ ({ \
|
|
|
+ __typeof__ (a) _a = (a); \
|
|
|
+ __typeof__ (b) _b = (b); \
|
|
|
+ _a > _b ? _a : _b; \
|
|
|
+ })
|
|
|
+
|
|
|
+#define LXL_FLAGS_VENDOR_LUXUL 0x00000001
|
|
|
+
|
|
|
+struct lxl_hdr {
|
|
|
+ char magic[4]; /* "LXL#" */
|
|
|
+ uint32_t version;
|
|
|
+ uint32_t hdr_len;
|
|
|
+ uint8_t v0_end[0];
|
|
|
+ /* Version: 1+ */
|
|
|
+ uint32_t flags;
|
|
|
+ char board[16];
|
|
|
+ uint8_t v1_end[0];
|
|
|
+ /* Version: 2+ */
|
|
|
+ uint8_t release[4];
|
|
|
+ uint8_t v2_end[0];
|
|
|
+} __packed;
|
|
|
+
|
|
|
+static uint32_t lxlfw_hdr_len(uint32_t version)
|
|
|
+{
|
|
|
+ switch (version) {
|
|
|
+ case 0:
|
|
|
+ return offsetof(struct lxl_hdr, v0_end);
|
|
|
+ case 1:
|
|
|
+ return offsetof(struct lxl_hdr, v1_end);
|
|
|
+ case 2:
|
|
|
+ return offsetof(struct lxl_hdr, v2_end);
|
|
|
+ default:
|
|
|
+ fprintf(stderr, "Unsupported version %d\n", version);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**************************************************
|
|
|
+ * Info
|
|
|
+ **************************************************/
|
|
|
+
|
|
|
+static int lxlfw_info(int argc, char **argv) {
|
|
|
+ struct lxl_hdr hdr;
|
|
|
+ uint32_t version;
|
|
|
+ uint32_t hdr_len;
|
|
|
+ char board[17];
|
|
|
+ size_t bytes;
|
|
|
+ int err = 0;
|
|
|
+ FILE *lxl;
|
|
|
+ int flags;
|
|
|
+
|
|
|
+ if (argc < 3) {
|
|
|
+ fprintf(stderr, "Missing <file> argument\n");
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ lxl = fopen(argv[2], "r");
|
|
|
+ if (!lxl) {
|
|
|
+ fprintf(stderr, "Could not open \"%s\" file\n", argv[2]);
|
|
|
+ err = -ENOENT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ bytes = fread(&hdr, 1, sizeof(hdr), lxl);
|
|
|
+ if (bytes < offsetof(struct lxl_hdr, v0_end)) {
|
|
|
+ fprintf(stderr, "Input file too small to use Luxul format\n");
|
|
|
+ err = -ENXIO;
|
|
|
+ goto err_close;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (memcmp(hdr.magic, "LXL#", 4)) {
|
|
|
+ fprintf(stderr, "File <file> does not use Luxul's format\n");
|
|
|
+ err = -EINVAL;
|
|
|
+ goto err_close;
|
|
|
+ }
|
|
|
+
|
|
|
+ version = le32_to_cpu(hdr.version);
|
|
|
+ hdr_len = lxlfw_hdr_len(version);
|
|
|
+ if (bytes < hdr_len) {
|
|
|
+ fprintf(stderr, "Input file too small for header version %d\n", version);
|
|
|
+ err = -ENXIO;
|
|
|
+ goto err_close;
|
|
|
+ }
|
|
|
+
|
|
|
+ printf("Format version:\t%d\n", version);
|
|
|
+ printf("Header length:\t%d\n", le32_to_cpu(hdr.hdr_len));
|
|
|
+ if (version >= 1) {
|
|
|
+ printf("Flags:\t\t");
|
|
|
+ flags = le32_to_cpu(hdr.flags);
|
|
|
+ if (flags & LXL_FLAGS_VENDOR_LUXUL)
|
|
|
+ printf("VENDOR_LUXUL ");
|
|
|
+ printf("\n");
|
|
|
+ memcpy(board, hdr.board, sizeof(hdr.board));
|
|
|
+ board[16] = '\0';
|
|
|
+ printf("Board:\t\t%s\n", board);
|
|
|
+ }
|
|
|
+ if (version >= 2) {
|
|
|
+ printf("Release:\t");
|
|
|
+ if (hdr.release[0] || hdr.release[1] || hdr.release[2] || hdr.release[3]) {
|
|
|
+ printf("%hu.%hu.%hu", hdr.release[0], hdr.release[1], hdr.release[2]);
|
|
|
+ if (hdr.release[3])
|
|
|
+ printf(".%hu", hdr.release[3]);
|
|
|
+ }
|
|
|
+ printf("\n");
|
|
|
+ }
|
|
|
+
|
|
|
+err_close:
|
|
|
+ fclose(lxl);
|
|
|
+out:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/**************************************************
|
|
|
+ * Create
|
|
|
+ **************************************************/
|
|
|
+
|
|
|
+static int lxlfw_create(int argc, char **argv) {
|
|
|
+ struct lxl_hdr hdr = {
|
|
|
+ .magic = { 'L', 'X', 'L', '#' },
|
|
|
+ };
|
|
|
+ char *in_path = NULL;
|
|
|
+ uint32_t version = 0;
|
|
|
+ uint32_t hdr_len;
|
|
|
+ ssize_t bytes;
|
|
|
+ char buf[512];
|
|
|
+ int err = 0;
|
|
|
+ FILE *lxl;
|
|
|
+ FILE *in;
|
|
|
+ int c;
|
|
|
+
|
|
|
+ if (argc < 3) {
|
|
|
+ fprintf(stderr, "Missing <file> argument\n");
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ optind = 3;
|
|
|
+ while ((c = getopt(argc, argv, "i:lb:r:")) != -1) {
|
|
|
+ switch (c) {
|
|
|
+ case 'i':
|
|
|
+ in_path = optarg;
|
|
|
+ break;
|
|
|
+ case 'l':
|
|
|
+ hdr.flags |= cpu_to_le32(LXL_FLAGS_VENDOR_LUXUL);
|
|
|
+ version = max(version, 1);
|
|
|
+ break;
|
|
|
+ case 'b':
|
|
|
+ memcpy(hdr.board, optarg, strlen(optarg) > 16 ? 16 : strlen(optarg));
|
|
|
+ version = max(version, 1);
|
|
|
+ break;
|
|
|
+ case 'r':
|
|
|
+ if (sscanf(optarg, "%hhu.%hhu.%hhu.%hhu", &hdr.release[0], &hdr.release[1], &hdr.release[2], &hdr.release[3]) < 1) {
|
|
|
+ fprintf(stderr, "Failed to parse release number \"%s\"\n", optarg);
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ version = max(version, 2);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ hdr.version = cpu_to_le32(version);
|
|
|
+ hdr_len = lxlfw_hdr_len(version);
|
|
|
+ if (!hdr_len) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ hdr.hdr_len = cpu_to_le32(hdr_len);
|
|
|
+
|
|
|
+ if (!in_path) {
|
|
|
+ fprintf(stderr, "Missing input file argument\n");
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ in = fopen(in_path, "r");
|
|
|
+ if (!in) {
|
|
|
+ fprintf(stderr, "Could not open input file %s\n", in_path);
|
|
|
+ err = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ lxl = fopen(argv[2], "w+");
|
|
|
+ if (!lxl) {
|
|
|
+ fprintf(stderr, "Could not open \"%s\" file\n", argv[2]);
|
|
|
+ err = -EIO;
|
|
|
+ goto err_close_in;
|
|
|
+ }
|
|
|
+
|
|
|
+ bytes = fwrite(&hdr, 1, hdr_len, lxl);
|
|
|
+ if (bytes != hdr_len) {
|
|
|
+ fprintf(stderr, "Could not write Luxul's header\n");
|
|
|
+ err = -EIO;
|
|
|
+ goto err_close_lxl;
|
|
|
+ }
|
|
|
+
|
|
|
+ while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
|
|
|
+ if (fwrite(buf, 1, bytes, lxl) != bytes) {
|
|
|
+ fprintf(stderr, "Could not copy %zu bytes from input file\n", bytes);
|
|
|
+ err = -EIO;
|
|
|
+ goto err_close_lxl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+err_close_lxl:
|
|
|
+ fclose(lxl);
|
|
|
+err_close_in:
|
|
|
+ fclose(in);
|
|
|
+out:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/**************************************************
|
|
|
+ * Start
|
|
|
+ **************************************************/
|
|
|
+
|
|
|
+static void usage() {
|
|
|
+ printf("Usage:\n");
|
|
|
+ printf("\n");
|
|
|
+ printf("Get info about Luxul firmware:\n");
|
|
|
+ printf("\tlxlfw info <file>\n");
|
|
|
+ printf("\n");
|
|
|
+ printf("Create new Luxul firmware:\n");
|
|
|
+ printf("\tlxlfw create <file> [options]\n");
|
|
|
+ printf("\t-i file\t\t\t\tinput file for Luxul's firmware container\n");
|
|
|
+ printf("\t-l\t\t\t\tmark firmware as created by Luxul company (DON'T USE)\n");
|
|
|
+ printf("\t-b board\t\t\tboard (device) name\n");
|
|
|
+ printf("\t-r release\t\t\trelease number (e.g. 5.1.0, 7.1.0.2)\n");
|
|
|
+}
|
|
|
+
|
|
|
+int main(int argc, char **argv) {
|
|
|
+ if (argc > 1) {
|
|
|
+ if (!strcmp(argv[1], "info"))
|
|
|
+ return lxlfw_info(argc, argv);
|
|
|
+ else if (!strcmp(argv[1], "create"))
|
|
|
+ return lxlfw_create(argc, argv);
|
|
|
+ }
|
|
|
+
|
|
|
+ usage();
|
|
|
+ return 0;
|
|
|
+}
|