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