It's a simple tool prepending image with a Luxul header. Signed-off-by: Dan Haab <dan.haab@legrand.com> (cherry picked from commit 9aa6569aa669767ec9febf1de01a3983a1cb9482)master
parent
887eb669f9
commit
cf2b042855
@ -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; |
||||||
|
} |
Loading…
Reference in new issue