/src/fwupd/libfwupdplugin/fu-cfu-payload.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 | | #define G_LOG_DOMAIN "FuFirmware" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-byte-array.h" |
12 | | #include "fu-cfu-firmware-struct.h" |
13 | | #include "fu-cfu-payload.h" |
14 | | #include "fu-common.h" |
15 | | #include "fu-input-stream.h" |
16 | | |
17 | | /** |
18 | | * FuCfuPayload: |
19 | | * |
20 | | * A CFU payload. This contains of a variable number of blocks, each containing the address, size |
21 | | * and the chunk data. The chunks do not have to be the same size, and the address ranges do not |
22 | | * have to be continuous. |
23 | | * |
24 | | * Documented: https://docs.microsoft.com/en-us/windows-hardware/drivers/cfu/cfu-specification |
25 | | * |
26 | | * See also: [class@FuFirmware] |
27 | | */ |
28 | | |
29 | 520 | G_DEFINE_TYPE(FuCfuPayload, fu_cfu_payload, FU_TYPE_FIRMWARE) |
30 | 520 | |
31 | 520 | static gboolean |
32 | 520 | fu_cfu_payload_parse(FuFirmware *firmware, |
33 | 520 | FuInputStream *stream, |
34 | 520 | FuFirmwareParseFlags flags, |
35 | 520 | GError **error) |
36 | 520 | { |
37 | 494 | gsize offset = 0; |
38 | 494 | gsize streamsz = 0; |
39 | | |
40 | | /* process into chunks */ |
41 | 494 | if (!fu_input_stream_size(stream, &streamsz, error)) |
42 | 0 | return FALSE; |
43 | 948k | while (offset < streamsz) { |
44 | 948k | guint8 chunk_size = 0; |
45 | 948k | g_autoptr(FuChunk) chk = NULL; |
46 | 948k | g_autoptr(GBytes) blob = NULL; |
47 | 948k | g_autoptr(FuStructCfuPayload) st = NULL; |
48 | | |
49 | 948k | st = fu_struct_cfu_payload_parse_stream(stream, offset, error); |
50 | 948k | if (st == NULL) |
51 | 37 | return FALSE; |
52 | | |
53 | | /* add payload structure size */ |
54 | 948k | if (!fu_size_checked_inc(&offset, st->buf->len, error)) { |
55 | 0 | g_prefix_error_literal(error, "payload offset overflow: "); |
56 | 0 | return FALSE; |
57 | 0 | } |
58 | | |
59 | 948k | chunk_size = fu_struct_cfu_payload_get_size(st); |
60 | 948k | if (chunk_size == 0) { |
61 | 20 | g_set_error_literal(error, |
62 | 20 | FWUPD_ERROR, |
63 | 20 | FWUPD_ERROR_INVALID_DATA, |
64 | 20 | "payload size was invalid"); |
65 | 20 | return FALSE; |
66 | 20 | } |
67 | 948k | blob = fu_input_stream_read_bytes(stream, offset, chunk_size, NULL, error); |
68 | 948k | if (blob == NULL) |
69 | 16 | return FALSE; |
70 | 948k | chk = fu_chunk_bytes_new(blob); |
71 | 948k | fu_chunk_set_address(chk, fu_struct_cfu_payload_get_addr(st)); |
72 | 948k | fu_firmware_add_chunk(firmware, chk); |
73 | | |
74 | | /* next! */ |
75 | 948k | if (!fu_size_checked_inc(&offset, chunk_size, error)) |
76 | 0 | return FALSE; |
77 | 948k | } |
78 | | |
79 | | /* success */ |
80 | 421 | return TRUE; |
81 | 494 | } |
82 | | |
83 | | static GByteArray * |
84 | | fu_cfu_payload_write(FuFirmware *firmware, GError **error) |
85 | 362 | { |
86 | 362 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
87 | 362 | g_autoptr(GPtrArray) chunks = NULL; |
88 | | |
89 | 362 | chunks = fu_firmware_get_chunks(firmware, error); |
90 | 362 | if (chunks == NULL) |
91 | 0 | return NULL; |
92 | 1.20M | for (guint i = 0; i < chunks->len; i++) { |
93 | 1.20M | FuChunk *chk = g_ptr_array_index(chunks, i); |
94 | 1.20M | g_autoptr(FuStructCfuPayload) st = fu_struct_cfu_payload_new(); |
95 | 1.20M | fu_struct_cfu_payload_set_addr(st, fu_chunk_get_address(chk)); |
96 | 1.20M | fu_struct_cfu_payload_set_size(st, fu_chunk_get_data_sz(chk)); |
97 | 1.20M | fu_byte_array_append_array(buf, st->buf); |
98 | 1.20M | g_byte_array_append(buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); |
99 | 1.20M | } |
100 | 362 | return g_steal_pointer(&buf); |
101 | 362 | } |
102 | | |
103 | | static void |
104 | | fu_cfu_payload_init(FuCfuPayload *self) |
105 | 519 | { |
106 | 519 | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); |
107 | 519 | } |
108 | | |
109 | | static void |
110 | | fu_cfu_payload_class_init(FuCfuPayloadClass *klass) |
111 | 1 | { |
112 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
113 | 1 | firmware_class->parse = fu_cfu_payload_parse; |
114 | 1 | firmware_class->write = fu_cfu_payload_write; |
115 | 1 | fu_firmware_set_size_max(firmware_class, 128 * FU_MB); |
116 | 1 | } |
117 | | |
118 | | /** |
119 | | * fu_cfu_payload_new: |
120 | | * |
121 | | * Creates a new #FuFirmware for a CFU payload |
122 | | * |
123 | | * Since: 1.7.0 |
124 | | **/ |
125 | | FuFirmware * |
126 | | fu_cfu_payload_new(void) |
127 | 519 | { |
128 | 519 | return FU_FIRMWARE(g_object_new(FU_TYPE_CFU_PAYLOAD, NULL)); |
129 | 519 | } |