/src/fwupd/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2012 Andrew Duggan |
3 | | * Copyright 2012 Synaptics Inc. |
4 | | * Copyright 2019 Richard Hughes <richard@hughsie.com> |
5 | | * |
6 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
7 | | */ |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include <string.h> |
12 | | |
13 | | #include "fu-synaptics-rmi-common.h" |
14 | | #include "fu-synaptics-rmi-firmware.h" |
15 | | #include "fu-synaptics-rmi-struct.h" |
16 | | |
17 | | typedef enum { |
18 | | RMI_FIRMWARE_KIND_UNKNOWN = 0x00, |
19 | | RMI_FIRMWARE_KIND_0X = 0x01, |
20 | | RMI_FIRMWARE_KIND_10 = 0x10, |
21 | | RMI_FIRMWARE_KIND_LAST, |
22 | | } RmiFirmwareKind; |
23 | | |
24 | | struct _FuSynapticsRmiFirmware { |
25 | | FuFirmware parent_instance; |
26 | | RmiFirmwareKind kind; |
27 | | guint32 checksum; |
28 | | guint8 io; |
29 | | guint8 bootloader_version; |
30 | | guint32 build_id; |
31 | | guint32 package_id; |
32 | | guint16 product_info; |
33 | | gchar *product_id; |
34 | | guint32 sig_size; |
35 | | }; |
36 | | |
37 | | G_DEFINE_TYPE(FuSynapticsRmiFirmware, fu_synaptics_rmi_firmware, FU_TYPE_FIRMWARE) |
38 | | |
39 | 246 | #define RMI_IMG_FW_OFFSET 0x100 |
40 | | |
41 | 856 | #define RMI_IMG_V10_CNTR_ADDR_OFFSET 0x0c |
42 | 2.85k | #define RMI_IMG_MAX_CONTAINERS 1024 |
43 | | |
44 | | static gboolean |
45 | | fu_synaptics_rmi_firmware_add_image(FuFirmware *firmware, |
46 | | const gchar *id, |
47 | | GInputStream *stream, |
48 | | gsize offset, |
49 | | gsize bufsz, |
50 | | FuFirmwareParseFlags flags, |
51 | | GError **error) |
52 | 19.8k | { |
53 | 19.8k | g_autoptr(FuFirmware) img = fu_firmware_new(); |
54 | 19.8k | g_autoptr(GInputStream) partial_stream = NULL; |
55 | 19.8k | partial_stream = fu_partial_input_stream_new(stream, offset, bufsz, error); |
56 | 19.8k | if (partial_stream == NULL) |
57 | 139 | return FALSE; |
58 | 19.6k | if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) |
59 | 0 | return FALSE; |
60 | 19.6k | fu_firmware_set_id(img, id); |
61 | 19.6k | return fu_firmware_add_image_full(firmware, img, error); |
62 | 19.6k | } |
63 | | |
64 | | static gboolean |
65 | | fu_synaptics_rmi_firmware_add_image_v10(FuFirmware *firmware, |
66 | | const gchar *id, |
67 | | GInputStream *stream, |
68 | | gsize offset, |
69 | | gsize bufsz, |
70 | | gsize sig_sz, |
71 | | FuFirmwareParseFlags flags, |
72 | | GError **error) |
73 | 19.6k | { |
74 | 19.6k | if (!fu_synaptics_rmi_firmware_add_image(firmware, id, stream, offset, bufsz, flags, error)) |
75 | 5 | return FALSE; |
76 | 19.6k | if (sig_sz != 0) { |
77 | 18.3k | g_autoptr(GInputStream) partial_stream = NULL; |
78 | 18.3k | g_autoptr(FuFirmware) img = fu_firmware_new(); |
79 | 18.3k | g_autofree gchar *sig_id = NULL; |
80 | | |
81 | 18.3k | partial_stream = fu_partial_input_stream_new(stream, offset + bufsz, sig_sz, error); |
82 | 18.3k | if (partial_stream == NULL) |
83 | 69 | return FALSE; |
84 | 18.2k | if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) |
85 | 0 | return FALSE; |
86 | 18.2k | sig_id = g_strdup_printf("%s-signature", id); |
87 | 18.2k | fu_firmware_set_id(img, sig_id); |
88 | 18.2k | fu_firmware_add_image(firmware, img); |
89 | 18.2k | } |
90 | 19.6k | return TRUE; |
91 | 19.6k | } |
92 | | |
93 | | static void |
94 | | fu_synaptics_rmi_firmware_export(FuFirmware *firmware, |
95 | | FuFirmwareExportFlags flags, |
96 | | XbBuilderNode *bn) |
97 | 0 | { |
98 | 0 | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); |
99 | 0 | fu_xmlb_builder_insert_kx(bn, "kind", self->kind); |
100 | 0 | fu_xmlb_builder_insert_kv(bn, "product_id", self->product_id); |
101 | 0 | if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { |
102 | 0 | fu_xmlb_builder_insert_kx(bn, "bootloader_version", self->bootloader_version); |
103 | 0 | fu_xmlb_builder_insert_kx(bn, "io", self->io); |
104 | 0 | fu_xmlb_builder_insert_kx(bn, "checksum", self->checksum); |
105 | 0 | fu_xmlb_builder_insert_kx(bn, "build_id", self->build_id); |
106 | 0 | fu_xmlb_builder_insert_kx(bn, "package_id", self->package_id); |
107 | 0 | fu_xmlb_builder_insert_kx(bn, "product_info", self->product_info); |
108 | 0 | fu_xmlb_builder_insert_kx(bn, "sig_size", self->sig_size); |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | static gboolean |
113 | | fu_synaptics_rmi_firmware_parse_v10(FuFirmware *firmware, |
114 | | GInputStream *stream, |
115 | | FuFirmwareParseFlags flags, |
116 | | GError **error) |
117 | 856 | { |
118 | 856 | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); |
119 | 856 | guint16 container_id; |
120 | 856 | guint32 cntrs_len; |
121 | 856 | guint32 offset; |
122 | 856 | guint32 cntr_addr; |
123 | 856 | guint8 product_id[RMI_PRODUCT_ID_LENGTH] = {0x0}; |
124 | 856 | gsize bufsz = 0; |
125 | 856 | const guint8 *buf; |
126 | 856 | guint32 signature_size; |
127 | 856 | g_autoptr(GByteArray) st_dsc = NULL; |
128 | 856 | g_autoptr(GBytes) fw = NULL; |
129 | | |
130 | | /* maybe stream later */ |
131 | 856 | fw = fu_input_stream_read_bytes(stream, 0, G_MAXSIZE, NULL, error); |
132 | 856 | if (fw == NULL) |
133 | 0 | return FALSE; |
134 | 856 | buf = g_bytes_get_data(fw, &bufsz); |
135 | | |
136 | 856 | if (!fu_memread_uint32_safe(buf, |
137 | 856 | bufsz, |
138 | 856 | RMI_IMG_V10_CNTR_ADDR_OFFSET, |
139 | 856 | &cntr_addr, |
140 | 856 | G_LITTLE_ENDIAN, |
141 | 856 | error)) |
142 | 0 | return FALSE; |
143 | 856 | g_debug("v10 RmiContainerDescriptor at 0x%x", cntr_addr); |
144 | 856 | st_dsc = fu_struct_rmi_container_descriptor_parse_stream(stream, cntr_addr, error); |
145 | 856 | if (st_dsc == NULL) { |
146 | 23 | g_prefix_error(error, "RmiContainerDescriptor invalid: "); |
147 | 23 | return FALSE; |
148 | 23 | } |
149 | 833 | if (bufsz < sizeof(guint32) + st_dsc->len) { |
150 | 0 | g_set_error_literal(error, |
151 | 0 | FWUPD_ERROR, |
152 | 0 | FWUPD_ERROR_INVALID_FILE, |
153 | 0 | "stream was too small"); |
154 | 0 | return FALSE; |
155 | 0 | } |
156 | 833 | container_id = fu_struct_rmi_container_descriptor_get_container_id(st_dsc); |
157 | 833 | if (container_id != FU_RMI_CONTAINER_ID_TOP_LEVEL) { |
158 | 18 | g_set_error(error, |
159 | 18 | FWUPD_ERROR, |
160 | 18 | FWUPD_ERROR_INVALID_FILE, |
161 | 18 | "toplevel container_id invalid, got 0x%x expected 0x%x", |
162 | 18 | (guint)container_id, |
163 | 18 | (guint)FU_RMI_CONTAINER_ID_TOP_LEVEL); |
164 | 18 | return FALSE; |
165 | 18 | } |
166 | 815 | offset = fu_struct_rmi_container_descriptor_get_content_address(st_dsc); |
167 | 815 | if (offset > bufsz - sizeof(guint32) - st_dsc->len) { |
168 | 40 | g_set_error(error, |
169 | 40 | FWUPD_ERROR, |
170 | 40 | FWUPD_ERROR_INVALID_FILE, |
171 | 40 | "image offset invalid, got 0x%x, size 0x%x", |
172 | 40 | (guint)offset, |
173 | 40 | (guint)bufsz); |
174 | 40 | return FALSE; |
175 | 40 | } |
176 | 775 | cntrs_len = fu_struct_rmi_container_descriptor_get_content_length(st_dsc) / 4; |
177 | 775 | if (cntrs_len > RMI_IMG_MAX_CONTAINERS) { |
178 | 31 | g_set_error(error, |
179 | 31 | FWUPD_ERROR, |
180 | 31 | FWUPD_ERROR_INVALID_FILE, |
181 | 31 | "too many containers in file [%u], maximum is %u", |
182 | 31 | cntrs_len, |
183 | 31 | (guint)RMI_IMG_MAX_CONTAINERS); |
184 | 31 | return FALSE; |
185 | 31 | } |
186 | 744 | g_debug("offset=0x%x (cntrs_len=%u)", offset, cntrs_len); |
187 | | |
188 | 31.9k | for (guint32 i = 0; i < cntrs_len; i++) { |
189 | 31.8k | guint32 content_addr; |
190 | 31.8k | guint32 addr; |
191 | 31.8k | guint32 length; |
192 | 31.8k | g_autoptr(GByteArray) st_dsc2 = NULL; |
193 | | |
194 | 31.8k | if (!fu_memread_uint32_safe(buf, bufsz, offset, &addr, G_LITTLE_ENDIAN, error)) |
195 | 172 | return FALSE; |
196 | 31.6k | g_debug("parsing RmiContainerDescriptor at 0x%x", addr); |
197 | | |
198 | 31.6k | st_dsc2 = fu_struct_rmi_container_descriptor_parse_stream(stream, addr, error); |
199 | 31.6k | if (st_dsc2 == NULL) |
200 | 250 | return FALSE; |
201 | 31.3k | container_id = fu_struct_rmi_container_descriptor_get_container_id(st_dsc2); |
202 | 31.3k | content_addr = fu_struct_rmi_container_descriptor_get_content_address(st_dsc2); |
203 | 31.3k | length = fu_struct_rmi_container_descriptor_get_content_length(st_dsc2); |
204 | 31.3k | signature_size = fu_struct_rmi_container_descriptor_get_signature_size(st_dsc2); |
205 | 31.3k | g_debug("RmiContainerDescriptor 0x%02x @ 0x%x (len 0x%x) sig_size 0x%x", |
206 | 31.3k | container_id, |
207 | 31.3k | content_addr, |
208 | 31.3k | length, |
209 | 31.3k | signature_size); |
210 | 31.3k | if (length == 0 || length > bufsz) { |
211 | 110 | g_set_error(error, |
212 | 110 | FWUPD_ERROR, |
213 | 110 | FWUPD_ERROR_INVALID_FILE, |
214 | 110 | "length invalid, length 0x%x, size 0x%x", |
215 | 110 | (guint)length, |
216 | 110 | (guint)bufsz); |
217 | 110 | return FALSE; |
218 | 110 | } |
219 | 31.2k | if (content_addr > bufsz - length) { |
220 | 35 | g_set_error(error, |
221 | 35 | FWUPD_ERROR, |
222 | 35 | FWUPD_ERROR_INVALID_FILE, |
223 | 35 | "address invalid, got 0x%x (length 0x%x), size 0x%x", |
224 | 35 | (guint)content_addr, |
225 | 35 | (guint)length, |
226 | 35 | (guint)bufsz); |
227 | 35 | return FALSE; |
228 | 35 | } |
229 | 31.2k | switch (container_id) { |
230 | 949 | case FU_RMI_CONTAINER_ID_BL: |
231 | 949 | if (!fu_memread_uint8_safe(buf, |
232 | 949 | bufsz, |
233 | 949 | content_addr, |
234 | 949 | &self->bootloader_version, |
235 | 949 | error)) |
236 | 0 | return FALSE; |
237 | 949 | break; |
238 | 4.17k | case FU_RMI_CONTAINER_ID_UI: |
239 | 4.68k | case FU_RMI_CONTAINER_ID_CORE_CODE: |
240 | 4.68k | if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, |
241 | 4.68k | "ui", |
242 | 4.68k | stream, |
243 | 4.68k | content_addr, |
244 | 4.68k | length, |
245 | 4.68k | signature_size, |
246 | 4.68k | flags, |
247 | 4.68k | error)) |
248 | 31 | return FALSE; |
249 | 4.65k | break; |
250 | 4.65k | case FU_RMI_CONTAINER_ID_FLASH_CONFIG: |
251 | 2.99k | if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, |
252 | 2.99k | "flash-config", |
253 | 2.99k | stream, |
254 | 2.99k | content_addr, |
255 | 2.99k | length, |
256 | 2.99k | signature_size, |
257 | 2.99k | flags, |
258 | 2.99k | error)) |
259 | 8 | return FALSE; |
260 | 2.99k | break; |
261 | 3.90k | case FU_RMI_CONTAINER_ID_UI_CONFIG: |
262 | 6.10k | case FU_RMI_CONTAINER_ID_CORE_CONFIG: |
263 | 6.10k | if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, |
264 | 6.10k | "config", |
265 | 6.10k | stream, |
266 | 6.10k | content_addr, |
267 | 6.10k | length, |
268 | 6.10k | signature_size, |
269 | 6.10k | flags, |
270 | 6.10k | error)) |
271 | 8 | return FALSE; |
272 | 6.09k | break; |
273 | 6.09k | case FU_RMI_CONTAINER_ID_FIXED_LOCATION_DATA: |
274 | 1.07k | if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, |
275 | 1.07k | "fixed-location-data", |
276 | 1.07k | stream, |
277 | 1.07k | content_addr, |
278 | 1.07k | length, |
279 | 1.07k | signature_size, |
280 | 1.07k | flags, |
281 | 1.07k | error)) |
282 | 4 | return FALSE; |
283 | 1.06k | break; |
284 | 2.48k | case FU_RMI_CONTAINER_ID_EXTERNAL_TOUCH_AFE_CONFIG: |
285 | 2.48k | if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, |
286 | 2.48k | "afe-config", |
287 | 2.48k | stream, |
288 | 2.48k | content_addr, |
289 | 2.48k | length, |
290 | 2.48k | signature_size, |
291 | 2.48k | flags, |
292 | 2.48k | error)) |
293 | 17 | return FALSE; |
294 | 2.47k | break; |
295 | 2.47k | case FU_RMI_CONTAINER_ID_DISPLAY_CONFIG: |
296 | 2.33k | if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, |
297 | 2.33k | "display-config", |
298 | 2.33k | stream, |
299 | 2.33k | content_addr, |
300 | 2.33k | length, |
301 | 2.33k | signature_size, |
302 | 2.33k | flags, |
303 | 2.33k | error)) |
304 | 6 | return FALSE; |
305 | 2.32k | break; |
306 | 2.32k | case FU_RMI_CONTAINER_ID_GENERAL_INFORMATION: |
307 | 687 | if (length < 0x18 + RMI_PRODUCT_ID_LENGTH) { |
308 | 7 | g_set_error(error, |
309 | 7 | FWUPD_ERROR, |
310 | 7 | FWUPD_ERROR_INVALID_FILE, |
311 | 7 | "content_addr invalid, got 0x%x (length 0x%x)", |
312 | 7 | content_addr, |
313 | 7 | (guint)length); |
314 | 7 | return FALSE; |
315 | 7 | } |
316 | 687 | g_clear_pointer(&self->product_id, g_free); |
317 | 680 | self->io = 1; |
318 | 680 | if (!fu_memread_uint32_safe(buf, |
319 | 680 | bufsz, |
320 | 680 | content_addr, |
321 | 680 | &self->package_id, |
322 | 680 | G_LITTLE_ENDIAN, |
323 | 680 | error)) |
324 | 0 | return FALSE; |
325 | 680 | if (!fu_memread_uint32_safe(buf, |
326 | 680 | bufsz, |
327 | 680 | content_addr + 0x04, |
328 | 680 | &self->build_id, |
329 | 680 | G_LITTLE_ENDIAN, |
330 | 680 | error)) |
331 | 0 | return FALSE; |
332 | 680 | if (!fu_memcpy_safe(product_id, |
333 | 680 | sizeof(product_id), |
334 | 680 | 0x0, /* dst */ |
335 | 680 | buf, |
336 | 680 | bufsz, |
337 | 680 | content_addr + 0x18, /* src */ |
338 | 680 | sizeof(product_id), |
339 | 680 | error)) |
340 | 0 | return FALSE; |
341 | 680 | break; |
342 | 9.93k | default: |
343 | 9.93k | g_debug("unsupported container %s [0x%02x]", |
344 | 9.93k | fu_rmi_container_id_to_string(container_id), |
345 | 9.93k | container_id); |
346 | 9.93k | break; |
347 | 31.2k | } |
348 | 31.1k | offset += 4; |
349 | 31.1k | } |
350 | 96 | if (product_id[0] != '\0') { |
351 | 2 | g_free(self->product_id); |
352 | 2 | self->product_id = g_strndup((const gchar *)product_id, sizeof(product_id)); |
353 | 2 | } |
354 | 96 | return TRUE; |
355 | 744 | } |
356 | | |
357 | | static gboolean |
358 | | fu_synaptics_rmi_firmware_parse_v0x(FuFirmware *firmware, |
359 | | GInputStream *stream, |
360 | | FuFirmwareParseFlags flags, |
361 | | GError **error) |
362 | 145 | { |
363 | 145 | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); |
364 | 145 | guint32 cfg_sz; |
365 | 145 | guint32 img_sz; |
366 | 145 | g_autoptr(GByteArray) st_img = NULL; |
367 | | |
368 | | /* main firmware */ |
369 | 145 | st_img = fu_struct_rmi_img_parse_stream(stream, 0x0, error); |
370 | 145 | if (st_img == NULL) |
371 | 0 | return FALSE; |
372 | 145 | img_sz = fu_struct_rmi_img_get_image_size(st_img); |
373 | 145 | if (img_sz > 0) { |
374 | | /* payload, then signature appended */ |
375 | 93 | if (self->sig_size > 0) { |
376 | 74 | guint32 sig_offset = img_sz - self->sig_size; |
377 | 74 | if (!fu_synaptics_rmi_firmware_add_image(firmware, |
378 | 74 | "sig", |
379 | 74 | stream, |
380 | 74 | RMI_IMG_FW_OFFSET + sig_offset, |
381 | 74 | self->sig_size, |
382 | 74 | flags, |
383 | 74 | error)) |
384 | 71 | return FALSE; |
385 | 74 | } |
386 | 22 | if (!fu_synaptics_rmi_firmware_add_image(firmware, |
387 | 22 | "ui", |
388 | 22 | stream, |
389 | 22 | RMI_IMG_FW_OFFSET, |
390 | 22 | img_sz, |
391 | 22 | flags, |
392 | 22 | error)) |
393 | 15 | return FALSE; |
394 | 22 | } |
395 | | |
396 | | /* config */ |
397 | 59 | cfg_sz = fu_struct_rmi_img_get_config_size(st_img); |
398 | 59 | if (cfg_sz > 0) { |
399 | 54 | if (!fu_synaptics_rmi_firmware_add_image(firmware, |
400 | 54 | "config", |
401 | 54 | stream, |
402 | 54 | RMI_IMG_FW_OFFSET + img_sz, |
403 | 54 | cfg_sz, |
404 | 54 | flags, |
405 | 54 | error)) |
406 | 53 | return FALSE; |
407 | 54 | } |
408 | 6 | return TRUE; |
409 | 59 | } |
410 | | |
411 | | static gboolean |
412 | | fu_synaptics_rmi_firmware_parse(FuFirmware *firmware, |
413 | | GInputStream *stream, |
414 | | FuFirmwareParseFlags flags, |
415 | | GError **error) |
416 | 2.04k | { |
417 | 2.04k | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); |
418 | 2.04k | gsize bufsz = 0; |
419 | 2.04k | const guint8 *buf; |
420 | 2.04k | g_autoptr(GByteArray) st_img = NULL; |
421 | 2.04k | g_autoptr(GBytes) fw = NULL; |
422 | | |
423 | | /* maybe stream later */ |
424 | 2.04k | fw = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, NULL, error); |
425 | 2.04k | if (fw == NULL) |
426 | 0 | return FALSE; |
427 | 2.04k | buf = g_bytes_get_data(fw, &bufsz); |
428 | | |
429 | | /* sanity check */ |
430 | 2.04k | st_img = fu_struct_rmi_img_parse_stream(stream, 0x0, error); |
431 | 2.04k | if (st_img == NULL) |
432 | 52 | return FALSE; |
433 | 1.99k | if (bufsz % 2 != 0) { |
434 | 62 | g_set_error_literal(error, |
435 | 62 | FWUPD_ERROR, |
436 | 62 | FWUPD_ERROR_INVALID_FILE, |
437 | 62 | "data not aligned to 16 bits"); |
438 | 62 | return FALSE; |
439 | 62 | } |
440 | 1.93k | if (bufsz < 4) { |
441 | 0 | g_set_error_literal(error, |
442 | 0 | FWUPD_ERROR, |
443 | 0 | FWUPD_ERROR_INVALID_FILE, |
444 | 0 | "stream was too small"); |
445 | 0 | return FALSE; |
446 | 0 | } |
447 | | |
448 | | /* verify checksum */ |
449 | 1.93k | self->checksum = fu_struct_rmi_img_get_checksum(st_img); |
450 | 1.93k | if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { |
451 | 967 | guint32 checksum_calculated = |
452 | 967 | fu_synaptics_rmi_generate_checksum(buf + 4, bufsz - 4); |
453 | 967 | if (self->checksum != checksum_calculated) { |
454 | 893 | g_set_error(error, |
455 | 893 | FWUPD_ERROR, |
456 | 893 | FWUPD_ERROR_INVALID_FILE, |
457 | 893 | "checksum verification failed, got 0x%08x, actual 0x%08x", |
458 | 893 | (guint)self->checksum, |
459 | 893 | (guint)checksum_calculated); |
460 | 893 | return FALSE; |
461 | 893 | } |
462 | 967 | } |
463 | | |
464 | | /* parse legacy image */ |
465 | 1.93k | g_clear_pointer(&self->product_id, g_free); |
466 | 1.03k | self->io = fu_struct_rmi_img_get_io_offset(st_img); |
467 | 1.03k | self->bootloader_version = fu_struct_rmi_img_get_bootloader_version(st_img); |
468 | 1.03k | if (self->io == 1) { |
469 | 122 | self->build_id = fu_struct_rmi_img_get_fw_build_id(st_img); |
470 | 122 | self->package_id = fu_struct_rmi_img_get_package_id(st_img); |
471 | 122 | } |
472 | 1.03k | self->product_id = fu_struct_rmi_img_get_product_id(st_img); |
473 | 1.03k | self->product_info = fu_struct_rmi_img_get_product_info(st_img); |
474 | 1.03k | fu_firmware_set_size(firmware, fu_struct_rmi_img_get_image_size(st_img)); |
475 | | |
476 | | /* parse partitions, but ignore lockdown */ |
477 | 1.03k | switch (self->bootloader_version) { |
478 | 41 | case 2: |
479 | 86 | case 3: |
480 | 126 | case 4: |
481 | 139 | case 5: |
482 | 145 | case 6: |
483 | 145 | if ((self->io & 0x10) >> 1) |
484 | 117 | self->sig_size = fu_struct_rmi_img_get_signature_size(st_img); |
485 | 145 | if (!fu_synaptics_rmi_firmware_parse_v0x(firmware, stream, flags, error)) |
486 | 139 | return FALSE; |
487 | 6 | self->kind = RMI_FIRMWARE_KIND_0X; |
488 | 6 | break; |
489 | 688 | case 16: |
490 | 856 | case 17: |
491 | 856 | if (!fu_synaptics_rmi_firmware_parse_v10(firmware, stream, flags, error)) |
492 | 760 | return FALSE; |
493 | 96 | self->kind = RMI_FIRMWARE_KIND_10; |
494 | 96 | break; |
495 | 36 | default: |
496 | 36 | g_set_error(error, |
497 | 36 | FWUPD_ERROR, |
498 | 36 | FWUPD_ERROR_INVALID_FILE, |
499 | 36 | "unsupported image version 0x%02x", |
500 | 36 | self->bootloader_version); |
501 | 36 | return FALSE; |
502 | 1.03k | } |
503 | | |
504 | | /* success */ |
505 | 102 | return TRUE; |
506 | 1.03k | } |
507 | | |
508 | | guint32 |
509 | | fu_synaptics_rmi_firmware_get_sig_size(FuSynapticsRmiFirmware *self) |
510 | 0 | { |
511 | 0 | return self->sig_size; |
512 | 0 | } |
513 | | |
514 | | static GByteArray * |
515 | | fu_synaptics_rmi_firmware_write_v0x(FuFirmware *firmware, GError **error) |
516 | 6 | { |
517 | 6 | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); |
518 | 6 | gsize bufsz = 0; |
519 | 6 | guint32 csum; |
520 | 6 | g_autoptr(FuFirmware) img = NULL; |
521 | 6 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
522 | 6 | g_autoptr(GByteArray) st_img = fu_struct_rmi_img_new(); |
523 | 6 | g_autoptr(GBytes) buf_blob = NULL; |
524 | | |
525 | | /* default image */ |
526 | 6 | img = fu_firmware_get_image_by_id(firmware, "ui", error); |
527 | 6 | if (img == NULL) |
528 | 2 | return NULL; |
529 | 4 | buf_blob = fu_firmware_write(img, error); |
530 | 4 | if (buf_blob == NULL) |
531 | 4 | return NULL; |
532 | 0 | bufsz = g_bytes_get_size(buf_blob); |
533 | | |
534 | | /* create empty block */ |
535 | 0 | fu_struct_rmi_img_set_bootloader_version(st_img, 0x2); /* not hierarchical */ |
536 | 0 | if (self->product_id != NULL) { |
537 | 0 | if (!fu_struct_rmi_img_set_product_id(st_img, self->product_id, error)) |
538 | 0 | return NULL; |
539 | 0 | } |
540 | 0 | fu_struct_rmi_img_set_product_info(st_img, 0x1234); |
541 | 0 | fu_struct_rmi_img_set_image_size(st_img, bufsz); |
542 | 0 | fu_struct_rmi_img_set_config_size(st_img, bufsz); |
543 | 0 | g_byte_array_append(buf, st_img->data, st_img->len); |
544 | 0 | fu_byte_array_set_size(buf, RMI_IMG_FW_OFFSET + 0x4 + bufsz, 0x00); |
545 | 0 | fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET, 0xDEAD, G_LITTLE_ENDIAN); /* img */ |
546 | 0 | fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + bufsz, |
547 | 0 | 0xBEEF, |
548 | 0 | G_LITTLE_ENDIAN); /* config */ |
549 | | |
550 | | /* fixup checksum */ |
551 | 0 | csum = fu_synaptics_rmi_generate_checksum(buf->data + 4, buf->len - 4); |
552 | 0 | fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_CHECKSUM, csum, G_LITTLE_ENDIAN); |
553 | | |
554 | | /* success */ |
555 | 0 | return g_steal_pointer(&buf); |
556 | 0 | } |
557 | | |
558 | | static GByteArray * |
559 | | fu_synaptics_rmi_firmware_write_v10(FuFirmware *firmware, GError **error) |
560 | 96 | { |
561 | 96 | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); |
562 | 96 | gsize bufsz; |
563 | 96 | guint32 csum; |
564 | 96 | g_autoptr(FuFirmware) img = NULL; |
565 | 96 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
566 | 96 | g_autoptr(GByteArray) desc_hdr = fu_struct_rmi_container_descriptor_new(); |
567 | 96 | g_autoptr(GByteArray) desc = fu_struct_rmi_container_descriptor_new(); |
568 | 96 | g_autoptr(GBytes) buf_blob = NULL; |
569 | | |
570 | | /* header | desc_hdr | offset_table | desc | flash_config | |
571 | | * \0x0 \0x20 \0x24 \0x44 |0x48 */ |
572 | 96 | guint32 offset_table[] = {/* offset to first descriptor */ |
573 | 96 | GUINT32_TO_LE(RMI_IMG_FW_OFFSET + 0x24)}; /* nocheck:blocked */ |
574 | 96 | fu_struct_rmi_container_descriptor_set_container_id(desc, FU_RMI_CONTAINER_ID_FLASH_CONFIG); |
575 | 96 | fu_struct_rmi_container_descriptor_set_content_address(desc, RMI_IMG_FW_OFFSET + 0x44); |
576 | | |
577 | | /* default image */ |
578 | 96 | img = fu_firmware_get_image_by_id(firmware, "ui", error); |
579 | 96 | if (img == NULL) |
580 | 60 | return NULL; |
581 | 36 | buf_blob = fu_firmware_write(img, error); |
582 | 36 | if (buf_blob == NULL) |
583 | 36 | return NULL; |
584 | 0 | bufsz = g_bytes_get_size(buf_blob); |
585 | 0 | fu_struct_rmi_container_descriptor_set_content_length(desc, bufsz); |
586 | | |
587 | | /* create empty block */ |
588 | 0 | fu_byte_array_set_size(buf, RMI_IMG_FW_OFFSET + 0x48, 0x00); |
589 | 0 | buf->data[FU_STRUCT_RMI_IMG_OFFSET_IO_OFFSET] = 0x1; |
590 | 0 | buf->data[FU_STRUCT_RMI_IMG_OFFSET_BOOTLOADER_VERSION] = 16; /* hierarchical */ |
591 | 0 | if (self->product_id != NULL) { |
592 | 0 | gsize product_id_sz = strlen(self->product_id); |
593 | 0 | if (!fu_memcpy_safe(buf->data, |
594 | 0 | buf->len, |
595 | 0 | FU_STRUCT_RMI_IMG_OFFSET_PRODUCT_ID, /* dst */ |
596 | 0 | (const guint8 *)self->product_id, |
597 | 0 | product_id_sz, |
598 | 0 | 0x0, /* src */ |
599 | 0 | product_id_sz, |
600 | 0 | error)) |
601 | 0 | return NULL; |
602 | 0 | } |
603 | 0 | fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_FW_BUILD_ID, |
604 | 0 | 0x1234, |
605 | 0 | G_LITTLE_ENDIAN); |
606 | 0 | fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_PACKAGE_ID, |
607 | 0 | 0x4321, |
608 | 0 | G_LITTLE_ENDIAN); |
609 | 0 | fu_memwrite_uint16(buf->data + FU_STRUCT_RMI_IMG_OFFSET_PRODUCT_INFO, |
610 | 0 | 0x3456, |
611 | 0 | G_LITTLE_ENDIAN); |
612 | 0 | fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_IMAGE_SIZE, bufsz, G_LITTLE_ENDIAN); |
613 | 0 | fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_CONFIG_SIZE, |
614 | 0 | bufsz, |
615 | 0 | G_LITTLE_ENDIAN); |
616 | 0 | fu_memwrite_uint32(buf->data + RMI_IMG_V10_CNTR_ADDR_OFFSET, |
617 | 0 | RMI_IMG_FW_OFFSET, |
618 | 0 | G_LITTLE_ENDIAN); |
619 | | |
620 | | /* hierarchical section */ |
621 | 0 | fu_struct_rmi_container_descriptor_set_container_id(desc_hdr, |
622 | 0 | FU_RMI_CONTAINER_ID_TOP_LEVEL); |
623 | 0 | fu_struct_rmi_container_descriptor_set_content_length(desc_hdr, 0x1 * 4); /* bytes */ |
624 | 0 | fu_struct_rmi_container_descriptor_set_content_address(desc_hdr, |
625 | 0 | RMI_IMG_FW_OFFSET + |
626 | 0 | 0x20); /* offset to table */ |
627 | 0 | memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x00, /* nocheck:blocked */ |
628 | 0 | desc_hdr->data, |
629 | 0 | desc_hdr->len); |
630 | 0 | memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x20, /* nocheck:blocked */ |
631 | 0 | offset_table, |
632 | 0 | sizeof(offset_table)); |
633 | 0 | memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x24, /* nocheck:blocked */ |
634 | 0 | desc->data, |
635 | 0 | desc->len); |
636 | 0 | fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + 0x44, |
637 | 0 | 0xfeed, |
638 | 0 | G_LITTLE_ENDIAN); /* flash_config */ |
639 | | |
640 | | /* fixup checksum */ |
641 | 0 | csum = fu_synaptics_rmi_generate_checksum(buf->data + 4, buf->len - 4); |
642 | 0 | fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_CHECKSUM, csum, G_LITTLE_ENDIAN); |
643 | | |
644 | | /* success */ |
645 | 0 | return g_steal_pointer(&buf); |
646 | 0 | } |
647 | | |
648 | | static gboolean |
649 | | fu_synaptics_rmi_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) |
650 | 0 | { |
651 | 0 | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); |
652 | 0 | const gchar *product_id; |
653 | 0 | guint64 tmp; |
654 | | |
655 | | /* either 0x or 10 */ |
656 | 0 | tmp = xb_node_query_text_as_uint(n, "kind", NULL); |
657 | 0 | if (tmp != G_MAXUINT64) |
658 | 0 | self->kind = tmp; |
659 | | |
660 | | /* any string */ |
661 | 0 | product_id = xb_node_query_text(n, "product_id", NULL); |
662 | 0 | if (product_id != NULL) { |
663 | 0 | gsize product_id_sz = strlen(product_id); |
664 | 0 | if (product_id_sz > RMI_PRODUCT_ID_LENGTH) { |
665 | 0 | g_set_error(error, |
666 | 0 | FWUPD_ERROR, |
667 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
668 | 0 | "product_id not supported, %u of %u bytes", |
669 | 0 | (guint)product_id_sz, |
670 | 0 | (guint)RMI_PRODUCT_ID_LENGTH); |
671 | 0 | return FALSE; |
672 | 0 | } |
673 | 0 | g_free(self->product_id); |
674 | 0 | self->product_id = g_strdup(product_id); |
675 | 0 | } |
676 | | |
677 | | /* success */ |
678 | 0 | return TRUE; |
679 | 0 | } |
680 | | |
681 | | static GByteArray * |
682 | | fu_synaptics_rmi_firmware_write(FuFirmware *firmware, GError **error) |
683 | 102 | { |
684 | 102 | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); |
685 | | |
686 | | /* two supported container formats */ |
687 | 102 | if (self->kind == RMI_FIRMWARE_KIND_0X) |
688 | 6 | return fu_synaptics_rmi_firmware_write_v0x(firmware, error); |
689 | 96 | if (self->kind == RMI_FIRMWARE_KIND_10) |
690 | 96 | return fu_synaptics_rmi_firmware_write_v10(firmware, error); |
691 | | |
692 | | /* not supported */ |
693 | 0 | g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "kind not supported"); |
694 | 0 | return NULL; |
695 | 96 | } |
696 | | |
697 | | static void |
698 | | fu_synaptics_rmi_firmware_init(FuSynapticsRmiFirmware *self) |
699 | 2.04k | { |
700 | 2.04k | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); |
701 | 2.04k | fu_firmware_set_images_max(FU_FIRMWARE(self), RMI_IMG_MAX_CONTAINERS); |
702 | 2.04k | } |
703 | | |
704 | | static void |
705 | | fu_synaptics_rmi_firmware_finalize(GObject *obj) |
706 | 2.04k | { |
707 | 2.04k | FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(obj); |
708 | 2.04k | g_free(self->product_id); |
709 | 2.04k | G_OBJECT_CLASS(fu_synaptics_rmi_firmware_parent_class)->finalize(obj); |
710 | 2.04k | } |
711 | | |
712 | | static void |
713 | | fu_synaptics_rmi_firmware_class_init(FuSynapticsRmiFirmwareClass *klass) |
714 | 1 | { |
715 | 1 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
716 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
717 | 1 | object_class->finalize = fu_synaptics_rmi_firmware_finalize; |
718 | 1 | firmware_class->parse = fu_synaptics_rmi_firmware_parse; |
719 | 1 | firmware_class->export = fu_synaptics_rmi_firmware_export; |
720 | 1 | firmware_class->build = fu_synaptics_rmi_firmware_build; |
721 | 1 | firmware_class->write = fu_synaptics_rmi_firmware_write; |
722 | 1 | } |
723 | | |
724 | | FuFirmware * |
725 | | fu_synaptics_rmi_firmware_new(void) |
726 | 0 | { |
727 | 0 | return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPTICS_RMI_FIRMWARE, NULL)); |
728 | 0 | } |