/src/fwupd/libfwupdplugin/fu-efi-hard-drive-device-path.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2023 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuEfiDevicePath" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-common.h" |
12 | | #include "fu-efi-hard-drive-device-path.h" |
13 | | #include "fu-efi-struct.h" |
14 | | #include "fu-firmware-common.h" |
15 | | #include "fu-mem.h" |
16 | | #include "fu-string.h" |
17 | | |
18 | | /** |
19 | | * FuEfiHardDriveDevicePath: |
20 | | * |
21 | | * See also: [class@FuEfiDevicePath] |
22 | | */ |
23 | | |
24 | | struct _FuEfiHardDriveDevicePath { |
25 | | FuEfiDevicePath parent_instance; |
26 | | guint32 partition_number; |
27 | | guint64 partition_start; /* blocks */ |
28 | | guint64 partition_size; /* blocks */ |
29 | | fwupd_guid_t partition_signature; |
30 | | FuEfiHardDriveDevicePathPartitionFormat partition_format; |
31 | | FuEfiHardDriveDevicePathSignatureType signature_type; |
32 | | }; |
33 | | |
34 | | static void |
35 | | fu_efi_hard_drive_device_path_codec_iface_init(FwupdCodecInterface *iface); |
36 | | |
37 | | G_DEFINE_TYPE_EXTENDED(FuEfiHardDriveDevicePath, |
38 | | fu_efi_hard_drive_device_path, |
39 | | FU_TYPE_EFI_DEVICE_PATH, |
40 | | 0, |
41 | | G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, |
42 | | fu_efi_hard_drive_device_path_codec_iface_init)) |
43 | | |
44 | 0 | #define BLOCK_SIZE_FALLBACK 0x200 |
45 | | |
46 | | static void |
47 | | fu_efi_hard_drive_device_path_export(FuFirmware *firmware, |
48 | | FuFirmwareExportFlags flags, |
49 | | XbBuilderNode *bn) |
50 | 0 | { |
51 | 0 | FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware); |
52 | 0 | g_autofree gchar *partition_signature = |
53 | 0 | fwupd_guid_to_string(&self->partition_signature, FWUPD_GUID_FLAG_MIXED_ENDIAN); |
54 | 0 | fu_xmlb_builder_insert_kx(bn, "partition_number", self->partition_number); |
55 | 0 | fu_xmlb_builder_insert_kx(bn, "partition_start", self->partition_start); |
56 | 0 | fu_xmlb_builder_insert_kx(bn, "partition_size", self->partition_size); |
57 | 0 | fu_xmlb_builder_insert_kv(bn, "partition_signature", partition_signature); |
58 | 0 | fu_xmlb_builder_insert_kv( |
59 | 0 | bn, |
60 | 0 | "partition_format", |
61 | 0 | fu_efi_hard_drive_device_path_partition_format_to_string(self->partition_format)); |
62 | 0 | fu_xmlb_builder_insert_kv( |
63 | 0 | bn, |
64 | 0 | "signature_type", |
65 | 0 | fu_efi_hard_drive_device_path_signature_type_to_string(self->signature_type)); |
66 | 0 | } |
67 | | |
68 | | static void |
69 | | fu_efi_hard_drive_device_path_add_json(FwupdCodec *codec, |
70 | | JsonBuilder *builder, |
71 | | FwupdCodecFlags flags) |
72 | 0 | { |
73 | 0 | FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(codec); |
74 | 0 | g_autofree gchar *partition_signature = |
75 | 0 | fwupd_guid_to_string(&self->partition_signature, FWUPD_GUID_FLAG_MIXED_ENDIAN); |
76 | |
|
77 | 0 | fwupd_codec_json_append_int(builder, "PartitionNumber", self->partition_number); |
78 | 0 | fwupd_codec_json_append_int(builder, "PartitionStart", self->partition_start); |
79 | 0 | fwupd_codec_json_append_int(builder, "PartitionSize", self->partition_size); |
80 | 0 | fwupd_codec_json_append(builder, "PartitionSignature", partition_signature); |
81 | 0 | fwupd_codec_json_append( |
82 | 0 | builder, |
83 | 0 | "PartitionFormat", |
84 | 0 | fu_efi_hard_drive_device_path_partition_format_to_string(self->partition_format)); |
85 | 0 | fwupd_codec_json_append( |
86 | 0 | builder, |
87 | 0 | "SignatureType", |
88 | 0 | fu_efi_hard_drive_device_path_signature_type_to_string(self->signature_type)); |
89 | 0 | } |
90 | | |
91 | | static gboolean |
92 | | fu_efi_hard_drive_device_path_parse(FuFirmware *firmware, |
93 | | GInputStream *stream, |
94 | | FuFirmwareParseFlags flags, |
95 | | GError **error) |
96 | 645 | { |
97 | 645 | FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware); |
98 | 645 | g_autoptr(GByteArray) st = NULL; |
99 | | |
100 | | /* re-parse */ |
101 | 645 | st = fu_struct_efi_hard_drive_device_path_parse_stream(stream, 0x0, error); |
102 | 645 | if (st == NULL) |
103 | 32 | return FALSE; |
104 | 613 | self->partition_number = fu_struct_efi_hard_drive_device_path_get_partition_number(st); |
105 | 613 | self->partition_start = fu_struct_efi_hard_drive_device_path_get_partition_start(st); |
106 | 613 | self->partition_size = fu_struct_efi_hard_drive_device_path_get_partition_size(st); |
107 | 613 | memcpy(self->partition_signature, /* nocheck:blocked */ |
108 | 613 | fu_struct_efi_hard_drive_device_path_get_partition_signature(st), |
109 | 613 | sizeof(self->partition_signature)); |
110 | 613 | self->partition_format = fu_struct_efi_hard_drive_device_path_get_partition_format(st); |
111 | 613 | self->signature_type = fu_struct_efi_hard_drive_device_path_get_signature_type(st); |
112 | | |
113 | | /* success */ |
114 | 613 | fu_firmware_set_size(firmware, fu_struct_efi_device_path_get_length(st)); |
115 | 613 | return TRUE; |
116 | 645 | } |
117 | | |
118 | | static GByteArray * |
119 | | fu_efi_hard_drive_device_path_write(FuFirmware *firmware, GError **error) |
120 | 385 | { |
121 | 385 | FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware); |
122 | 385 | g_autoptr(GByteArray) st = fu_struct_efi_hard_drive_device_path_new(); |
123 | | |
124 | | /* required */ |
125 | 385 | fu_struct_efi_hard_drive_device_path_set_partition_number(st, self->partition_number); |
126 | 385 | fu_struct_efi_hard_drive_device_path_set_partition_start(st, self->partition_start); |
127 | 385 | fu_struct_efi_hard_drive_device_path_set_partition_size(st, self->partition_size); |
128 | 385 | fu_struct_efi_hard_drive_device_path_set_partition_signature(st, |
129 | 385 | &self->partition_signature); |
130 | 385 | fu_struct_efi_hard_drive_device_path_set_partition_format(st, self->partition_format); |
131 | 385 | fu_struct_efi_hard_drive_device_path_set_signature_type(st, self->signature_type); |
132 | | |
133 | | /* success */ |
134 | 385 | return g_steal_pointer(&st); |
135 | 385 | } |
136 | | |
137 | | static gboolean |
138 | | fu_efi_hard_drive_device_path_build(FuFirmware *firmware, XbNode *n, GError **error) |
139 | 0 | { |
140 | 0 | FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware); |
141 | 0 | const gchar *tmp; |
142 | 0 | guint64 value = 0; |
143 | | |
144 | | /* optional data */ |
145 | 0 | tmp = xb_node_query_text(n, "partition_number", NULL); |
146 | 0 | if (tmp != NULL) { |
147 | 0 | if (!fu_strtoull(tmp, &value, 0x0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) |
148 | 0 | return FALSE; |
149 | 0 | self->partition_number = value; |
150 | 0 | } |
151 | 0 | tmp = xb_node_query_text(n, "partition_start", NULL); |
152 | 0 | if (tmp != NULL) { |
153 | 0 | if (!fu_strtoull(tmp, &value, 0x0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error)) |
154 | 0 | return FALSE; |
155 | 0 | self->partition_start = value; |
156 | 0 | } |
157 | 0 | tmp = xb_node_query_text(n, "partition_size", NULL); |
158 | 0 | if (tmp != NULL) { |
159 | 0 | if (!fu_strtoull(tmp, &value, 0x0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error)) |
160 | 0 | return FALSE; |
161 | 0 | self->partition_size = value; |
162 | 0 | } |
163 | 0 | tmp = xb_node_query_text(n, "partition_signature", NULL); |
164 | 0 | if (tmp != NULL) { |
165 | 0 | if (!fwupd_guid_from_string(tmp, |
166 | 0 | &self->partition_signature, |
167 | 0 | FWUPD_GUID_FLAG_MIXED_ENDIAN, |
168 | 0 | error)) |
169 | 0 | return FALSE; |
170 | 0 | } |
171 | 0 | tmp = xb_node_query_text(n, "partition_format", NULL); |
172 | 0 | if (tmp != NULL) { |
173 | 0 | self->partition_format = |
174 | 0 | fu_efi_hard_drive_device_path_partition_format_from_string(tmp); |
175 | 0 | } |
176 | 0 | tmp = xb_node_query_text(n, "signature_type", NULL); |
177 | 0 | if (tmp != NULL) { |
178 | 0 | self->signature_type = |
179 | 0 | fu_efi_hard_drive_device_path_signature_type_from_string(tmp); |
180 | 0 | } |
181 | | |
182 | | /* success */ |
183 | 0 | return TRUE; |
184 | 0 | } |
185 | | |
186 | | static void |
187 | | fu_efi_hard_drive_device_path_init(FuEfiHardDriveDevicePath *self) |
188 | 645 | { |
189 | 645 | fu_firmware_set_idx(FU_FIRMWARE(self), FU_EFI_DEVICE_PATH_TYPE_MEDIA); |
190 | 645 | fu_efi_device_path_set_subtype(FU_EFI_DEVICE_PATH(self), |
191 | 645 | FU_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE_HARD_DRIVE); |
192 | 645 | } |
193 | | |
194 | | static void |
195 | | fu_efi_hard_drive_device_path_class_init(FuEfiHardDriveDevicePathClass *klass) |
196 | 1 | { |
197 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
198 | 1 | firmware_class->export = fu_efi_hard_drive_device_path_export; |
199 | 1 | firmware_class->parse = fu_efi_hard_drive_device_path_parse; |
200 | 1 | firmware_class->write = fu_efi_hard_drive_device_path_write; |
201 | 1 | firmware_class->build = fu_efi_hard_drive_device_path_build; |
202 | 1 | } |
203 | | |
204 | | /** |
205 | | * fu_efi_hard_drive_device_path_get_partition_signature: |
206 | | * @self: a #FuEfiHardDriveDevicePath |
207 | | * |
208 | | * Gets the DP partition signature. |
209 | | * |
210 | | * Returns: a #fwupd_guid_t |
211 | | * |
212 | | * Since: 2.0.0 |
213 | | **/ |
214 | | const fwupd_guid_t * |
215 | | fu_efi_hard_drive_device_path_get_partition_signature(FuEfiHardDriveDevicePath *self) |
216 | 0 | { |
217 | 0 | g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(self), NULL); |
218 | 0 | return &self->partition_signature; |
219 | 0 | } |
220 | | |
221 | | /** |
222 | | * fu_efi_hard_drive_device_path_get_partition_size: |
223 | | * @self: a #FuEfiHardDriveDevicePath |
224 | | * |
225 | | * Gets the DP partition size. |
226 | | * |
227 | | * NOTE: This are multiples of the block size, which can be found using fu_volume_get_block_size() |
228 | | * |
229 | | * Returns: integer |
230 | | * |
231 | | * Since: 2.0.0 |
232 | | **/ |
233 | | guint64 |
234 | | fu_efi_hard_drive_device_path_get_partition_size(FuEfiHardDriveDevicePath *self) |
235 | 0 | { |
236 | 0 | g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(self), 0); |
237 | 0 | return self->partition_size; |
238 | 0 | } |
239 | | |
240 | | /** |
241 | | * fu_efi_hard_drive_device_path_get_partition_start: |
242 | | * @self: a #FuEfiHardDriveDevicePath |
243 | | * |
244 | | * Gets the DP partition start. |
245 | | * |
246 | | * NOTE: This are multiples of the block size, which can be found using fu_volume_get_block_size() |
247 | | * |
248 | | * Returns: integer |
249 | | * |
250 | | * Since: 2.0.0 |
251 | | **/ |
252 | | guint64 |
253 | | fu_efi_hard_drive_device_path_get_partition_start(FuEfiHardDriveDevicePath *self) |
254 | 0 | { |
255 | 0 | g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(self), 0); |
256 | 0 | return self->partition_start; |
257 | 0 | } |
258 | | |
259 | | /** |
260 | | * fu_efi_hard_drive_device_path_get_partition_number: |
261 | | * @self: a #FuEfiHardDriveDevicePath |
262 | | * |
263 | | * Gets the DP partition number. |
264 | | * |
265 | | * Returns: integer |
266 | | * |
267 | | * Since: 2.0.0 |
268 | | **/ |
269 | | guint32 |
270 | | fu_efi_hard_drive_device_path_get_partition_number(FuEfiHardDriveDevicePath *self) |
271 | 0 | { |
272 | 0 | g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(self), 0); |
273 | 0 | return self->partition_number; |
274 | 0 | } |
275 | | |
276 | | /** |
277 | | * fu_efi_hard_drive_device_path_compare: |
278 | | * @dp1: a #FuEfiHardDriveDevicePath |
279 | | * @dp2: a #FuEfiHardDriveDevicePath |
280 | | * |
281 | | * Compares two EFI HardDrive `DEVICE_PATH`s. |
282 | | * |
283 | | * Returns: %TRUE is considered equal |
284 | | * |
285 | | * Since: 2.0.0 |
286 | | **/ |
287 | | gboolean |
288 | | fu_efi_hard_drive_device_path_compare(FuEfiHardDriveDevicePath *dp1, FuEfiHardDriveDevicePath *dp2) |
289 | 0 | { |
290 | 0 | g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(dp1), FALSE); |
291 | 0 | g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(dp2), FALSE); |
292 | | |
293 | 0 | if (dp1->partition_format != dp2->partition_format) |
294 | 0 | return FALSE; |
295 | 0 | if (dp1->signature_type != dp2->signature_type) |
296 | 0 | return FALSE; |
297 | 0 | if (memcmp(dp1->partition_signature, dp2->partition_signature, sizeof(fwupd_guid_t)) != 0) |
298 | 0 | return FALSE; |
299 | 0 | if (dp1->partition_number != dp2->partition_number) |
300 | 0 | return FALSE; |
301 | 0 | if (dp1->partition_start != dp2->partition_start) |
302 | 0 | return FALSE; |
303 | 0 | if (dp1->partition_size != dp2->partition_size) |
304 | 0 | return FALSE; |
305 | 0 | return TRUE; |
306 | 0 | } |
307 | | |
308 | | static void |
309 | | fu_efi_hard_drive_device_path_codec_iface_init(FwupdCodecInterface *iface) |
310 | 1 | { |
311 | 1 | iface->add_json = fu_efi_hard_drive_device_path_add_json; |
312 | 1 | } |
313 | | |
314 | | /** |
315 | | * fu_efi_hard_drive_device_path_new: |
316 | | * |
317 | | * Creates a new EFI `DEVICE_PATH`. |
318 | | * |
319 | | * Returns: (transfer full): a #FuEfiHardDriveDevicePath |
320 | | * |
321 | | * Since: 1.9.3 |
322 | | **/ |
323 | | FuEfiHardDriveDevicePath * |
324 | | fu_efi_hard_drive_device_path_new(void) |
325 | 645 | { |
326 | 645 | return g_object_new(FU_TYPE_EFI_HARD_DRIVE_DEVICE_PATH, NULL); |
327 | 645 | } |
328 | | |
329 | | /** |
330 | | * fu_efi_hard_drive_device_path_new_from_volume: |
331 | | * @volume: a #FuVolume |
332 | | * @error: (nullable): optional return location for an error |
333 | | * |
334 | | * Creates a new EFI `DEVICE_PATH` for a specific volume. |
335 | | * |
336 | | * Returns: (transfer full): a #FuEfiHardDriveDevicePath, or %NULL on error |
337 | | * |
338 | | * Since: 1.9.3 |
339 | | **/ |
340 | | FuEfiHardDriveDevicePath * |
341 | | fu_efi_hard_drive_device_path_new_from_volume(FuVolume *volume, GError **error) |
342 | 0 | { |
343 | 0 | guint16 block_size; |
344 | 0 | g_autoptr(FuEfiHardDriveDevicePath) self = fu_efi_hard_drive_device_path_new(); |
345 | 0 | g_autofree gchar *partition_kind = NULL; |
346 | 0 | g_autofree gchar *partition_uuid = NULL; |
347 | 0 | g_autoptr(GError) error_local = NULL; |
348 | |
|
349 | 0 | g_return_val_if_fail(FU_IS_VOLUME(volume), NULL); |
350 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
351 | | |
352 | | /* common to both */ |
353 | 0 | block_size = fu_volume_get_block_size(volume, &error_local); |
354 | 0 | if (block_size == 0) { |
355 | 0 | g_debug("failed to get volume block size, falling back to 0x%x: %s", |
356 | 0 | (guint)BLOCK_SIZE_FALLBACK, |
357 | 0 | error_local->message); |
358 | 0 | block_size = BLOCK_SIZE_FALLBACK; |
359 | 0 | } |
360 | 0 | self->partition_number = fu_volume_get_partition_number(volume); |
361 | 0 | self->partition_start = fu_volume_get_partition_offset(volume) / block_size; |
362 | 0 | self->partition_size = fu_volume_get_partition_size(volume) / block_size; |
363 | | |
364 | | /* set up the rest of the struct */ |
365 | 0 | partition_kind = fu_volume_get_partition_kind(volume); |
366 | 0 | if (partition_kind == NULL) { |
367 | 0 | g_set_error_literal(error, |
368 | 0 | FWUPD_ERROR, |
369 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
370 | 0 | "partition kind required"); |
371 | 0 | return NULL; |
372 | 0 | } |
373 | 0 | partition_uuid = fu_volume_get_partition_uuid(volume); |
374 | 0 | if (partition_uuid == NULL) { |
375 | 0 | g_set_error_literal(error, |
376 | 0 | FWUPD_ERROR, |
377 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
378 | 0 | "partition UUID required"); |
379 | 0 | return NULL; |
380 | 0 | } |
381 | 0 | if (g_strcmp0(partition_kind, FU_VOLUME_KIND_ESP) == 0 || |
382 | 0 | g_strcmp0(partition_kind, FU_VOLUME_KIND_BDP) == 0) { |
383 | 0 | self->partition_format = |
384 | 0 | FU_EFI_HARD_DRIVE_DEVICE_PATH_PARTITION_FORMAT_GUID_PARTITION_TABLE; |
385 | 0 | self->signature_type = FU_EFI_HARD_DRIVE_DEVICE_PATH_SIGNATURE_TYPE_GUID; |
386 | 0 | if (!fwupd_guid_from_string(partition_uuid, |
387 | 0 | &self->partition_signature, |
388 | 0 | FWUPD_GUID_FLAG_MIXED_ENDIAN, |
389 | 0 | error)) |
390 | 0 | return NULL; |
391 | 0 | } else if (g_strcmp0(partition_kind, "0xef") == 0) { |
392 | 0 | guint32 value = 0; |
393 | 0 | g_auto(GStrv) parts = g_strsplit(partition_uuid, "-", -1); |
394 | 0 | if (!fu_firmware_strparse_uint32_safe(parts[0], |
395 | 0 | strlen(parts[0]), |
396 | 0 | 0x0, |
397 | 0 | &value, |
398 | 0 | error)) { |
399 | 0 | g_prefix_error(error, "failed to parse %s: ", parts[0]); |
400 | 0 | return NULL; |
401 | 0 | } |
402 | 0 | if (!fu_memwrite_uint32_safe(self->partition_signature, |
403 | 0 | sizeof(self->partition_signature), |
404 | 0 | 0x0, |
405 | 0 | value, |
406 | 0 | G_LITTLE_ENDIAN, |
407 | 0 | error)) |
408 | 0 | return NULL; |
409 | 0 | self->partition_format = FU_EFI_HARD_DRIVE_DEVICE_PATH_PARTITION_FORMAT_LEGACY_MBR; |
410 | 0 | self->signature_type = FU_EFI_HARD_DRIVE_DEVICE_PATH_SIGNATURE_TYPE_ADDR1B8; |
411 | 0 | } else { |
412 | 0 | g_set_error(error, |
413 | 0 | FWUPD_ERROR, |
414 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
415 | 0 | "partition kind %s not supported", |
416 | 0 | partition_kind); |
417 | 0 | return NULL; |
418 | 0 | } |
419 | | |
420 | | /* success */ |
421 | 0 | return g_steal_pointer(&self); |
422 | 0 | } |