/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-common.h" |
13 | | #include "fu-efi-device-path-list.h" |
14 | | #include "fu-efi-file-path-device-path.h" |
15 | | #include "fu-efi-hard-drive-device-path.h" |
16 | | #include "fu-efi-struct.h" |
17 | | #include "fu-input-stream.h" |
18 | | |
19 | | struct _FuEfiDevicePathList { |
20 | | FuFirmware parent_instance; |
21 | | }; |
22 | | |
23 | | static void |
24 | | fu_efi_device_path_list_codec_iface_init(FwupdCodecInterface *iface); |
25 | | |
26 | 4.98k | G_DEFINE_TYPE_EXTENDED(FuEfiDevicePathList, |
27 | 4.98k | fu_efi_device_path_list, |
28 | 4.98k | FU_TYPE_FIRMWARE, |
29 | 4.98k | 0, |
30 | 4.98k | G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, |
31 | 4.98k | fu_efi_device_path_list_codec_iface_init)) |
32 | 4.98k | |
33 | 4.98k | #define FU_EFI_DEVICE_PATH_LIST_IMAGES_MAX 1000u |
34 | | |
35 | | static const gchar * |
36 | | fu_efi_device_path_list_gtype_to_member_name(GType gtype) |
37 | 0 | { |
38 | 0 | if (gtype == FU_TYPE_EFI_DEVICE_PATH) |
39 | 0 | return "Dp"; |
40 | 0 | if (gtype == FU_TYPE_EFI_FILE_PATH_DEVICE_PATH) |
41 | 0 | return "Fp"; |
42 | 0 | if (gtype == FU_TYPE_EFI_HARD_DRIVE_DEVICE_PATH) |
43 | 0 | return "Hd"; |
44 | 0 | return g_type_name(gtype); |
45 | 0 | } |
46 | | |
47 | | static void |
48 | | fu_efi_device_path_list_add_json(FwupdCodec *codec, |
49 | | FwupdJsonObject *json_obj, |
50 | | FwupdCodecFlags flags) |
51 | 0 | { |
52 | 0 | FuFirmware *firmware = FU_FIRMWARE(codec); |
53 | 0 | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
54 | 0 | g_autoptr(FwupdJsonArray) json_arr = fwupd_json_array_new(); |
55 | |
|
56 | 0 | for (guint i = 0; i < imgs->len; i++) { |
57 | 0 | FuFirmware *img = g_ptr_array_index(imgs, i); |
58 | 0 | g_autoptr(FwupdJsonObject) json_obj_tmp = fwupd_json_object_new(); |
59 | 0 | g_autoptr(FwupdJsonObject) json_object_tmp2 = fwupd_json_object_new(); |
60 | 0 | fwupd_codec_to_json(FWUPD_CODEC(img), json_object_tmp2, flags); |
61 | 0 | fwupd_json_object_add_object( |
62 | 0 | json_obj_tmp, |
63 | 0 | fu_efi_device_path_list_gtype_to_member_name(G_OBJECT_TYPE(img)), |
64 | 0 | json_object_tmp2); |
65 | 0 | fwupd_json_array_add_object(json_arr, json_obj_tmp); |
66 | 0 | } |
67 | 0 | fwupd_json_object_add_array(json_obj, "DPs", json_arr); |
68 | 0 | } |
69 | | |
70 | | static gboolean |
71 | | fu_efi_device_path_list_parse(FuFirmware *firmware, |
72 | | GInputStream *stream, |
73 | | FuFirmwareParseFlags flags, |
74 | | GError **error) |
75 | 1.76k | { |
76 | 1.76k | gsize offset = 0; |
77 | 1.76k | gsize streamsz = 0; |
78 | 1.76k | if (!fu_input_stream_size(stream, &streamsz, error)) |
79 | 0 | return FALSE; |
80 | 23.8k | while (offset < streamsz) { |
81 | 23.4k | g_autoptr(FuEfiDevicePath) efi_dp = NULL; |
82 | 23.4k | g_autoptr(FuStructEfiDevicePath) st_dp = NULL; |
83 | | |
84 | | /* parse the header so we can work out what GType to create */ |
85 | 23.4k | st_dp = fu_struct_efi_device_path_parse_stream(stream, offset, error); |
86 | 23.4k | if (st_dp == NULL) |
87 | 37 | return FALSE; |
88 | 23.3k | if (fu_struct_efi_device_path_get_type(st_dp) == FU_EFI_DEVICE_PATH_TYPE_END) |
89 | 1.08k | break; |
90 | 22.2k | if (fu_struct_efi_device_path_get_type(st_dp) == FU_EFI_DEVICE_PATH_TYPE_MEDIA && |
91 | 17.9k | fu_struct_efi_device_path_get_subtype(st_dp) == |
92 | 17.9k | FU_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE_FILE_PATH) { |
93 | 16.6k | efi_dp = FU_EFI_DEVICE_PATH(fu_efi_file_path_device_path_new()); |
94 | 16.6k | } else if (fu_struct_efi_device_path_get_type(st_dp) == |
95 | 5.60k | FU_EFI_DEVICE_PATH_TYPE_MEDIA && |
96 | 1.28k | fu_struct_efi_device_path_get_subtype(st_dp) == |
97 | 1.28k | FU_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE_HARD_DRIVE) { |
98 | 659 | efi_dp = FU_EFI_DEVICE_PATH(fu_efi_hard_drive_device_path_new()); |
99 | 4.94k | } else { |
100 | 4.94k | efi_dp = fu_efi_device_path_new(); |
101 | 4.94k | } |
102 | 22.2k | fu_firmware_set_offset(FU_FIRMWARE(efi_dp), offset); |
103 | 22.2k | if (!fu_firmware_parse_stream(FU_FIRMWARE(efi_dp), stream, offset, flags, error)) |
104 | 234 | return FALSE; |
105 | 22.0k | if (fu_firmware_get_size(FU_FIRMWARE(efi_dp)) == 0) { |
106 | 0 | g_set_error_literal(error, |
107 | 0 | FWUPD_ERROR, |
108 | 0 | FWUPD_ERROR_INVALID_DATA, |
109 | 0 | "DP section had zero size"); |
110 | 0 | return FALSE; |
111 | 0 | } |
112 | 22.0k | if (!fu_firmware_add_image(firmware, FU_FIRMWARE(efi_dp), error)) |
113 | 3 | return FALSE; |
114 | 22.0k | if (!fu_size_checked_inc(&offset, fu_firmware_get_size(FU_FIRMWARE(efi_dp)), error)) |
115 | 0 | return FALSE; |
116 | 22.0k | } |
117 | | |
118 | | /* success */ |
119 | 1.48k | return TRUE; |
120 | 1.76k | } |
121 | | |
122 | | static GByteArray * |
123 | | fu_efi_device_path_list_write(FuFirmware *firmware, GError **error) |
124 | 979 | { |
125 | 979 | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
126 | 979 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
127 | 979 | g_autoptr(FuStructEfiDevicePath) st_dp_end = NULL; |
128 | | |
129 | | /* add each image */ |
130 | 7.16k | for (guint i = 0; i < imgs->len; i++) { |
131 | 6.48k | FuFirmware *img = g_ptr_array_index(imgs, i); |
132 | 6.48k | g_autoptr(GBytes) dp_blob = fu_firmware_write(img, error); |
133 | 6.48k | if (dp_blob == NULL) |
134 | 298 | return NULL; |
135 | 6.18k | fu_byte_array_append_bytes(buf, dp_blob); |
136 | 6.18k | } |
137 | | |
138 | | /* add end marker */ |
139 | 681 | st_dp_end = fu_struct_efi_device_path_new(); |
140 | 681 | fu_struct_efi_device_path_set_type(st_dp_end, FU_EFI_DEVICE_PATH_TYPE_END); |
141 | 681 | fu_struct_efi_device_path_set_subtype(st_dp_end, 0xFF); |
142 | 681 | fu_byte_array_append_array(buf, st_dp_end->buf); |
143 | | |
144 | | /* success */ |
145 | 681 | return g_steal_pointer(&buf); |
146 | 979 | } |
147 | | |
148 | | static void |
149 | | fu_efi_device_path_list_codec_iface_init(FwupdCodecInterface *iface) |
150 | 1 | { |
151 | 1 | iface->add_json = fu_efi_device_path_list_add_json; |
152 | 1 | } |
153 | | |
154 | | static void |
155 | | fu_efi_device_path_list_class_init(FuEfiDevicePathListClass *klass) |
156 | 1 | { |
157 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
158 | 1 | firmware_class->parse = fu_efi_device_path_list_parse; |
159 | 1 | firmware_class->write = fu_efi_device_path_list_write; |
160 | 1 | } |
161 | | |
162 | | static void |
163 | | fu_efi_device_path_list_init(FuEfiDevicePathList *self) |
164 | 2.00k | { |
165 | 2.00k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_DEVICE_PATH); |
166 | 2.00k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_FILE_PATH_DEVICE_PATH); |
167 | 2.00k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_HARD_DRIVE_DEVICE_PATH); |
168 | 2.00k | fu_firmware_set_images_max(FU_FIRMWARE(self), FU_EFI_DEVICE_PATH_LIST_IMAGES_MAX); |
169 | 2.00k | fu_firmware_set_size_max(FU_FIRMWARE(self), 1 * FU_MB); |
170 | 2.00k | } |
171 | | |
172 | | /** |
173 | | * fu_efi_device_path_list_new: |
174 | | * |
175 | | * Creates a new EFI DEVICE_PATH list. |
176 | | * |
177 | | * Returns: (transfer full): a #FuEfiDevicePathList |
178 | | * |
179 | | * Since: 1.9.3 |
180 | | **/ |
181 | | FuEfiDevicePathList * |
182 | | fu_efi_device_path_list_new(void) |
183 | 2.00k | { |
184 | 2.00k | return g_object_new(FU_TYPE_EFI_DEVICE_PATH_LIST, NULL); |
185 | 2.00k | } |