xref: /dpdk/lib/eal/unix/eal_firmware.c (revision 6f80df8cb0f889203d7cd27766abcc6ebc720e33)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2021 Red Hat, Inc.
3  */
4 
5 #ifdef RTE_HAS_LIBARCHIVE
6 #include <archive.h>
7 #endif
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 
13 #include <rte_common.h>
14 #include <rte_log.h>
15 
16 #include "eal_firmware.h"
17 #include "eal_private.h"
18 
19 static const char * const compression_suffixes[] = { "xz", "zst" };
20 
21 #ifdef RTE_HAS_LIBARCHIVE
22 
23 struct firmware_read_ctx {
24 	struct archive *a;
25 };
26 
27 static int
firmware_open(struct firmware_read_ctx * ctx,const char * name,size_t blocksize)28 firmware_open(struct firmware_read_ctx *ctx, const char *name, size_t blocksize)
29 {
30 	struct archive_entry *e;
31 	int err;
32 
33 	ctx->a = archive_read_new();
34 	if (ctx->a == NULL)
35 		return -1;
36 
37 	if (archive_read_support_format_raw(ctx->a) != ARCHIVE_OK)
38 		goto error;
39 
40 	err = archive_read_support_filter_xz(ctx->a);
41 	if (err != ARCHIVE_OK && err != ARCHIVE_WARN)
42 		EAL_LOG(DEBUG, "could not initialise libarchive for xz compression");
43 
44 	err = archive_read_support_filter_zstd(ctx->a);
45 	if (err != ARCHIVE_OK && err != ARCHIVE_WARN)
46 		EAL_LOG(DEBUG, "could not initialise libarchive for zstd compression");
47 
48 	if (archive_read_open_filename(ctx->a, name, blocksize) != ARCHIVE_OK)
49 		goto error;
50 
51 	if (archive_read_next_header(ctx->a, &e) != ARCHIVE_OK)
52 		goto error;
53 
54 	return 0;
55 
56 error:
57 	archive_read_free(ctx->a);
58 	ctx->a = NULL;
59 	return -1;
60 }
61 
62 static ssize_t
firmware_read_block(struct firmware_read_ctx * ctx,void * buf,size_t count)63 firmware_read_block(struct firmware_read_ctx *ctx, void *buf, size_t count)
64 {
65 	return archive_read_data(ctx->a, buf, count);
66 }
67 
68 static void
firmware_close(struct firmware_read_ctx * ctx)69 firmware_close(struct firmware_read_ctx *ctx)
70 {
71 	archive_read_free(ctx->a);
72 	ctx->a = NULL;
73 }
74 
75 #else /* !RTE_HAS_LIBARCHIVE */
76 
77 struct firmware_read_ctx {
78 	int fd;
79 };
80 
81 static int
firmware_open(struct firmware_read_ctx * ctx,const char * name,__rte_unused size_t blocksize)82 firmware_open(struct firmware_read_ctx *ctx, const char *name,
83 	__rte_unused size_t blocksize)
84 {
85 	ctx->fd = open(name, O_RDONLY);
86 	if (ctx->fd < 0)
87 		return -1;
88 	return 0;
89 }
90 
91 static ssize_t
firmware_read_block(struct firmware_read_ctx * ctx,void * buf,size_t count)92 firmware_read_block(struct firmware_read_ctx *ctx, void *buf, size_t count)
93 {
94 	return read(ctx->fd, buf, count);
95 }
96 
97 static void
firmware_close(struct firmware_read_ctx * ctx)98 firmware_close(struct firmware_read_ctx *ctx)
99 {
100 	close(ctx->fd);
101 	ctx->fd = -1;
102 }
103 
104 #endif /* !RTE_HAS_LIBARCHIVE */
105 
106 static int
firmware_read(const char * name,void ** buf,size_t * bufsz)107 firmware_read(const char *name, void **buf, size_t *bufsz)
108 {
109 	const size_t blocksize = 4096;
110 	struct firmware_read_ctx ctx;
111 	int ret = -1;
112 	int err;
113 
114 	*buf = NULL;
115 	*bufsz = 0;
116 
117 	if (firmware_open(&ctx, name, blocksize) < 0)
118 		return -1;
119 
120 	do {
121 		void *tmp;
122 
123 		tmp = realloc(*buf, *bufsz + blocksize);
124 		if (tmp == NULL) {
125 			free(*buf);
126 			*buf = NULL;
127 			*bufsz = 0;
128 			goto out;
129 		}
130 		*buf = tmp;
131 
132 		err = firmware_read_block(&ctx, RTE_PTR_ADD(*buf, *bufsz), blocksize);
133 		if (err < 0) {
134 			free(*buf);
135 			*buf = NULL;
136 			*bufsz = 0;
137 			goto out;
138 		}
139 		*bufsz += err;
140 
141 	} while (err != 0);
142 
143 	ret = 0;
144 out:
145 	firmware_close(&ctx);
146 	return ret;
147 }
148 
149 int
rte_firmware_read(const char * name,void ** buf,size_t * bufsz)150 rte_firmware_read(const char *name, void **buf, size_t *bufsz)
151 {
152 	char path[PATH_MAX];
153 	int ret;
154 
155 	ret = firmware_read(name, buf, bufsz);
156 	if (ret < 0) {
157 		unsigned int i;
158 
159 		for (i = 0; i < RTE_DIM(compression_suffixes); i++) {
160 			snprintf(path, sizeof(path), "%s.%s", name, compression_suffixes[i]);
161 			path[PATH_MAX - 1] = '\0';
162 			if (access(path, F_OK) != 0)
163 				continue;
164 #ifndef RTE_HAS_LIBARCHIVE
165 			EAL_LOG(WARNING, "libarchive not linked, %s cannot be decompressed",
166 				path);
167 #else
168 			ret = firmware_read(path, buf, bufsz);
169 #endif
170 			break;
171 		}
172 	}
173 	return ret;
174 }
175