/src/fwupd/libfwupdplugin/fu-ifwi-fpt-firmware.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2022 Richard Hughes <richard@hughsie.com> |
3 | | * Copyright 2022 Intel |
4 | | * |
5 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
6 | | */ |
7 | | |
8 | | #define G_LOG_DOMAIN "FuFirmware" |
9 | | |
10 | | #include "config.h" |
11 | | |
12 | | #include <string.h> |
13 | | |
14 | | #include "fu-byte-array.h" |
15 | | #include "fu-common.h" |
16 | | #include "fu-ifwi-fpt-firmware.h" |
17 | | #include "fu-ifwi-struct.h" |
18 | | #include "fu-input-stream.h" |
19 | | #include "fu-partial-input-stream.h" |
20 | | #include "fu-string.h" |
21 | | |
22 | | /** |
23 | | * FuIfwiFptFirmware: |
24 | | * |
25 | | * An Intel Flash Program Tool (aka FPT) header can be found in IFWI (Integrated Firmware Image) |
26 | | * firmware blobs which are used in various Intel products using an IPU (Infrastructure Processing |
27 | | * Unit). |
28 | | * |
29 | | * This could include hardware like SmartNICs, GPUs, camera and audio devices. |
30 | | * |
31 | | * See also: [class@FuFirmware] |
32 | | */ |
33 | | |
34 | 450 | G_DEFINE_TYPE(FuIfwiFptFirmware, fu_ifwi_fpt_firmware, FU_TYPE_FIRMWARE) |
35 | 450 | |
36 | 450 | #define FU_IFWI_FPT_MAX_ENTRIES 56 |
37 | | |
38 | | static gboolean |
39 | | fu_ifwi_fpt_firmware_validate(FuFirmware *firmware, |
40 | | FuInputStream *stream, |
41 | | gsize offset, |
42 | | GError **error) |
43 | 448 | { |
44 | 448 | return fu_struct_ifwi_fpt_validate_stream(stream, offset, error); |
45 | 448 | } |
46 | | |
47 | | static gboolean |
48 | | fu_ifwi_fpt_firmware_parse(FuFirmware *firmware, |
49 | | FuInputStream *stream, |
50 | | FuFirmwareParseFlags flags, |
51 | | GError **error) |
52 | 367 | { |
53 | 367 | guint32 num_of_entries; |
54 | 367 | gsize offset = 0; |
55 | 367 | g_autoptr(FuStructIfwiFpt) st_hdr = NULL; |
56 | | |
57 | | /* sanity check */ |
58 | 367 | st_hdr = fu_struct_ifwi_fpt_parse_stream(stream, offset, error); |
59 | 367 | if (st_hdr == NULL) |
60 | 0 | return FALSE; |
61 | 367 | num_of_entries = fu_struct_ifwi_fpt_get_num_of_entries(st_hdr); |
62 | 367 | if (num_of_entries > FU_IFWI_FPT_MAX_ENTRIES) { |
63 | 32 | g_set_error(error, |
64 | 32 | FWUPD_ERROR, |
65 | 32 | FWUPD_ERROR_INVALID_DATA, |
66 | 32 | "invalid FPT number of entries %u", |
67 | 32 | num_of_entries); |
68 | 32 | return FALSE; |
69 | 32 | } |
70 | 335 | if (fu_struct_ifwi_fpt_get_header_version(st_hdr) < |
71 | 335 | FU_STRUCT_IFWI_FPT_DEFAULT_HEADER_VERSION) { |
72 | 6 | g_set_error(error, |
73 | 6 | FWUPD_ERROR, |
74 | 6 | FWUPD_ERROR_INVALID_DATA, |
75 | 6 | "invalid FPT header version: 0x%x", |
76 | 6 | fu_struct_ifwi_fpt_get_header_version(st_hdr)); |
77 | 6 | return FALSE; |
78 | 6 | } |
79 | | |
80 | | /* offset by header length */ |
81 | 329 | if (!fu_size_checked_inc(&offset, fu_struct_ifwi_fpt_get_header_length(st_hdr), error)) |
82 | 0 | return FALSE; |
83 | | |
84 | | /* read out entries */ |
85 | 4.30k | for (guint i = 0; i < num_of_entries; i++) { |
86 | 4.24k | guint32 data_length; |
87 | 4.24k | guint32 partition_name; |
88 | 4.24k | g_autofree gchar *id = NULL; |
89 | 4.24k | g_autoptr(FuFirmware) img = fu_firmware_new(); |
90 | 4.24k | g_autoptr(FuStructIfwiFptEntry) st_ent = NULL; |
91 | | |
92 | | /* read IDX */ |
93 | 4.24k | st_ent = fu_struct_ifwi_fpt_entry_parse_stream(stream, offset, error); |
94 | 4.24k | if (st_ent == NULL) |
95 | 132 | return FALSE; |
96 | 4.11k | partition_name = fu_struct_ifwi_fpt_entry_get_partition_name(st_ent); |
97 | 4.11k | fu_firmware_set_idx(img, partition_name); |
98 | | |
99 | | /* convert to text form for convenience */ |
100 | 4.11k | id = fu_strsafe((const gchar *)&partition_name, sizeof(partition_name)); |
101 | 4.11k | if (id != NULL) |
102 | 1.75k | fu_firmware_set_id(img, id); |
103 | | |
104 | | /* get data at offset using zero-copy */ |
105 | 4.11k | data_length = fu_struct_ifwi_fpt_entry_get_length(st_ent); |
106 | 4.11k | if (data_length != 0x0) { |
107 | 2.46k | guint32 data_offset = fu_struct_ifwi_fpt_entry_get_offset(st_ent); |
108 | 2.46k | g_autoptr(FuInputStream) partial_stream = NULL; |
109 | 2.46k | partial_stream = |
110 | 2.46k | fu_partial_input_stream_new(stream, data_offset, data_length, error); |
111 | 2.46k | if (partial_stream == NULL) { |
112 | 141 | g_prefix_error_literal(error, "failed to cut FPT image: "); |
113 | 141 | return FALSE; |
114 | 141 | } |
115 | 2.32k | if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) |
116 | 0 | return FALSE; |
117 | 2.32k | fu_firmware_set_offset(img, data_offset); |
118 | 2.32k | } |
119 | 3.97k | if (!fu_firmware_add_image(firmware, img, error)) |
120 | 0 | return FALSE; |
121 | | |
122 | | /* next */ |
123 | 3.97k | if (!fu_size_checked_inc(&offset, st_ent->buf->len, error)) |
124 | 0 | return FALSE; |
125 | 3.97k | } |
126 | | |
127 | | /* success */ |
128 | 56 | return TRUE; |
129 | 329 | } |
130 | | |
131 | | static GByteArray * |
132 | | fu_ifwi_fpt_firmware_write(FuFirmware *firmware, GError **error) |
133 | 56 | { |
134 | 56 | gsize offset = 0; |
135 | 56 | g_autoptr(FuStructIfwiFpt) st = fu_struct_ifwi_fpt_new(); |
136 | 56 | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
137 | | |
138 | | /* fixup the image offsets */ |
139 | 56 | if (!fu_size_checked_inc(&offset, st->buf->len, error)) |
140 | 0 | return NULL; |
141 | 56 | if (!fu_size_checked_inc_product(&offset, FU_STRUCT_IFWI_FPT_ENTRY_SIZE, imgs->len, error)) |
142 | 0 | return NULL; |
143 | 56 | for (guint i = 0; i < imgs->len; i++) { |
144 | 54 | FuFirmware *img = g_ptr_array_index(imgs, i); |
145 | 54 | g_autoptr(GBytes) blob = NULL; |
146 | 54 | blob = fu_firmware_get_bytes(img, error); |
147 | 54 | if (blob == NULL) { |
148 | 54 | g_prefix_error(error, "image 0x%x: ", i); |
149 | 54 | return NULL; |
150 | 54 | } |
151 | 0 | fu_firmware_set_offset(img, offset); |
152 | 0 | if (!fu_size_checked_inc(&offset, g_bytes_get_size(blob), error)) { |
153 | 0 | g_prefix_error(error, "offset overflow for partition %u: ", i); |
154 | 0 | return NULL; |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | | /* write the header */ |
159 | 2 | fu_struct_ifwi_fpt_set_num_of_entries(st, imgs->len); |
160 | | |
161 | | /* add entries */ |
162 | 2 | for (guint i = 0; i < imgs->len; i++) { |
163 | 0 | FuFirmware *img = g_ptr_array_index(imgs, i); |
164 | 0 | g_autoptr(FuStructIfwiFptEntry) st_ent = fu_struct_ifwi_fpt_entry_new(); |
165 | 0 | fu_struct_ifwi_fpt_entry_set_partition_name(st_ent, fu_firmware_get_idx(img)); |
166 | 0 | fu_struct_ifwi_fpt_entry_set_offset(st_ent, fu_firmware_get_offset(img)); |
167 | 0 | fu_struct_ifwi_fpt_entry_set_length(st_ent, fu_firmware_get_size(img)); |
168 | 0 | fu_byte_array_append_array(st->buf, st_ent->buf); |
169 | 0 | } |
170 | | |
171 | | /* add entry data */ |
172 | 2 | for (guint i = 0; i < imgs->len; i++) { |
173 | 0 | FuFirmware *img = g_ptr_array_index(imgs, i); |
174 | 0 | g_autoptr(GBytes) blob = NULL; |
175 | 0 | blob = fu_firmware_get_bytes(img, error); |
176 | 0 | if (blob == NULL) |
177 | 0 | return NULL; |
178 | 0 | fu_byte_array_append_bytes(st->buf, blob); |
179 | 0 | } |
180 | | |
181 | | /* success */ |
182 | 2 | return g_steal_pointer(&st->buf); |
183 | 2 | } |
184 | | |
185 | | static void |
186 | | fu_ifwi_fpt_firmware_init(FuIfwiFptFirmware *self) |
187 | 448 | { |
188 | 448 | } |
189 | | |
190 | | static void |
191 | | fu_ifwi_fpt_firmware_class_init(FuIfwiFptFirmwareClass *klass) |
192 | 1 | { |
193 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
194 | 1 | fu_firmware_add_image_gtype(firmware_class, FU_TYPE_FIRMWARE); |
195 | 1 | fu_firmware_set_size_max(firmware_class, 128 * FU_MB); |
196 | 1 | firmware_class->validate = fu_ifwi_fpt_firmware_validate; |
197 | 1 | firmware_class->parse = fu_ifwi_fpt_firmware_parse; |
198 | 1 | firmware_class->write = fu_ifwi_fpt_firmware_write; |
199 | 1 | fu_firmware_set_images_max(firmware_class, FU_IFWI_FPT_MAX_ENTRIES); |
200 | 1 | } |
201 | | |
202 | | /** |
203 | | * fu_ifwi_fpt_firmware_new: |
204 | | * |
205 | | * Creates a new #FuFirmware of Intel Flash Program Tool format |
206 | | * |
207 | | * Since: 1.8.2 |
208 | | **/ |
209 | | FuFirmware * |
210 | | fu_ifwi_fpt_firmware_new(void) |
211 | 0 | { |
212 | 0 | return FU_FIRMWARE(g_object_new(FU_TYPE_IFWI_FPT_FIRMWARE, NULL)); |
213 | 0 | } |