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