ptgen: add Chromium OS kernel partition support
[project/firmware-utils.git] / src / lxlfw.c
1 // SPDX-License-Identifier: GPL-2.0-or-later OR MIT
2 /*
3 * Luxul's firmware container format
4 *
5 * Copyright 2020 Legrand AV Inc.
6 */
7
8 #define _GNU_SOURCE
9
10 #include <byteswap.h>
11 #include <endian.h>
12 #include <errno.h>
13 #include <libgen.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #if __BYTE_ORDER == __BIG_ENDIAN
22 #define cpu_to_le32(x) bswap_32(x)
23 #define cpu_to_le16(x) bswap_16(x)
24 #define le32_to_cpu(x) bswap_32(x)
25 #define le16_to_cpu(x) bswap_16(x)
26 #elif __BYTE_ORDER == __LITTLE_ENDIAN
27 #define cpu_to_le32(x) (x)
28 #define cpu_to_le16(x) (x)
29 #define le32_to_cpu(x) (x)
30 #define le16_to_cpu(x) (x)
31 #endif
32
33 #define min(a, b) \
34 ({ \
35 __typeof__ (a) _a = (a); \
36 __typeof__ (b) _b = (b); \
37 _a < _b ? _a : _b; \
38 })
39
40 #define max(a, b) \
41 ({ \
42 __typeof__ (a) _a = (a); \
43 __typeof__ (b) _b = (b); \
44 _a > _b ? _a : _b; \
45 })
46
47 #define LXL_FLAGS_VENDOR_LUXUL 0x00000001
48
49 struct lxl_hdr {
50 char magic[4]; /* "LXL#" */
51 uint32_t version;
52 uint32_t hdr_len;
53 uint8_t v0_end[0];
54 /* Version: 1+ */
55 uint32_t flags;
56 char board[16];
57 uint8_t v1_end[0];
58 /* Version: 2+ */
59 uint8_t release[4];
60 uint8_t v2_end[0];
61 } __packed;
62
63 static uint32_t lxlfw_hdr_len(uint32_t version)
64 {
65 switch (version) {
66 case 0:
67 return offsetof(struct lxl_hdr, v0_end);
68 case 1:
69 return offsetof(struct lxl_hdr, v1_end);
70 case 2:
71 return offsetof(struct lxl_hdr, v2_end);
72 default:
73 fprintf(stderr, "Unsupported version %d\n", version);
74 return 0;
75 }
76 }
77
78 /**************************************************
79 * Info
80 **************************************************/
81
82 static int lxlfw_info(int argc, char **argv) {
83 struct lxl_hdr hdr;
84 uint32_t version;
85 uint32_t hdr_len;
86 char board[17];
87 size_t bytes;
88 int err = 0;
89 FILE *lxl;
90 int flags;
91
92 if (argc < 3) {
93 fprintf(stderr, "Missing <file> argument\n");
94 err = -EINVAL;
95 goto out;
96 }
97
98 lxl = fopen(argv[2], "r");
99 if (!lxl) {
100 fprintf(stderr, "Could not open \"%s\" file\n", argv[2]);
101 err = -ENOENT;
102 goto out;
103 }
104
105 bytes = fread(&hdr, 1, sizeof(hdr), lxl);
106 if (bytes < offsetof(struct lxl_hdr, v0_end)) {
107 fprintf(stderr, "Input file too small to use Luxul format\n");
108 err = -ENXIO;
109 goto err_close;
110 }
111
112 if (memcmp(hdr.magic, "LXL#", 4)) {
113 fprintf(stderr, "File <file> does not use Luxul's format\n");
114 err = -EINVAL;
115 goto err_close;
116 }
117
118 version = le32_to_cpu(hdr.version);
119 hdr_len = lxlfw_hdr_len(version);
120 if (bytes < hdr_len) {
121 fprintf(stderr, "Input file too small for header version %d\n", version);
122 err = -ENXIO;
123 goto err_close;
124 }
125
126 printf("Format version:\t%d\n", version);
127 printf("Header length:\t%d\n", le32_to_cpu(hdr.hdr_len));
128 if (version >= 1) {
129 printf("Flags:\t\t");
130 flags = le32_to_cpu(hdr.flags);
131 if (flags & LXL_FLAGS_VENDOR_LUXUL)
132 printf("VENDOR_LUXUL ");
133 printf("\n");
134 memcpy(board, hdr.board, sizeof(hdr.board));
135 board[16] = '\0';
136 printf("Board:\t\t%s\n", board);
137 }
138 if (version >= 2) {
139 printf("Release:\t");
140 if (hdr.release[0] || hdr.release[1] || hdr.release[2] || hdr.release[3]) {
141 printf("%hu.%hu.%hu", hdr.release[0], hdr.release[1], hdr.release[2]);
142 if (hdr.release[3])
143 printf(".%hu", hdr.release[3]);
144 }
145 printf("\n");
146 }
147
148 err_close:
149 fclose(lxl);
150 out:
151 return err;
152 }
153
154 /**************************************************
155 * Create
156 **************************************************/
157
158 static int lxlfw_create(int argc, char **argv) {
159 struct lxl_hdr hdr = {
160 .magic = { 'L', 'X', 'L', '#' },
161 };
162 char *in_path = NULL;
163 uint32_t version = 0;
164 uint32_t hdr_len;
165 ssize_t bytes;
166 char buf[512];
167 int err = 0;
168 FILE *lxl;
169 FILE *in;
170 int c;
171
172 if (argc < 3) {
173 fprintf(stderr, "Missing <file> argument\n");
174 err = -EINVAL;
175 goto out;
176 }
177
178 optind = 3;
179 while ((c = getopt(argc, argv, "i:lb:r:")) != -1) {
180 switch (c) {
181 case 'i':
182 in_path = optarg;
183 break;
184 case 'l':
185 hdr.flags |= cpu_to_le32(LXL_FLAGS_VENDOR_LUXUL);
186 version = max(version, 1);
187 break;
188 case 'b':
189 memcpy(hdr.board, optarg, strlen(optarg) > 16 ? 16 : strlen(optarg));
190 version = max(version, 1);
191 break;
192 case 'r':
193 if (sscanf(optarg, "%hhu.%hhu.%hhu.%hhu", &hdr.release[0], &hdr.release[1], &hdr.release[2], &hdr.release[3]) < 1) {
194 fprintf(stderr, "Failed to parse release number \"%s\"\n", optarg);
195 err = -EINVAL;
196 goto out;
197 }
198 version = max(version, 2);
199 break;
200 }
201 }
202
203 hdr.version = cpu_to_le32(version);
204 hdr_len = lxlfw_hdr_len(version);
205 if (!hdr_len) {
206 err = -EINVAL;
207 goto out;
208 }
209 hdr.hdr_len = cpu_to_le32(hdr_len);
210
211 if (!in_path) {
212 fprintf(stderr, "Missing input file argument\n");
213 err = -EINVAL;
214 goto out;
215 }
216
217 in = fopen(in_path, "r");
218 if (!in) {
219 fprintf(stderr, "Could not open input file %s\n", in_path);
220 err = -EIO;
221 goto out;
222 }
223
224 lxl = fopen(argv[2], "w+");
225 if (!lxl) {
226 fprintf(stderr, "Could not open \"%s\" file\n", argv[2]);
227 err = -EIO;
228 goto err_close_in;
229 }
230
231 bytes = fwrite(&hdr, 1, hdr_len, lxl);
232 if (bytes != hdr_len) {
233 fprintf(stderr, "Could not write Luxul's header\n");
234 err = -EIO;
235 goto err_close_lxl;
236 }
237
238 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
239 if (fwrite(buf, 1, bytes, lxl) != bytes) {
240 fprintf(stderr, "Could not copy %zu bytes from input file\n", bytes);
241 err = -EIO;
242 goto err_close_lxl;
243 }
244 }
245
246 err_close_lxl:
247 fclose(lxl);
248 err_close_in:
249 fclose(in);
250 out:
251 return err;
252 }
253
254 /**************************************************
255 * Start
256 **************************************************/
257
258 static void usage() {
259 printf("Usage:\n");
260 printf("\n");
261 printf("Get info about Luxul firmware:\n");
262 printf("\tlxlfw info <file>\n");
263 printf("\n");
264 printf("Create new Luxul firmware:\n");
265 printf("\tlxlfw create <file> [options]\n");
266 printf("\t-i file\t\t\t\tinput file for Luxul's firmware container\n");
267 printf("\t-l\t\t\t\tmark firmware as created by Luxul company (DON'T USE)\n");
268 printf("\t-b board\t\t\tboard (device) name\n");
269 printf("\t-r release\t\t\trelease number (e.g. 5.1.0, 7.1.0.2)\n");
270 }
271
272 int main(int argc, char **argv) {
273 if (argc > 1) {
274 if (!strcmp(argv[1], "info"))
275 return lxlfw_info(argc, argv);
276 else if (!strcmp(argv[1], "create"))
277 return lxlfw_create(argc, argv);
278 }
279
280 usage();
281 return 0;
282 }