/src/fwupd/plugins/elanfp/fu-elanfp-firmware.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2021 Michael Cheng <michael.cheng@emc.com.tw> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | | #include "config.h" |
8 | | |
9 | | #include "fu-elanfp-firmware.h" |
10 | | #include "fu-elanfp-struct.h" |
11 | | |
12 | | struct _FuElanfpFirmware { |
13 | | FuFirmwareClass parent_instance; |
14 | | guint32 format_version; |
15 | | }; |
16 | | |
17 | | G_DEFINE_TYPE(FuElanfpFirmware, fu_elanfp_firmware, FU_TYPE_FIRMWARE) |
18 | | |
19 | | static void |
20 | | fu_elanfp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) |
21 | 0 | { |
22 | 0 | FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); |
23 | 0 | fu_xmlb_builder_insert_kx(bn, "format_version", self->format_version); |
24 | 0 | } |
25 | | |
26 | | static gboolean |
27 | | fu_elanfp_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) |
28 | 0 | { |
29 | 0 | FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); |
30 | 0 | guint64 tmp; |
31 | | |
32 | | /* optional properties */ |
33 | 0 | tmp = xb_node_query_text_as_uint(n, "format_version", NULL); |
34 | 0 | if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) |
35 | 0 | self->format_version = tmp; |
36 | | |
37 | | /* success */ |
38 | 0 | return TRUE; |
39 | 0 | } |
40 | | |
41 | | static gboolean |
42 | | fu_elanfp_firmware_validate(FuFirmware *firmware, |
43 | | GInputStream *stream, |
44 | | gsize offset, |
45 | | GError **error) |
46 | 14.3M | { |
47 | 14.3M | return fu_struct_elanfp_firmware_hdr_validate_stream(stream, offset, error); |
48 | 14.3M | } |
49 | | |
50 | | static gboolean |
51 | | fu_elanfp_firmware_parse(FuFirmware *firmware, |
52 | | GInputStream *stream, |
53 | | FuFirmwareParseFlags flags, |
54 | | GError **error) |
55 | 752 | { |
56 | 752 | FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); |
57 | 752 | gsize offset = 0; |
58 | | |
59 | | /* file format version */ |
60 | 752 | if (!fu_input_stream_read_u32(stream, |
61 | 752 | offset + 0x4, |
62 | 752 | &self->format_version, |
63 | 752 | G_LITTLE_ENDIAN, |
64 | 752 | error)) |
65 | 41 | return FALSE; |
66 | | |
67 | | /* read indexes */ |
68 | 711 | offset += 0x10; |
69 | 7.14k | while (1) { |
70 | 7.14k | guint32 start_addr = 0; |
71 | 7.14k | guint32 length = 0; |
72 | 7.14k | guint32 fwtype = 0; |
73 | 7.14k | g_autoptr(FuFirmware) img = NULL; |
74 | 7.14k | g_autoptr(GInputStream) stream_tmp = NULL; |
75 | | |
76 | | /* type, reserved, start-addr, len */ |
77 | 7.14k | if (!fu_input_stream_read_u32(stream, |
78 | 7.14k | offset + 0x0, |
79 | 7.14k | &fwtype, |
80 | 7.14k | G_LITTLE_ENDIAN, |
81 | 7.14k | error)) |
82 | 156 | return FALSE; |
83 | | |
84 | | /* check not already added */ |
85 | 6.98k | img = fu_firmware_get_image_by_idx(firmware, fwtype, NULL); |
86 | 6.98k | if (img != NULL) { |
87 | 6 | g_set_error(error, |
88 | 6 | FWUPD_ERROR, |
89 | 6 | FWUPD_ERROR_NOT_SUPPORTED, |
90 | 6 | "already parsed image with fwtype 0x%x", |
91 | 6 | fwtype); |
92 | 6 | return FALSE; |
93 | 6 | } |
94 | | |
95 | | /* done */ |
96 | 6.98k | if (fwtype == FU_ELANTP_FIRMWARE_IDX_END) |
97 | 156 | break; |
98 | 6.82k | switch (fwtype) { |
99 | 112 | case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_A: |
100 | 215 | case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_B: |
101 | 215 | img = fu_cfu_offer_new(); |
102 | 215 | break; |
103 | 320 | case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_A: |
104 | 569 | case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_B: |
105 | 569 | img = fu_cfu_payload_new(); |
106 | 569 | break; |
107 | 6.04k | default: |
108 | 6.04k | img = fu_firmware_new(); |
109 | 6.04k | break; |
110 | 6.82k | } |
111 | 6.82k | fu_firmware_set_idx(img, fwtype); |
112 | 6.82k | if (!fu_input_stream_read_u32(stream, |
113 | 6.82k | offset + 0x8, |
114 | 6.82k | &start_addr, |
115 | 6.82k | G_LITTLE_ENDIAN, |
116 | 6.82k | error)) |
117 | 132 | return FALSE; |
118 | 6.69k | fu_firmware_set_addr(img, start_addr); |
119 | 6.69k | if (!fu_input_stream_read_u32(stream, |
120 | 6.69k | offset + 0xC, |
121 | 6.69k | &length, |
122 | 6.69k | G_LITTLE_ENDIAN, |
123 | 6.69k | error)) |
124 | 23 | return FALSE; |
125 | 6.67k | if (length == 0) { |
126 | 7 | g_set_error(error, |
127 | 7 | FWUPD_ERROR, |
128 | 7 | FWUPD_ERROR_NOT_SUPPORTED, |
129 | 7 | "zero size fwtype 0x%x not supported", |
130 | 7 | fwtype); |
131 | 7 | return FALSE; |
132 | 7 | } |
133 | | |
134 | 6.66k | stream_tmp = fu_partial_input_stream_new(stream, start_addr, length, error); |
135 | 6.66k | if (stream_tmp == NULL) |
136 | 165 | return FALSE; |
137 | 6.50k | if (!fu_firmware_parse_stream(img, |
138 | 6.50k | stream_tmp, |
139 | 6.50k | 0x0, |
140 | 6.50k | flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, |
141 | 6.50k | error)) |
142 | 66 | return FALSE; |
143 | 6.43k | if (!fu_firmware_add_image_full(firmware, img, error)) |
144 | 0 | return FALSE; |
145 | | |
146 | 6.43k | offset += 0x10; |
147 | 6.43k | } |
148 | | |
149 | | /* success */ |
150 | 156 | return TRUE; |
151 | 711 | } |
152 | | |
153 | | static GByteArray * |
154 | | fu_elanfp_firmware_write(FuFirmware *firmware, GError **error) |
155 | 156 | { |
156 | 156 | FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); |
157 | 156 | gsize offset = 0; |
158 | 156 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
159 | 156 | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
160 | | |
161 | | /* S2F_HEADER */ |
162 | 156 | fu_byte_array_append_uint32(buf, 0x46325354, G_LITTLE_ENDIAN); |
163 | 156 | fu_byte_array_append_uint32(buf, self->format_version, G_LITTLE_ENDIAN); |
164 | 156 | fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* ICID, assumed */ |
165 | 156 | fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ |
166 | | |
167 | | /* S2F_INDEX */ |
168 | 156 | offset += 0x10 + ((imgs->len + 1) * 0x10); |
169 | 393 | for (guint i = 0; i < imgs->len; i++) { |
170 | 299 | FuFirmware *img = g_ptr_array_index(imgs, i); |
171 | 299 | g_autoptr(GBytes) blob = fu_firmware_write(img, error); |
172 | 299 | if (blob == NULL) |
173 | 62 | return NULL; |
174 | 237 | fu_byte_array_append_uint32(buf, fu_firmware_get_idx(img), G_LITTLE_ENDIAN); |
175 | 237 | fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ |
176 | 237 | fu_byte_array_append_uint32(buf, offset, G_LITTLE_ENDIAN); |
177 | 237 | fu_byte_array_append_uint32(buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN); |
178 | 237 | offset += g_bytes_get_size(blob); |
179 | 237 | } |
180 | | |
181 | | /* end of index */ |
182 | 94 | fu_byte_array_append_uint32(buf, FU_ELANTP_FIRMWARE_IDX_END, G_LITTLE_ENDIAN); |
183 | 94 | fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ |
184 | 94 | fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */ |
185 | 94 | fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */ |
186 | | |
187 | | /* data */ |
188 | 268 | for (guint i = 0; i < imgs->len; i++) { |
189 | 174 | FuFirmware *img = g_ptr_array_index(imgs, i); |
190 | 174 | g_autoptr(GBytes) blob = fu_firmware_write(img, error); |
191 | 174 | if (blob == NULL) |
192 | 0 | return NULL; |
193 | 174 | fu_byte_array_append_bytes(buf, blob); |
194 | 174 | } |
195 | | |
196 | | /* success */ |
197 | 94 | return g_steal_pointer(&buf); |
198 | 94 | } |
199 | | |
200 | | static void |
201 | | fu_elanfp_firmware_init(FuElanfpFirmware *self) |
202 | 901 | { |
203 | 901 | fu_firmware_set_images_max(FU_FIRMWARE(self), 256); |
204 | 901 | g_type_ensure(FU_TYPE_CFU_OFFER); |
205 | 901 | g_type_ensure(FU_TYPE_CFU_PAYLOAD); |
206 | 901 | } |
207 | | |
208 | | static void |
209 | | fu_elanfp_firmware_class_init(FuElanfpFirmwareClass *klass) |
210 | 1 | { |
211 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
212 | 1 | firmware_class->validate = fu_elanfp_firmware_validate; |
213 | 1 | firmware_class->parse = fu_elanfp_firmware_parse; |
214 | 1 | firmware_class->write = fu_elanfp_firmware_write; |
215 | 1 | firmware_class->export = fu_elanfp_firmware_export; |
216 | 1 | firmware_class->build = fu_elanfp_firmware_build; |
217 | 1 | } |