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