/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 | 4.91k | G_DEFINE_TYPE_EXTENDED(FuEfiDevicePathList, |
26 | 4.91k | fu_efi_device_path_list, |
27 | 4.91k | FU_TYPE_FIRMWARE, |
28 | 4.91k | 0, |
29 | 4.91k | G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, |
30 | 4.91k | fu_efi_device_path_list_codec_iface_init)) |
31 | 4.91k | |
32 | 4.91k | #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, |
48 | | FwupdJsonObject *json_obj, |
49 | | FwupdCodecFlags flags) |
50 | 0 | { |
51 | 0 | FuFirmware *firmware = FU_FIRMWARE(codec); |
52 | 0 | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
53 | 0 | g_autoptr(FwupdJsonArray) json_arr = fwupd_json_array_new(); |
54 | |
|
55 | 0 | for (guint i = 0; i < imgs->len; i++) { |
56 | 0 | FuFirmware *img = g_ptr_array_index(imgs, i); |
57 | 0 | g_autoptr(FwupdJsonObject) json_obj_tmp = fwupd_json_object_new(); |
58 | 0 | g_autoptr(FwupdJsonObject) json_object_tmp2 = fwupd_json_object_new(); |
59 | 0 | fwupd_codec_to_json(FWUPD_CODEC(img), json_object_tmp2, flags); |
60 | 0 | fwupd_json_object_add_object( |
61 | 0 | json_obj_tmp, |
62 | 0 | fu_efi_device_path_list_gtype_to_member_name(G_OBJECT_TYPE(img)), |
63 | 0 | json_object_tmp2); |
64 | 0 | fwupd_json_array_add_object(json_arr, json_obj_tmp); |
65 | 0 | } |
66 | 0 | fwupd_json_object_add_array(json_obj, "DPs", json_arr); |
67 | 0 | } |
68 | | |
69 | | static gboolean |
70 | | fu_efi_device_path_list_parse(FuFirmware *firmware, |
71 | | GInputStream *stream, |
72 | | FuFirmwareParseFlags flags, |
73 | | GError **error) |
74 | 1.70k | { |
75 | 1.70k | gsize offset = 0; |
76 | 1.70k | gsize streamsz = 0; |
77 | 1.70k | if (!fu_input_stream_size(stream, &streamsz, error)) |
78 | 0 | return FALSE; |
79 | 24.8k | while (offset < streamsz) { |
80 | 24.5k | g_autoptr(FuEfiDevicePath) efi_dp = NULL; |
81 | 24.5k | g_autoptr(FuStructEfiDevicePath) st_dp = NULL; |
82 | | |
83 | | /* parse the header so we can work out what GType to create */ |
84 | 24.5k | st_dp = fu_struct_efi_device_path_parse_stream(stream, offset, error); |
85 | 24.5k | if (st_dp == NULL) |
86 | 44 | return FALSE; |
87 | 24.5k | if (fu_struct_efi_device_path_get_type(st_dp) == FU_EFI_DEVICE_PATH_TYPE_END) |
88 | 1.14k | break; |
89 | 23.3k | if (fu_struct_efi_device_path_get_type(st_dp) == FU_EFI_DEVICE_PATH_TYPE_MEDIA && |
90 | 19.2k | fu_struct_efi_device_path_get_subtype(st_dp) == |
91 | 19.2k | FU_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE_FILE_PATH) { |
92 | 17.7k | efi_dp = FU_EFI_DEVICE_PATH(fu_efi_file_path_device_path_new()); |
93 | 17.7k | } else if (fu_struct_efi_device_path_get_type(st_dp) == |
94 | 5.60k | FU_EFI_DEVICE_PATH_TYPE_MEDIA && |
95 | 1.43k | fu_struct_efi_device_path_get_subtype(st_dp) == |
96 | 1.43k | FU_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE_HARD_DRIVE) { |
97 | 652 | efi_dp = FU_EFI_DEVICE_PATH(fu_efi_hard_drive_device_path_new()); |
98 | 4.95k | } else { |
99 | 4.95k | efi_dp = fu_efi_device_path_new(); |
100 | 4.95k | } |
101 | 23.3k | fu_firmware_set_offset(FU_FIRMWARE(efi_dp), offset); |
102 | 23.3k | if (!fu_firmware_parse_stream(FU_FIRMWARE(efi_dp), stream, offset, flags, error)) |
103 | 213 | return FALSE; |
104 | 23.1k | if (!fu_firmware_add_image(firmware, FU_FIRMWARE(efi_dp), error)) |
105 | 2 | return FALSE; |
106 | 23.1k | offset += fu_firmware_get_size(FU_FIRMWARE(efi_dp)); |
107 | 23.1k | } |
108 | | |
109 | | /* success */ |
110 | 1.45k | return TRUE; |
111 | 1.70k | } |
112 | | |
113 | | static GByteArray * |
114 | | fu_efi_device_path_list_write(FuFirmware *firmware, GError **error) |
115 | 982 | { |
116 | 982 | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
117 | 982 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
118 | 982 | g_autoptr(FuStructEfiDevicePath) st_dp_end = NULL; |
119 | | |
120 | | /* add each image */ |
121 | 6.16k | for (guint i = 0; i < imgs->len; i++) { |
122 | 5.43k | FuFirmware *img = g_ptr_array_index(imgs, i); |
123 | 5.43k | g_autoptr(GBytes) dp_blob = fu_firmware_write(img, error); |
124 | 5.43k | if (dp_blob == NULL) |
125 | 245 | return NULL; |
126 | 5.18k | fu_byte_array_append_bytes(buf, dp_blob); |
127 | 5.18k | } |
128 | | |
129 | | /* add end marker */ |
130 | 737 | st_dp_end = fu_struct_efi_device_path_new(); |
131 | 737 | fu_struct_efi_device_path_set_type(st_dp_end, FU_EFI_DEVICE_PATH_TYPE_END); |
132 | 737 | fu_struct_efi_device_path_set_subtype(st_dp_end, 0xFF); |
133 | 737 | fu_byte_array_append_array(buf, st_dp_end->buf); |
134 | | |
135 | | /* success */ |
136 | 737 | return g_steal_pointer(&buf); |
137 | 982 | } |
138 | | |
139 | | static void |
140 | | fu_efi_device_path_list_codec_iface_init(FwupdCodecInterface *iface) |
141 | 1 | { |
142 | 1 | iface->add_json = fu_efi_device_path_list_add_json; |
143 | 1 | } |
144 | | |
145 | | static void |
146 | | fu_efi_device_path_list_class_init(FuEfiDevicePathListClass *klass) |
147 | 1 | { |
148 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
149 | 1 | firmware_class->parse = fu_efi_device_path_list_parse; |
150 | 1 | firmware_class->write = fu_efi_device_path_list_write; |
151 | 1 | } |
152 | | |
153 | | static void |
154 | | fu_efi_device_path_list_init(FuEfiDevicePathList *self) |
155 | 1.96k | { |
156 | 1.96k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_DEVICE_PATH); |
157 | 1.96k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_FILE_PATH_DEVICE_PATH); |
158 | 1.96k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_HARD_DRIVE_DEVICE_PATH); |
159 | 1.96k | fu_firmware_set_images_max(FU_FIRMWARE(self), FU_EFI_DEVICE_PATH_LIST_IMAGES_MAX); |
160 | 1.96k | } |
161 | | |
162 | | /** |
163 | | * fu_efi_device_path_list_new: |
164 | | * |
165 | | * Creates a new EFI DEVICE_PATH list. |
166 | | * |
167 | | * Returns: (transfer full): a #FuEfiDevicePathList |
168 | | * |
169 | | * Since: 1.9.3 |
170 | | **/ |
171 | | FuEfiDevicePathList * |
172 | | fu_efi_device_path_list_new(void) |
173 | 1.96k | { |
174 | 1.96k | return g_object_new(FU_TYPE_EFI_DEVICE_PATH_LIST, NULL); |
175 | 1.96k | } |