/src/fwupd/libfwupdplugin/fu-efi-device-path-list.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 | | #define G_LOG_DOMAIN "FuEfiDevicePath" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-byte-array.h" |
12 | | #include "fu-efi-device-path-list.h" |
13 | | #include "fu-efi-file-path-device-path.h" |
14 | | #include "fu-efi-hard-drive-device-path.h" |
15 | | #include "fu-efi-struct.h" |
16 | | #include "fu-input-stream.h" |
17 | | |
18 | | struct _FuEfiDevicePathList { |
19 | | FuFirmware parent_instance; |
20 | | }; |
21 | | |
22 | | static void |
23 | | fu_efi_device_path_list_codec_iface_init(FwupdCodecInterface *iface); |
24 | | |
25 | 5.32k | G_DEFINE_TYPE_EXTENDED(FuEfiDevicePathList, |
26 | 5.32k | fu_efi_device_path_list, |
27 | 5.32k | FU_TYPE_FIRMWARE, |
28 | 5.32k | 0, |
29 | 5.32k | G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, |
30 | 5.32k | fu_efi_device_path_list_codec_iface_init)) |
31 | 5.32k | |
32 | 5.32k | #define FU_EFI_DEVICE_PATH_LIST_IMAGES_MAX 1000u |
33 | | |
34 | | static const gchar * |
35 | | fu_efi_device_path_list_gtype_to_member_name(GType gtype) |
36 | 0 | { |
37 | 0 | if (gtype == FU_TYPE_EFI_DEVICE_PATH) |
38 | 0 | return "Dp"; |
39 | 0 | if (gtype == FU_TYPE_EFI_FILE_PATH_DEVICE_PATH) |
40 | 0 | return "Fp"; |
41 | 0 | if (gtype == FU_TYPE_EFI_HARD_DRIVE_DEVICE_PATH) |
42 | 0 | return "Hd"; |
43 | 0 | return g_type_name(gtype); |
44 | 0 | } |
45 | | |
46 | | static void |
47 | | fu_efi_device_path_list_add_json(FwupdCodec *codec, JsonBuilder *builder, FwupdCodecFlags flags) |
48 | 0 | { |
49 | 0 | FuFirmware *firmware = FU_FIRMWARE(codec); |
50 | 0 | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
51 | |
|
52 | 0 | json_builder_set_member_name(builder, "DPs"); |
53 | 0 | json_builder_begin_array(builder); |
54 | 0 | for (guint i = 0; i < imgs->len; i++) { |
55 | 0 | FuFirmware *img = g_ptr_array_index(imgs, i); |
56 | 0 | json_builder_begin_object(builder); |
57 | 0 | json_builder_set_member_name( |
58 | 0 | builder, |
59 | 0 | fu_efi_device_path_list_gtype_to_member_name(G_OBJECT_TYPE(img))); |
60 | 0 | json_builder_begin_object(builder); |
61 | 0 | fwupd_codec_to_json(FWUPD_CODEC(img), builder, flags); |
62 | 0 | json_builder_end_object(builder); |
63 | 0 | json_builder_end_object(builder); |
64 | 0 | } |
65 | 0 | json_builder_end_array(builder); |
66 | 0 | } |
67 | | |
68 | | static gboolean |
69 | | fu_efi_device_path_list_parse(FuFirmware *firmware, |
70 | | GInputStream *stream, |
71 | | FuFirmwareParseFlags flags, |
72 | | GError **error) |
73 | 1.83k | { |
74 | 1.83k | gsize offset = 0; |
75 | 1.83k | gsize streamsz = 0; |
76 | 1.83k | if (!fu_input_stream_size(stream, &streamsz, error)) |
77 | 0 | return FALSE; |
78 | 26.1k | while (offset < streamsz) { |
79 | 25.5k | g_autoptr(FuEfiDevicePath) efi_dp = NULL; |
80 | 25.5k | g_autoptr(FuStructEfiDevicePath) st_dp = NULL; |
81 | | |
82 | | /* parse the header so we can work out what GType to create */ |
83 | 25.5k | st_dp = fu_struct_efi_device_path_parse_stream(stream, offset, error); |
84 | 25.5k | if (st_dp == NULL) |
85 | 41 | return FALSE; |
86 | 25.4k | if (fu_struct_efi_device_path_get_type(st_dp) == FU_EFI_DEVICE_PATH_TYPE_END) |
87 | 1.00k | break; |
88 | 24.4k | if (fu_struct_efi_device_path_get_type(st_dp) == FU_EFI_DEVICE_PATH_TYPE_MEDIA && |
89 | 12.6k | fu_struct_efi_device_path_get_subtype(st_dp) == |
90 | 12.6k | FU_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE_FILE_PATH) { |
91 | 3.33k | efi_dp = FU_EFI_DEVICE_PATH(fu_efi_file_path_device_path_new()); |
92 | 21.1k | } else if (fu_struct_efi_device_path_get_type(st_dp) == |
93 | 21.1k | FU_EFI_DEVICE_PATH_TYPE_MEDIA && |
94 | 9.33k | fu_struct_efi_device_path_get_subtype(st_dp) == |
95 | 9.33k | FU_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE_HARD_DRIVE) { |
96 | 654 | efi_dp = FU_EFI_DEVICE_PATH(fu_efi_hard_drive_device_path_new()); |
97 | 20.4k | } else { |
98 | 20.4k | efi_dp = fu_efi_device_path_new(); |
99 | 20.4k | } |
100 | 24.4k | fu_firmware_set_offset(FU_FIRMWARE(efi_dp), offset); |
101 | 24.4k | if (!fu_firmware_parse_stream(FU_FIRMWARE(efi_dp), stream, offset, flags, error)) |
102 | 209 | return FALSE; |
103 | 24.2k | if (!fu_firmware_add_image(firmware, FU_FIRMWARE(efi_dp), error)) |
104 | 2 | return FALSE; |
105 | 24.2k | offset += fu_firmware_get_size(FU_FIRMWARE(efi_dp)); |
106 | 24.2k | } |
107 | | |
108 | | /* success */ |
109 | 1.58k | return TRUE; |
110 | 1.83k | } |
111 | | |
112 | | static GByteArray * |
113 | | fu_efi_device_path_list_write(FuFirmware *firmware, GError **error) |
114 | 1.16k | { |
115 | 1.16k | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
116 | 1.16k | g_autoptr(GByteArray) buf = g_byte_array_new(); |
117 | 1.16k | g_autoptr(FuStructEfiDevicePath) st_dp_end = NULL; |
118 | | |
119 | | /* add each image */ |
120 | 8.33k | for (guint i = 0; i < imgs->len; i++) { |
121 | 7.55k | FuFirmware *img = g_ptr_array_index(imgs, i); |
122 | 7.55k | g_autoptr(GBytes) dp_blob = fu_firmware_write(img, error); |
123 | 7.55k | if (dp_blob == NULL) |
124 | 389 | return NULL; |
125 | 7.16k | fu_byte_array_append_bytes(buf, dp_blob); |
126 | 7.16k | } |
127 | | |
128 | | /* add end marker */ |
129 | 778 | st_dp_end = fu_struct_efi_device_path_new(); |
130 | 778 | fu_struct_efi_device_path_set_type(st_dp_end, FU_EFI_DEVICE_PATH_TYPE_END); |
131 | 778 | fu_struct_efi_device_path_set_subtype(st_dp_end, 0xFF); |
132 | 778 | fu_byte_array_append_array(buf, st_dp_end->buf); |
133 | | |
134 | | /* success */ |
135 | 778 | return g_steal_pointer(&buf); |
136 | 1.16k | } |
137 | | |
138 | | static void |
139 | | fu_efi_device_path_list_codec_iface_init(FwupdCodecInterface *iface) |
140 | 1 | { |
141 | 1 | iface->add_json = fu_efi_device_path_list_add_json; |
142 | 1 | } |
143 | | |
144 | | static void |
145 | | fu_efi_device_path_list_class_init(FuEfiDevicePathListClass *klass) |
146 | 1 | { |
147 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
148 | 1 | firmware_class->parse = fu_efi_device_path_list_parse; |
149 | 1 | firmware_class->write = fu_efi_device_path_list_write; |
150 | 1 | } |
151 | | |
152 | | static void |
153 | | fu_efi_device_path_list_init(FuEfiDevicePathList *self) |
154 | 2.07k | { |
155 | 2.07k | g_type_ensure(FU_TYPE_EFI_FILE_PATH_DEVICE_PATH); |
156 | 2.07k | g_type_ensure(FU_TYPE_EFI_HARD_DRIVE_DEVICE_PATH); |
157 | 2.07k | fu_firmware_set_images_max(FU_FIRMWARE(self), FU_EFI_DEVICE_PATH_LIST_IMAGES_MAX); |
158 | 2.07k | } |
159 | | |
160 | | /** |
161 | | * fu_efi_device_path_list_new: |
162 | | * |
163 | | * Creates a new EFI DEVICE_PATH list. |
164 | | * |
165 | | * Returns: (transfer full): a #FuEfiDevicePathList |
166 | | * |
167 | | * Since: 1.9.3 |
168 | | **/ |
169 | | FuEfiDevicePathList * |
170 | | fu_efi_device_path_list_new(void) |
171 | 2.07k | { |
172 | 2.07k | return g_object_new(FU_TYPE_EFI_DEVICE_PATH_LIST, NULL); |
173 | 2.07k | } |