/src/fwupd/libfwupdplugin/fu-efi-volume.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2021 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 23.6k | #define G_LOG_DOMAIN "FuEfiVolume" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-byte-array.h" |
12 | | #include "fu-bytes.h" |
13 | | #include "fu-chunk-array.h" |
14 | | #include "fu-common.h" |
15 | | #include "fu-efi-common.h" |
16 | | #include "fu-efi-filesystem.h" |
17 | | #include "fu-efi-ftw-store.h" |
18 | | #include "fu-efi-struct.h" |
19 | | #include "fu-efi-volume.h" |
20 | | #include "fu-efi-vss2-variable-store.h" |
21 | | #include "fu-input-stream.h" |
22 | | #include "fu-partial-input-stream.h" |
23 | | #include "fu-sum.h" |
24 | | |
25 | | /** |
26 | | * FuEfiVolume: |
27 | | * |
28 | | * A UEFI file volume. |
29 | | * |
30 | | * See also: [class@FuFirmware] |
31 | | */ |
32 | | |
33 | | typedef struct { |
34 | | guint16 attrs; |
35 | | } FuEfiVolumePrivate; |
36 | | |
37 | 99.9k | G_DEFINE_TYPE_WITH_PRIVATE(FuEfiVolume, fu_efi_volume, FU_TYPE_FIRMWARE) |
38 | 99.9k | #define GET_PRIVATE(o) (fu_efi_volume_get_instance_private(o)) |
39 | | |
40 | | static void |
41 | | fu_efi_volume_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) |
42 | 0 | { |
43 | 0 | FuEfiVolume *self = FU_EFI_VOLUME(firmware); |
44 | 0 | FuEfiVolumePrivate *priv = GET_PRIVATE(self); |
45 | 0 | fu_xmlb_builder_insert_kx(bn, "attrs", priv->attrs); |
46 | 0 | if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { |
47 | 0 | fu_xmlb_builder_insert_kv(bn, |
48 | 0 | "name", |
49 | 0 | fu_efi_guid_to_name(fu_firmware_get_id(firmware))); |
50 | 0 | } |
51 | 0 | } |
52 | | |
53 | | static gboolean |
54 | | fu_efi_volume_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error) |
55 | 34.5k | { |
56 | 34.5k | return fu_struct_efi_volume_validate_stream(stream, offset, error); |
57 | 34.5k | } |
58 | | |
59 | | static gboolean |
60 | | fu_efi_volume_parse_nvram_evsa(FuEfiVolume *self, |
61 | | GInputStream *stream, |
62 | | gsize offset, |
63 | | FuFirmwareParseFlags flags, |
64 | | GError **error) |
65 | 6.20k | { |
66 | 6.20k | gsize streamsz = 0; |
67 | 6.20k | guint found_cnt = 0; |
68 | 6.20k | gsize offset_last = offset; |
69 | | |
70 | 6.20k | if (!fu_input_stream_size(stream, &streamsz, error)) |
71 | 0 | return FALSE; |
72 | 137k | while (offset < streamsz) { |
73 | 131k | g_autoptr(FuFirmware) img = NULL; |
74 | 131k | g_autoptr(GError) error_local = NULL; |
75 | | |
76 | | /* try to find a NVRAM store */ |
77 | 131k | img = fu_firmware_new_from_gtypes(stream, |
78 | 131k | offset, |
79 | 131k | flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, |
80 | 131k | &error_local, |
81 | 131k | FU_TYPE_EFI_VSS2_VARIABLE_STORE, |
82 | 131k | FU_TYPE_EFI_FTW_STORE, |
83 | 131k | G_TYPE_INVALID); |
84 | 131k | if (img == NULL) { |
85 | 130k | if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) { |
86 | 6.07k | g_debug("ignoring EFI NVRAM @0x%x: %s", |
87 | 6.07k | (guint)offset, |
88 | 6.07k | error_local->message); |
89 | 6.07k | } |
90 | 130k | if (!fu_size_checked_inc(&offset, 4 * FU_KB, error)) { |
91 | 0 | g_prefix_error_literal(error, "NVRAM scan offset overflow: "); |
92 | 0 | return FALSE; |
93 | 0 | } |
94 | 130k | continue; |
95 | 130k | } |
96 | | |
97 | | /* sanity check */ |
98 | 1.37k | if (fu_firmware_get_size(img) == 0) { |
99 | 14 | g_set_error_literal(error, |
100 | 14 | FWUPD_ERROR, |
101 | 14 | FWUPD_ERROR_INTERNAL, |
102 | 14 | "NVRAM store entry has zero size"); |
103 | 14 | return FALSE; |
104 | 14 | } |
105 | | |
106 | | /* preserve the exact padding between EVSA stores */ |
107 | 1.35k | if (offset != offset_last) { |
108 | 527 | g_autoptr(GBytes) blob = g_bytes_new(NULL, 0); |
109 | 527 | g_autoptr(GBytes) blob_padded = |
110 | 527 | fu_bytes_pad(blob, offset - offset_last, 0xFF); |
111 | 527 | g_autoptr(FuFirmware) img_padded = fu_firmware_new_from_bytes(blob_padded); |
112 | 527 | if (!fu_firmware_add_image(FU_FIRMWARE(self), img_padded, error)) |
113 | 0 | return FALSE; |
114 | 527 | } |
115 | | |
116 | | /* we found something */ |
117 | 1.35k | fu_firmware_set_offset(img, offset); |
118 | 1.35k | if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) |
119 | 0 | return FALSE; |
120 | 1.35k | if (!fu_size_checked_inc(&offset, fu_firmware_get_size(img), error)) |
121 | 0 | return FALSE; |
122 | 1.35k | offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4K); |
123 | 1.35k | found_cnt += 1; |
124 | | |
125 | | /* the last thing we found */ |
126 | 1.35k | offset_last = offset; |
127 | 1.35k | } |
128 | | |
129 | | /* we found nothing */ |
130 | 6.19k | if (found_cnt == 0) { |
131 | 4.84k | g_set_error_literal(error, |
132 | 4.84k | FWUPD_ERROR, |
133 | 4.84k | FWUPD_ERROR_INTERNAL, |
134 | 4.84k | "no NVRAM stores found"); |
135 | 4.84k | return FALSE; |
136 | 4.84k | } |
137 | | |
138 | | /* success */ |
139 | 1.34k | return TRUE; |
140 | 6.19k | } |
141 | | |
142 | | static gboolean |
143 | | fu_efi_volume_parse(FuFirmware *firmware, |
144 | | GInputStream *stream, |
145 | | FuFirmwareParseFlags flags, |
146 | | GError **error) |
147 | 12.6k | { |
148 | 12.6k | FuEfiVolume *self = FU_EFI_VOLUME(firmware); |
149 | 12.6k | FuEfiVolumePrivate *priv = GET_PRIVATE(self); |
150 | 12.6k | gsize blockmap_sz = 0; |
151 | 12.6k | gsize offset = 0; |
152 | 12.6k | gsize streamsz = 0; |
153 | 12.6k | guint16 hdr_length = 0; |
154 | 12.6k | guint32 attrs = 0; |
155 | 12.6k | guint64 fv_length = 0; |
156 | 12.6k | guint8 alignment; |
157 | 12.6k | g_autofree gchar *guid_str = NULL; |
158 | 12.6k | g_autoptr(FuStructEfiVolume) st_hdr = NULL; |
159 | 12.6k | g_autoptr(GInputStream) partial_stream = NULL; |
160 | | |
161 | | /* parse */ |
162 | 12.6k | st_hdr = fu_struct_efi_volume_parse_stream(stream, 0x0, error); |
163 | 12.6k | if (st_hdr == NULL) |
164 | 0 | return FALSE; |
165 | | |
166 | | /* guid */ |
167 | 12.6k | guid_str = fwupd_guid_to_string(fu_struct_efi_volume_get_guid(st_hdr), |
168 | 12.6k | FWUPD_GUID_FLAG_MIXED_ENDIAN); |
169 | 12.6k | g_debug("volume GUID: %s [%s]", guid_str, fu_efi_guid_to_name(guid_str)); |
170 | | |
171 | | /* length */ |
172 | 12.6k | if (!fu_input_stream_size(stream, &streamsz, error)) |
173 | 0 | return FALSE; |
174 | 12.6k | fv_length = fu_struct_efi_volume_get_length(st_hdr); |
175 | 12.6k | if (fv_length == 0x0) { |
176 | 24 | g_set_error_literal(error, |
177 | 24 | FWUPD_ERROR, |
178 | 24 | FWUPD_ERROR_INTERNAL, |
179 | 24 | "invalid volume length"); |
180 | 24 | return FALSE; |
181 | 24 | } |
182 | 12.6k | if (fv_length > fu_firmware_get_size_max(firmware)) { |
183 | 3.02k | g_set_error(error, |
184 | 3.02k | FWUPD_ERROR, |
185 | 3.02k | FWUPD_ERROR_INTERNAL, |
186 | 3.02k | "volume length larger than max size: 0x%x > 0x%x", |
187 | 3.02k | (guint)fv_length, |
188 | 3.02k | (guint)fu_firmware_get_size_max(firmware)); |
189 | 3.02k | return FALSE; |
190 | 3.02k | } |
191 | 9.63k | attrs = fu_struct_efi_volume_get_attrs(st_hdr); |
192 | 9.63k | alignment = (attrs & 0x00ff0000) >> 16; |
193 | 9.63k | if (alignment > FU_FIRMWARE_ALIGNMENT_2G) { |
194 | 56 | g_set_error(error, |
195 | 56 | FWUPD_ERROR, |
196 | 56 | FWUPD_ERROR_NOT_FOUND, |
197 | 56 | "0x%x invalid, maximum is 0x%x", |
198 | 56 | (guint)alignment, |
199 | 56 | (guint)FU_FIRMWARE_ALIGNMENT_2G); |
200 | 56 | return FALSE; |
201 | 56 | } |
202 | 9.57k | fu_firmware_set_alignment(firmware, alignment); |
203 | 9.57k | priv->attrs = attrs & 0xffff; |
204 | 9.57k | hdr_length = fu_struct_efi_volume_get_hdr_len(st_hdr); |
205 | 9.57k | if (hdr_length < st_hdr->buf->len || hdr_length > fv_length || hdr_length > streamsz || |
206 | 9.32k | hdr_length % 2 != 0) { |
207 | 264 | g_set_error(error, |
208 | 264 | FWUPD_ERROR, |
209 | 264 | FWUPD_ERROR_INTERNAL, |
210 | 264 | "invalid volume header length 0x%x", |
211 | 264 | (guint)hdr_length); |
212 | 264 | return FALSE; |
213 | 264 | } |
214 | | |
215 | | /* verify checksum */ |
216 | 9.31k | if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { |
217 | 0 | guint16 checksum_verify; |
218 | 0 | g_autoptr(GBytes) blob_hdr = NULL; |
219 | |
|
220 | 0 | blob_hdr = fu_input_stream_read_bytes(stream, 0x0, hdr_length, NULL, error); |
221 | 0 | if (blob_hdr == NULL) |
222 | 0 | return FALSE; |
223 | 0 | checksum_verify = fu_sum16w_bytes(blob_hdr, G_LITTLE_ENDIAN); |
224 | 0 | if (checksum_verify != 0) { |
225 | 0 | g_set_error(error, |
226 | 0 | FWUPD_ERROR, |
227 | 0 | FWUPD_ERROR_INVALID_FILE, |
228 | 0 | "checksum invalid, got %02x, expected %02x", |
229 | 0 | checksum_verify, |
230 | 0 | fu_struct_efi_volume_get_checksum(st_hdr)); |
231 | 0 | return FALSE; |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | /* extended header items */ |
236 | 9.31k | if (fu_struct_efi_volume_get_ext_hdr(st_hdr) != 0) { |
237 | 753 | g_autoptr(FuStructEfiVolumeExtHeader) st_ext_hdr = NULL; |
238 | 753 | gsize offset_ext = fu_struct_efi_volume_get_ext_hdr(st_hdr); |
239 | 753 | st_ext_hdr = |
240 | 753 | fu_struct_efi_volume_ext_header_parse_stream(stream, offset_ext, error); |
241 | 753 | if (st_ext_hdr == NULL) |
242 | 108 | return FALSE; |
243 | 645 | if (!fu_size_checked_inc(&offset_ext, |
244 | 645 | fu_struct_efi_volume_ext_header_get_size(st_ext_hdr), |
245 | 645 | error)) |
246 | 0 | return FALSE; |
247 | 4.65k | do { |
248 | 4.65k | g_autoptr(FuStructEfiVolumeExtEntry) st_ext_entry = NULL; |
249 | 4.65k | st_ext_entry = |
250 | 4.65k | fu_struct_efi_volume_ext_entry_parse_stream(stream, offset_ext, error); |
251 | 4.65k | if (st_ext_entry == NULL) |
252 | 185 | return FALSE; |
253 | 4.46k | if (fu_struct_efi_volume_ext_entry_get_size(st_ext_entry) == 0x0) { |
254 | 54 | g_set_error_literal(error, |
255 | 54 | FWUPD_ERROR, |
256 | 54 | FWUPD_ERROR_INVALID_DATA, |
257 | 54 | "EFI_VOLUME_EXT_ENTRY invalid size"); |
258 | 54 | return FALSE; |
259 | 54 | } |
260 | 4.41k | if (fu_struct_efi_volume_ext_entry_get_size(st_ext_entry) == 0xFFFF) |
261 | 30 | break; |
262 | 4.38k | if (!fu_size_checked_inc( |
263 | 4.38k | &offset_ext, |
264 | 4.38k | fu_struct_efi_volume_ext_entry_get_size(st_ext_entry), |
265 | 4.38k | error)) |
266 | 0 | return FALSE; |
267 | 4.38k | } while (offset_ext < fv_length); |
268 | 645 | } |
269 | | |
270 | | /* add image */ |
271 | 8.96k | partial_stream = |
272 | 8.96k | fu_partial_input_stream_new(stream, hdr_length, fv_length - hdr_length, error); |
273 | 8.96k | if (partial_stream == NULL) { |
274 | 143 | g_prefix_error_literal(error, "failed to cut EFI volume: "); |
275 | 143 | return FALSE; |
276 | 143 | } |
277 | 8.82k | fu_firmware_set_id(firmware, guid_str); |
278 | 8.82k | fu_firmware_set_size(firmware, fv_length); |
279 | | |
280 | | /* parse, which might cascade and do something like FFS2 */ |
281 | 8.82k | if (g_strcmp0(guid_str, FU_EFI_VOLUME_GUID_FFS2) == 0 || |
282 | 7.58k | g_strcmp0(guid_str, FU_EFI_VOLUME_GUID_FFS3) == 0) { |
283 | 1.24k | g_autoptr(FuFirmware) img = fu_efi_filesystem_new(); |
284 | 1.24k | fu_firmware_set_alignment(img, fu_firmware_get_alignment(firmware)); |
285 | 1.24k | if (!fu_firmware_parse_stream(img, |
286 | 1.24k | partial_stream, |
287 | 1.24k | 0x0, |
288 | 1.24k | flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, |
289 | 1.24k | error)) |
290 | 1.09k | return FALSE; |
291 | 150 | if (!fu_firmware_add_image(firmware, img, error)) |
292 | 0 | return FALSE; |
293 | 7.58k | } else if (g_strcmp0(guid_str, FU_EFI_VOLUME_GUID_NVRAM_EVSA) == 0 || |
294 | 6.20k | g_strcmp0(guid_str, FU_EFI_VOLUME_GUID_NVRAM_EVSA2) == 0) { |
295 | 6.20k | g_autoptr(GError) error_local = NULL; |
296 | 6.20k | if (!fu_efi_volume_parse_nvram_evsa(self, |
297 | 6.20k | stream, |
298 | 6.20k | hdr_length, |
299 | 6.20k | flags, |
300 | 6.20k | &error_local)) { |
301 | 4.85k | g_debug("ignoring %s [%s] EFI FV: %s", |
302 | 4.85k | guid_str, |
303 | 4.85k | fu_efi_guid_to_name(guid_str), |
304 | 4.85k | error_local->message); |
305 | 4.85k | if (!fu_firmware_set_stream(firmware, partial_stream, error)) |
306 | 0 | return FALSE; |
307 | 4.85k | } |
308 | 6.20k | } else { |
309 | | #ifndef HAVE_FUZZER |
310 | | g_warning("no idea how to parse %s [%s] EFI volume", |
311 | | guid_str, |
312 | | fu_efi_guid_to_name(guid_str)); |
313 | | #endif |
314 | 1.37k | if (!fu_firmware_set_stream(firmware, partial_stream, error)) |
315 | 0 | return FALSE; |
316 | 1.37k | } |
317 | | |
318 | | /* skip the blockmap */ |
319 | 7.73k | if (!fu_size_checked_inc(&offset, st_hdr->buf->len, error)) { |
320 | 0 | g_prefix_error_literal(error, "block map offset overflow: "); |
321 | 0 | return FALSE; |
322 | 0 | } |
323 | 1.15M | while (offset < streamsz) { |
324 | 1.14M | guint32 num_blocks; |
325 | 1.14M | guint32 length; |
326 | 1.14M | g_autoptr(FuStructEfiVolumeBlockMap) st_blk = NULL; |
327 | 1.14M | st_blk = fu_struct_efi_volume_block_map_parse_stream(stream, offset, error); |
328 | 1.14M | if (st_blk == NULL) |
329 | 2.08k | return FALSE; |
330 | 1.14M | num_blocks = fu_struct_efi_volume_block_map_get_num_blocks(st_blk); |
331 | 1.14M | length = fu_struct_efi_volume_block_map_get_length(st_blk); |
332 | 1.14M | if (!fu_size_checked_inc(&offset, st_blk->buf->len, error)) { |
333 | 0 | g_prefix_error_literal(error, "block map entry offset overflow: "); |
334 | 0 | return FALSE; |
335 | 0 | } |
336 | 1.14M | if (num_blocks == 0x0 && length == 0x0) |
337 | 4.37k | break; |
338 | 1.14M | blockmap_sz += (gsize)num_blocks * (gsize)length; |
339 | 1.14M | } |
340 | 5.64k | if (blockmap_sz < (gsize)fv_length) { |
341 | 311 | g_set_error_literal(error, |
342 | 311 | FWUPD_ERROR, |
343 | 311 | FWUPD_ERROR_INTERNAL, |
344 | 311 | "blocks allocated is less than volume length"); |
345 | 311 | return FALSE; |
346 | 311 | } |
347 | | |
348 | | /* success */ |
349 | 5.33k | return TRUE; |
350 | 5.64k | } |
351 | | |
352 | | static GByteArray * |
353 | | fu_efi_volume_write(FuFirmware *firmware, GError **error) |
354 | 3.48k | { |
355 | 3.48k | FuEfiVolume *self = FU_EFI_VOLUME(firmware); |
356 | 3.48k | FuEfiVolumePrivate *priv = GET_PRIVATE(self); |
357 | 3.48k | g_autoptr(FuStructEfiVolume) st_vol = fu_struct_efi_volume_new(); |
358 | 3.48k | g_autoptr(FuStructEfiVolumeBlockMap) st_blk = fu_struct_efi_volume_block_map_new(); |
359 | 3.48k | fwupd_guid_t guid = {0x0}; |
360 | 3.48k | guint32 hdr_length = 0x48; |
361 | 3.48k | guint64 fv_length; |
362 | 3.48k | g_autoptr(FuChunkArray) chunks = NULL; |
363 | 3.48k | g_autoptr(GBytes) img_blob = NULL; |
364 | 3.48k | g_autoptr(GPtrArray) images = NULL; |
365 | | |
366 | | /* sanity check */ |
367 | 3.48k | if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { |
368 | 12 | g_set_error(error, |
369 | 12 | FWUPD_ERROR, |
370 | 12 | FWUPD_ERROR_INVALID_FILE, |
371 | 12 | "alignment invalid, got 0x%02x", |
372 | 12 | fu_firmware_get_alignment(firmware)); |
373 | 12 | return NULL; |
374 | 12 | } |
375 | | |
376 | | /* GUID */ |
377 | 3.47k | if (fu_firmware_get_id(firmware) == NULL) { |
378 | 0 | g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no GUID set for FV"); |
379 | 0 | return NULL; |
380 | 0 | } |
381 | 3.47k | if (!fwupd_guid_from_string(fu_firmware_get_id(firmware), |
382 | 3.47k | &guid, |
383 | 3.47k | FWUPD_GUID_FLAG_MIXED_ENDIAN, |
384 | 3.47k | error)) |
385 | 0 | return NULL; |
386 | | |
387 | | /* length */ |
388 | 3.47k | images = fu_firmware_get_images(firmware); |
389 | 3.47k | if (images->len == 0) { |
390 | 2.45k | img_blob = fu_firmware_get_bytes_with_patches(firmware, error); |
391 | 2.45k | if (img_blob == NULL) { |
392 | 54 | g_prefix_error_literal(error, "no EFI FV payload: "); |
393 | 54 | return NULL; |
394 | 54 | } |
395 | 2.45k | } else { |
396 | 1.02k | g_autoptr(GByteArray) buf_tmp = g_byte_array_new(); |
397 | 2.35k | for (guint i = 0; i < images->len; i++) { |
398 | 1.48k | FuFirmware *img = g_ptr_array_index(images, i); |
399 | 1.48k | g_autoptr(GBytes) img_blob_tmp = NULL; |
400 | | |
401 | 1.48k | img_blob_tmp = fu_firmware_write(img, error); |
402 | 1.48k | if (img_blob_tmp == NULL) { |
403 | 155 | g_prefix_error_literal(error, "no EFI FV child payload: "); |
404 | 155 | return NULL; |
405 | 155 | } |
406 | 1.32k | fu_byte_array_append_bytes(buf_tmp, img_blob_tmp); |
407 | 1.32k | } |
408 | 871 | img_blob = |
409 | 871 | g_byte_array_free_to_bytes(g_steal_pointer(&buf_tmp)); /* nocheck:blocked */ |
410 | 871 | } |
411 | | |
412 | | /* pack */ |
413 | 3.26k | fu_struct_efi_volume_set_guid(st_vol, &guid); |
414 | 3.26k | fv_length = fu_common_align_up(hdr_length + g_bytes_get_size(img_blob), |
415 | 3.26k | fu_firmware_get_alignment(firmware)); |
416 | | |
417 | | /* we want a minimum size of volume */ |
418 | 3.26k | if (fu_firmware_get_size(firmware) > fv_length) { |
419 | 50 | g_debug("padding FV from 0x%x to 0x%x", |
420 | 50 | (guint)fv_length, |
421 | 50 | (guint)fu_firmware_get_size(firmware)); |
422 | 50 | fv_length = fu_firmware_get_size(firmware); |
423 | 50 | } |
424 | | |
425 | 3.26k | fu_struct_efi_volume_set_length(st_vol, fv_length); |
426 | 3.26k | fu_struct_efi_volume_set_attrs(st_vol, |
427 | 3.26k | priv->attrs | |
428 | 3.26k | ((guint32)fu_firmware_get_alignment(firmware) << 16)); |
429 | 3.26k | fu_struct_efi_volume_set_hdr_len(st_vol, hdr_length); |
430 | | |
431 | | /* blockmap */ |
432 | 3.26k | chunks = fu_chunk_array_new_virtual(fv_length, |
433 | 3.26k | FU_CHUNK_ADDR_OFFSET_NONE, |
434 | 3.26k | FU_CHUNK_PAGESZ_NONE, |
435 | 3.26k | 4 * FU_KB); |
436 | 3.26k | fu_struct_efi_volume_block_map_set_num_blocks(st_blk, fu_chunk_array_length(chunks)); |
437 | 3.26k | fu_struct_efi_volume_block_map_set_length(st_blk, 4 * FU_KB); |
438 | 3.26k | fu_byte_array_append_array(st_vol->buf, st_blk->buf); |
439 | 3.26k | fu_struct_efi_volume_block_map_set_num_blocks(st_blk, 0x0); |
440 | 3.26k | fu_struct_efi_volume_block_map_set_length(st_blk, 0x0); |
441 | 3.26k | fu_byte_array_append_array(st_vol->buf, st_blk->buf); |
442 | | |
443 | | /* fix up checksum */ |
444 | 3.26k | fu_struct_efi_volume_set_checksum( |
445 | 3.26k | st_vol, |
446 | 3.26k | 0x10000 - fu_sum16w(st_vol->buf->data, st_vol->buf->len, G_LITTLE_ENDIAN)); |
447 | | |
448 | | /* pad contents to alignment */ |
449 | 3.26k | fu_byte_array_append_bytes(st_vol->buf, img_blob); |
450 | 3.26k | fu_byte_array_set_size(st_vol->buf, fv_length, 0xFF); |
451 | | |
452 | | /* success */ |
453 | 3.26k | return g_steal_pointer(&st_vol->buf); |
454 | 3.47k | } |
455 | | |
456 | | static void |
457 | | fu_efi_volume_init(FuEfiVolume *self) |
458 | 34.5k | { |
459 | 34.5k | FuEfiVolumePrivate *priv = GET_PRIVATE(self); |
460 | 34.5k | priv->attrs = 0xfeff; |
461 | 34.5k | #ifdef HAVE_FUZZER |
462 | 34.5k | fu_firmware_set_size_max(FU_FIRMWARE(self), 1 * FU_MB); |
463 | 34.5k | fu_firmware_set_images_max(FU_FIRMWARE(self), 10); |
464 | | #else |
465 | | fu_firmware_set_size_max(FU_FIRMWARE(self), 256 * FU_MB); |
466 | | fu_firmware_set_images_max(FU_FIRMWARE(self), 1000); |
467 | | #endif |
468 | 34.5k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FIRMWARE); |
469 | 34.5k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_FILESYSTEM); |
470 | 34.5k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_VSS2_VARIABLE_STORE); |
471 | 34.5k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_FTW_STORE); |
472 | 34.5k | } |
473 | | |
474 | | static void |
475 | | fu_efi_volume_class_init(FuEfiVolumeClass *klass) |
476 | 3 | { |
477 | 3 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
478 | 3 | firmware_class->validate = fu_efi_volume_validate; |
479 | 3 | firmware_class->parse = fu_efi_volume_parse; |
480 | 3 | firmware_class->write = fu_efi_volume_write; |
481 | 3 | firmware_class->export = fu_efi_volume_export; |
482 | 3 | } |
483 | | |
484 | | /** |
485 | | * fu_efi_volume_new: |
486 | | * |
487 | | * Creates a new #FuFirmware |
488 | | * |
489 | | * Since: 2.0.0 |
490 | | **/ |
491 | | FuFirmware * |
492 | | fu_efi_volume_new(void) |
493 | 30.2k | { |
494 | 30.2k | return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_VOLUME, NULL)); |
495 | 30.2k | } |