xref: /dpdk/lib/eal/unix/eal_firmware.c (revision 6f80df8cb0f889203d7cd27766abcc6ebc720e33)
140edb9c0SDavid Marchand /* SPDX-License-Identifier: BSD-3-Clause
240edb9c0SDavid Marchand  * Copyright(c) 2021 Red Hat, Inc.
340edb9c0SDavid Marchand  */
440edb9c0SDavid Marchand 
540edb9c0SDavid Marchand #ifdef RTE_HAS_LIBARCHIVE
640edb9c0SDavid Marchand #include <archive.h>
740edb9c0SDavid Marchand #endif
840edb9c0SDavid Marchand #include <fcntl.h>
940edb9c0SDavid Marchand #include <stdio.h>
1040edb9c0SDavid Marchand #include <stdlib.h>
1140edb9c0SDavid Marchand #include <unistd.h>
1240edb9c0SDavid Marchand 
1340edb9c0SDavid Marchand #include <rte_common.h>
1440edb9c0SDavid Marchand #include <rte_log.h>
1540edb9c0SDavid Marchand 
1640edb9c0SDavid Marchand #include "eal_firmware.h"
17ae67895bSDavid Marchand #include "eal_private.h"
1840edb9c0SDavid Marchand 
19*6f80df8cSDavid Marchand static const char * const compression_suffixes[] = { "xz", "zst" };
20*6f80df8cSDavid Marchand 
2140edb9c0SDavid Marchand #ifdef RTE_HAS_LIBARCHIVE
2240edb9c0SDavid Marchand 
2340edb9c0SDavid Marchand struct firmware_read_ctx {
2440edb9c0SDavid Marchand 	struct archive *a;
2540edb9c0SDavid Marchand };
2640edb9c0SDavid Marchand 
2740edb9c0SDavid Marchand static int
firmware_open(struct firmware_read_ctx * ctx,const char * name,size_t blocksize)2840edb9c0SDavid Marchand firmware_open(struct firmware_read_ctx *ctx, const char *name, size_t blocksize)
2940edb9c0SDavid Marchand {
3040edb9c0SDavid Marchand 	struct archive_entry *e;
3139970abcSSrikanth Yalavarthi 	int err;
3240edb9c0SDavid Marchand 
3340edb9c0SDavid Marchand 	ctx->a = archive_read_new();
3440edb9c0SDavid Marchand 	if (ctx->a == NULL)
3540edb9c0SDavid Marchand 		return -1;
3639970abcSSrikanth Yalavarthi 
3739970abcSSrikanth Yalavarthi 	if (archive_read_support_format_raw(ctx->a) != ARCHIVE_OK)
3839970abcSSrikanth Yalavarthi 		goto error;
3939970abcSSrikanth Yalavarthi 
4039970abcSSrikanth Yalavarthi 	err = archive_read_support_filter_xz(ctx->a);
4139970abcSSrikanth Yalavarthi 	if (err != ARCHIVE_OK && err != ARCHIVE_WARN)
42*6f80df8cSDavid Marchand 		EAL_LOG(DEBUG, "could not initialise libarchive for xz compression");
43*6f80df8cSDavid Marchand 
44*6f80df8cSDavid Marchand 	err = archive_read_support_filter_zstd(ctx->a);
45*6f80df8cSDavid Marchand 	if (err != ARCHIVE_OK && err != ARCHIVE_WARN)
46*6f80df8cSDavid Marchand 		EAL_LOG(DEBUG, "could not initialise libarchive for zstd compression");
4739970abcSSrikanth Yalavarthi 
4839970abcSSrikanth Yalavarthi 	if (archive_read_open_filename(ctx->a, name, blocksize) != ARCHIVE_OK)
4939970abcSSrikanth Yalavarthi 		goto error;
5039970abcSSrikanth Yalavarthi 
5139970abcSSrikanth Yalavarthi 	if (archive_read_next_header(ctx->a, &e) != ARCHIVE_OK)
5239970abcSSrikanth Yalavarthi 		goto error;
5339970abcSSrikanth Yalavarthi 
5439970abcSSrikanth Yalavarthi 	return 0;
5539970abcSSrikanth Yalavarthi 
5639970abcSSrikanth Yalavarthi error:
5740edb9c0SDavid Marchand 	archive_read_free(ctx->a);
5840edb9c0SDavid Marchand 	ctx->a = NULL;
5940edb9c0SDavid Marchand 	return -1;
6040edb9c0SDavid Marchand }
6140edb9c0SDavid Marchand 
6240edb9c0SDavid Marchand static ssize_t
firmware_read_block(struct firmware_read_ctx * ctx,void * buf,size_t count)6340edb9c0SDavid Marchand firmware_read_block(struct firmware_read_ctx *ctx, void *buf, size_t count)
6440edb9c0SDavid Marchand {
6540edb9c0SDavid Marchand 	return archive_read_data(ctx->a, buf, count);
6640edb9c0SDavid Marchand }
6740edb9c0SDavid Marchand 
6840edb9c0SDavid Marchand static void
firmware_close(struct firmware_read_ctx * ctx)6940edb9c0SDavid Marchand firmware_close(struct firmware_read_ctx *ctx)
7040edb9c0SDavid Marchand {
7140edb9c0SDavid Marchand 	archive_read_free(ctx->a);
7240edb9c0SDavid Marchand 	ctx->a = NULL;
7340edb9c0SDavid Marchand }
7440edb9c0SDavid Marchand 
7540edb9c0SDavid Marchand #else /* !RTE_HAS_LIBARCHIVE */
7640edb9c0SDavid Marchand 
7740edb9c0SDavid Marchand struct firmware_read_ctx {
7840edb9c0SDavid Marchand 	int fd;
7940edb9c0SDavid Marchand };
8040edb9c0SDavid Marchand 
8140edb9c0SDavid Marchand static int
firmware_open(struct firmware_read_ctx * ctx,const char * name,__rte_unused size_t blocksize)8240edb9c0SDavid Marchand firmware_open(struct firmware_read_ctx *ctx, const char *name,
8340edb9c0SDavid Marchand 	__rte_unused size_t blocksize)
8440edb9c0SDavid Marchand {
8540edb9c0SDavid Marchand 	ctx->fd = open(name, O_RDONLY);
8640edb9c0SDavid Marchand 	if (ctx->fd < 0)
8740edb9c0SDavid Marchand 		return -1;
8840edb9c0SDavid Marchand 	return 0;
8940edb9c0SDavid Marchand }
9040edb9c0SDavid Marchand 
9140edb9c0SDavid Marchand static ssize_t
firmware_read_block(struct firmware_read_ctx * ctx,void * buf,size_t count)9240edb9c0SDavid Marchand firmware_read_block(struct firmware_read_ctx *ctx, void *buf, size_t count)
9340edb9c0SDavid Marchand {
9440edb9c0SDavid Marchand 	return read(ctx->fd, buf, count);
9540edb9c0SDavid Marchand }
9640edb9c0SDavid Marchand 
9740edb9c0SDavid Marchand static void
firmware_close(struct firmware_read_ctx * ctx)9840edb9c0SDavid Marchand firmware_close(struct firmware_read_ctx *ctx)
9940edb9c0SDavid Marchand {
10040edb9c0SDavid Marchand 	close(ctx->fd);
10140edb9c0SDavid Marchand 	ctx->fd = -1;
10240edb9c0SDavid Marchand }
10340edb9c0SDavid Marchand 
10440edb9c0SDavid Marchand #endif /* !RTE_HAS_LIBARCHIVE */
10540edb9c0SDavid Marchand 
10640edb9c0SDavid Marchand static int
firmware_read(const char * name,void ** buf,size_t * bufsz)10740edb9c0SDavid Marchand firmware_read(const char *name, void **buf, size_t *bufsz)
10840edb9c0SDavid Marchand {
10940edb9c0SDavid Marchand 	const size_t blocksize = 4096;
11040edb9c0SDavid Marchand 	struct firmware_read_ctx ctx;
11140edb9c0SDavid Marchand 	int ret = -1;
11240edb9c0SDavid Marchand 	int err;
11340edb9c0SDavid Marchand 
11440edb9c0SDavid Marchand 	*buf = NULL;
11540edb9c0SDavid Marchand 	*bufsz = 0;
11640edb9c0SDavid Marchand 
11740edb9c0SDavid Marchand 	if (firmware_open(&ctx, name, blocksize) < 0)
11840edb9c0SDavid Marchand 		return -1;
11940edb9c0SDavid Marchand 
12040edb9c0SDavid Marchand 	do {
12140edb9c0SDavid Marchand 		void *tmp;
12240edb9c0SDavid Marchand 
12340edb9c0SDavid Marchand 		tmp = realloc(*buf, *bufsz + blocksize);
12440edb9c0SDavid Marchand 		if (tmp == NULL) {
12540edb9c0SDavid Marchand 			free(*buf);
12640edb9c0SDavid Marchand 			*buf = NULL;
12740edb9c0SDavid Marchand 			*bufsz = 0;
12840edb9c0SDavid Marchand 			goto out;
12940edb9c0SDavid Marchand 		}
13040edb9c0SDavid Marchand 		*buf = tmp;
13140edb9c0SDavid Marchand 
13240edb9c0SDavid Marchand 		err = firmware_read_block(&ctx, RTE_PTR_ADD(*buf, *bufsz), blocksize);
13340edb9c0SDavid Marchand 		if (err < 0) {
13440edb9c0SDavid Marchand 			free(*buf);
13540edb9c0SDavid Marchand 			*buf = NULL;
13640edb9c0SDavid Marchand 			*bufsz = 0;
13740edb9c0SDavid Marchand 			goto out;
13840edb9c0SDavid Marchand 		}
13940edb9c0SDavid Marchand 		*bufsz += err;
14040edb9c0SDavid Marchand 
14140edb9c0SDavid Marchand 	} while (err != 0);
14240edb9c0SDavid Marchand 
14340edb9c0SDavid Marchand 	ret = 0;
14440edb9c0SDavid Marchand out:
14540edb9c0SDavid Marchand 	firmware_close(&ctx);
14640edb9c0SDavid Marchand 	return ret;
14740edb9c0SDavid Marchand }
14840edb9c0SDavid Marchand 
14940edb9c0SDavid Marchand int
rte_firmware_read(const char * name,void ** buf,size_t * bufsz)15040edb9c0SDavid Marchand rte_firmware_read(const char *name, void **buf, size_t *bufsz)
15140edb9c0SDavid Marchand {
15240edb9c0SDavid Marchand 	char path[PATH_MAX];
15340edb9c0SDavid Marchand 	int ret;
15440edb9c0SDavid Marchand 
15540edb9c0SDavid Marchand 	ret = firmware_read(name, buf, bufsz);
15640edb9c0SDavid Marchand 	if (ret < 0) {
157*6f80df8cSDavid Marchand 		unsigned int i;
158*6f80df8cSDavid Marchand 
159*6f80df8cSDavid Marchand 		for (i = 0; i < RTE_DIM(compression_suffixes); i++) {
160*6f80df8cSDavid Marchand 			snprintf(path, sizeof(path), "%s.%s", name, compression_suffixes[i]);
16140edb9c0SDavid Marchand 			path[PATH_MAX - 1] = '\0';
162*6f80df8cSDavid Marchand 			if (access(path, F_OK) != 0)
163*6f80df8cSDavid Marchand 				continue;
16440edb9c0SDavid Marchand #ifndef RTE_HAS_LIBARCHIVE
165ae67895bSDavid Marchand 			EAL_LOG(WARNING, "libarchive not linked, %s cannot be decompressed",
16640edb9c0SDavid Marchand 				path);
16740edb9c0SDavid Marchand #else
16840edb9c0SDavid Marchand 			ret = firmware_read(path, buf, bufsz);
16940edb9c0SDavid Marchand #endif
170*6f80df8cSDavid Marchand 			break;
171*6f80df8cSDavid Marchand 		}
17240edb9c0SDavid Marchand 	}
17340edb9c0SDavid Marchand 	return ret;
17440edb9c0SDavid Marchand }
175