/src/fwupd/plugins/synaptics-prometheus/fu-synaprom-firmware.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2019 Richard Hughes <richard@hughsie.com> |
3 | | * Copyright 2019 Synaptics Inc |
4 | | * |
5 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
6 | | */ |
7 | | |
8 | | #include "config.h" |
9 | | |
10 | | #include <string.h> |
11 | | |
12 | | #include "fu-synaprom-firmware.h" |
13 | | #include "fu-synaprom-struct.h" |
14 | | |
15 | | struct _FuSynapromFirmware { |
16 | | FuFirmware parent_instance; |
17 | | guint32 product_id; |
18 | | guint32 signature_size; |
19 | | }; |
20 | | |
21 | | G_DEFINE_TYPE(FuSynapromFirmware, fu_synaprom_firmware, FU_TYPE_FIRMWARE) |
22 | | |
23 | | /* use only first 12 bit of 16 bits as tag value */ |
24 | 4.80k | #define FU_SYNAPROM_FIRMWARE_TAG_MAX 0xfff0 |
25 | | |
26 | 394 | #define FU_SYNAPROM_FIRMWARE_COUNT_MAX 64 |
27 | | |
28 | | guint32 |
29 | | fu_synaprom_firmware_get_product_id(FuSynapromFirmware *self) |
30 | 0 | { |
31 | 0 | g_return_val_if_fail(FU_IS_SYNAPROM_FIRMWARE(self), 0x0); |
32 | 0 | return self->product_id; |
33 | 0 | } |
34 | | |
35 | | gboolean |
36 | | fu_synaprom_firmware_set_signature_size(FuSynapromFirmware *self, guint32 signature_size) |
37 | 0 | { |
38 | 0 | g_return_val_if_fail(FU_IS_SYNAPROM_FIRMWARE(self), FALSE); |
39 | 0 | self->signature_size = signature_size; |
40 | 0 | return TRUE; |
41 | 0 | } |
42 | | |
43 | | static void |
44 | | fu_synaprom_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) |
45 | 0 | { |
46 | 0 | FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); |
47 | 0 | fu_xmlb_builder_insert_kx(bn, "product_id", self->product_id); |
48 | 0 | } |
49 | | |
50 | | static gboolean |
51 | | fu_synaprom_firmware_parse(FuFirmware *firmware, |
52 | | GInputStream *stream, |
53 | | FuFirmwareParseFlags flags, |
54 | | GError **error) |
55 | 394 | { |
56 | 394 | FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); |
57 | 394 | gsize offset = 0; |
58 | 394 | gsize streamsz = 0; |
59 | | |
60 | 394 | if (!fu_input_stream_size(stream, &streamsz, error)) |
61 | 0 | return FALSE; |
62 | 394 | if (streamsz < self->signature_size + FU_STRUCT_SYNAPROM_HDR_SIZE) { |
63 | 32 | g_set_error_literal(error, |
64 | 32 | FWUPD_ERROR, |
65 | 32 | FWUPD_ERROR_INVALID_DATA, |
66 | 32 | "blob is too small to be firmware"); |
67 | 32 | return FALSE; |
68 | 32 | } |
69 | 362 | streamsz -= self->signature_size; |
70 | | |
71 | | /* parse each chunk */ |
72 | 4.95k | while (offset < streamsz) { |
73 | 4.80k | guint32 hdrsz; |
74 | 4.80k | guint32 tag; |
75 | 4.80k | g_autoptr(FuFirmware) img = fu_firmware_new(); |
76 | 4.80k | g_autoptr(FuFirmware) img_old = NULL; |
77 | 4.80k | g_autoptr(GByteArray) st_hdr = NULL; |
78 | 4.80k | g_autoptr(GInputStream) partial_stream = NULL; |
79 | | |
80 | | /* verify item header */ |
81 | 4.80k | st_hdr = fu_struct_synaprom_hdr_parse_stream(stream, offset, error); |
82 | 4.80k | if (st_hdr == NULL) |
83 | 0 | return FALSE; |
84 | 4.80k | tag = fu_struct_synaprom_hdr_get_tag(st_hdr); |
85 | 4.80k | if (tag >= FU_SYNAPROM_FIRMWARE_TAG_MAX) { |
86 | 6 | g_set_error(error, |
87 | 6 | FWUPD_ERROR, |
88 | 6 | FWUPD_ERROR_INVALID_DATA, |
89 | 6 | "tag 0x%04x is too large", |
90 | 6 | tag); |
91 | 6 | return FALSE; |
92 | 6 | } |
93 | | |
94 | | /* sanity check */ |
95 | 4.79k | img_old = fu_firmware_get_image_by_idx(firmware, tag, NULL); |
96 | 4.79k | if (img_old != NULL) { |
97 | 15 | g_set_error(error, |
98 | 15 | FWUPD_ERROR, |
99 | 15 | FWUPD_ERROR_INVALID_DATA, |
100 | 15 | "tag 0x%04x already present in image", |
101 | 15 | tag); |
102 | 15 | return FALSE; |
103 | 15 | } |
104 | 4.78k | hdrsz = fu_struct_synaprom_hdr_get_bufsz(st_hdr); |
105 | 4.78k | if (hdrsz == 0) { |
106 | 23 | g_set_error(error, |
107 | 23 | FWUPD_ERROR, |
108 | 23 | FWUPD_ERROR_INVALID_DATA, |
109 | 23 | "empty header for tag 0x%04x", |
110 | 23 | tag); |
111 | 23 | return FALSE; |
112 | 23 | } |
113 | 4.76k | offset += st_hdr->len; |
114 | 4.76k | partial_stream = fu_partial_input_stream_new(stream, offset, hdrsz, error); |
115 | 4.76k | if (partial_stream == NULL) |
116 | 169 | return FALSE; |
117 | 4.59k | if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) |
118 | 0 | return FALSE; |
119 | 4.59k | g_debug("adding 0x%04x (%s) with size 0x%04x", |
120 | 4.59k | tag, |
121 | 4.59k | fu_synaprom_firmware_tag_to_string(tag), |
122 | 4.59k | hdrsz); |
123 | 4.59k | fu_firmware_set_idx(img, tag); |
124 | 4.59k | fu_firmware_set_id(img, fu_synaprom_firmware_tag_to_string(tag)); |
125 | 4.59k | if (!fu_firmware_add_image_full(firmware, img, error)) |
126 | 1 | return FALSE; |
127 | | |
128 | | /* metadata */ |
129 | 4.59k | if (tag == FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_HEADER) { |
130 | 143 | g_autofree gchar *version = NULL; |
131 | 143 | g_autoptr(GByteArray) st_mfw = NULL; |
132 | 143 | st_mfw = fu_struct_synaprom_mfw_hdr_parse_stream(stream, offset, error); |
133 | 143 | if (st_mfw == NULL) |
134 | 0 | return FALSE; |
135 | 143 | self->product_id = fu_struct_synaprom_mfw_hdr_get_product(st_mfw); |
136 | 143 | version = g_strdup_printf("%u.%u", |
137 | 143 | fu_struct_synaprom_mfw_hdr_get_vmajor(st_mfw), |
138 | 143 | fu_struct_synaprom_mfw_hdr_get_vminor(st_mfw)); |
139 | 143 | fu_firmware_set_version(firmware, version); |
140 | 143 | } |
141 | | |
142 | | /* next item */ |
143 | 4.59k | offset += hdrsz; |
144 | 4.59k | } |
145 | 148 | return TRUE; |
146 | 362 | } |
147 | | |
148 | | static GByteArray * |
149 | | fu_synaprom_firmware_write(FuFirmware *firmware, GError **error) |
150 | 148 | { |
151 | 148 | FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); |
152 | 148 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
153 | 148 | g_autoptr(GByteArray) st_hdr = fu_struct_synaprom_hdr_new(); |
154 | 148 | g_autoptr(GByteArray) st_mfw = fu_struct_synaprom_mfw_hdr_new(); |
155 | 148 | g_autoptr(GBytes) payload = NULL; |
156 | | |
157 | | /* add header */ |
158 | 148 | fu_struct_synaprom_hdr_set_tag(st_hdr, FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_HEADER); |
159 | 148 | fu_struct_synaprom_hdr_set_bufsz(st_hdr, st_mfw->len); |
160 | 148 | g_byte_array_append(buf, st_hdr->data, st_hdr->len); |
161 | 148 | fu_struct_synaprom_mfw_hdr_set_product(st_mfw, self->product_id); |
162 | 148 | g_byte_array_append(buf, st_mfw->data, st_mfw->len); |
163 | | |
164 | | /* add payload */ |
165 | 148 | payload = fu_firmware_get_bytes_with_patches(firmware, error); |
166 | 148 | if (payload == NULL) |
167 | 148 | return NULL; |
168 | 0 | fu_struct_synaprom_hdr_set_tag(st_hdr, FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_PAYLOAD); |
169 | 0 | fu_struct_synaprom_hdr_set_bufsz(st_hdr, g_bytes_get_size(payload)); |
170 | 0 | g_byte_array_append(buf, st_hdr->data, st_hdr->len); |
171 | 0 | fu_byte_array_append_bytes(buf, payload); |
172 | | |
173 | | /* add signature */ |
174 | 0 | for (guint i = 0; i < self->signature_size; i++) |
175 | 0 | fu_byte_array_append_uint8(buf, 0xff); |
176 | 0 | return g_steal_pointer(&buf); |
177 | 148 | } |
178 | | |
179 | | static gboolean |
180 | | fu_synaprom_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) |
181 | 0 | { |
182 | 0 | FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); |
183 | 0 | guint64 tmp; |
184 | | |
185 | | /* simple properties */ |
186 | 0 | tmp = xb_node_query_text_as_uint(n, "product_id", NULL); |
187 | 0 | if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) |
188 | 0 | self->product_id = tmp; |
189 | | |
190 | | /* success */ |
191 | 0 | return TRUE; |
192 | 0 | } |
193 | | |
194 | | static void |
195 | | fu_synaprom_firmware_init(FuSynapromFirmware *self) |
196 | 394 | { |
197 | 394 | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); |
198 | 394 | fu_firmware_set_images_max(FU_FIRMWARE(self), FU_SYNAPROM_FIRMWARE_COUNT_MAX); |
199 | 394 | self->signature_size = FU_SYNAPROM_FIRMWARE_PROMETHEUS_SIGSIZE; |
200 | 394 | } |
201 | | |
202 | | static void |
203 | | fu_synaprom_firmware_class_init(FuSynapromFirmwareClass *klass) |
204 | 1 | { |
205 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
206 | 1 | firmware_class->parse = fu_synaprom_firmware_parse; |
207 | 1 | firmware_class->write = fu_synaprom_firmware_write; |
208 | 1 | firmware_class->export = fu_synaprom_firmware_export; |
209 | 1 | firmware_class->build = fu_synaprom_firmware_build; |
210 | 1 | } |
211 | | |
212 | | FuFirmware * |
213 | | fu_synaprom_firmware_new(void) |
214 | 0 | { |
215 | 0 | return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPROM_FIRMWARE, NULL)); |
216 | 0 | } |