Coverage Report

Created: 2025-07-18 06:26

/src/fwupd/libfwupdplugin/fu-efi-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
1.39k
#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.h"
14
#include "fu-efi-struct.h"
15
#include "fu-input-stream.h"
16
17
/**
18
 * FuEfiDevicePath:
19
 *
20
 * See also: [class@FuFirmware]
21
 */
22
23
typedef struct {
24
  guint8 subtype;
25
} FuEfiDevicePathPrivate;
26
27
static void
28
fu_efi_device_path_codec_iface_init(FwupdCodecInterface *iface);
29
30
G_DEFINE_TYPE_EXTENDED(FuEfiDevicePath,
31
           fu_efi_device_path,
32
           FU_TYPE_FIRMWARE,
33
           0,
34
           G_ADD_PRIVATE(FuEfiDevicePath)
35
         G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC,
36
             fu_efi_device_path_codec_iface_init))
37
35.2k
#define GET_PRIVATE(o) (fu_efi_device_path_get_instance_private(o))
38
39
static void
40
fu_efi_device_path_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
41
0
{
42
0
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware);
43
0
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
44
0
  fu_xmlb_builder_insert_kx(bn, "subtype", priv->subtype);
45
0
}
46
47
static void
48
fu_efi_device_path_add_json(FwupdCodec *codec, JsonBuilder *builder, FwupdCodecFlags flags)
49
0
{
50
0
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(codec);
51
0
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
52
0
  fwupd_codec_json_append_int(builder, "Subtype", priv->subtype);
53
0
}
54
55
/**
56
 * fu_efi_device_path_get_subtype:
57
 * @self: a #FuEfiDevicePath
58
 *
59
 * Gets the DEVICE_PATH subtype.
60
 *
61
 * Returns: integer
62
 *
63
 * Since: 1.9.3
64
 **/
65
guint8
66
fu_efi_device_path_get_subtype(FuEfiDevicePath *self)
67
0
{
68
0
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
69
0
  g_return_val_if_fail(FU_IS_EFI_DEVICE_PATH(self), 0x0);
70
0
  return priv->subtype;
71
0
}
72
73
/**
74
 * fu_efi_device_path_set_subtype:
75
 * @self: a #FuEfiDevicePath
76
 * @subtype: integer
77
 *
78
 * Sets the DEVICE_PATH subtype.
79
 *
80
 * Since: 1.9.3
81
 **/
82
void
83
fu_efi_device_path_set_subtype(FuEfiDevicePath *self, guint8 subtype)
84
2.94k
{
85
2.94k
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
86
2.94k
  g_return_if_fail(FU_IS_EFI_DEVICE_PATH(self));
87
2.94k
  priv->subtype = subtype;
88
2.94k
}
89
90
static gboolean
91
fu_efi_device_path_parse(FuFirmware *firmware,
92
       GInputStream *stream,
93
       FuFirmwareParseFlags flags,
94
       GError **error)
95
25.0k
{
96
25.0k
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware);
97
25.0k
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
98
25.0k
  gsize dp_length;
99
25.0k
  gsize streamsz = 0;
100
25.0k
  g_autoptr(GByteArray) st = NULL;
101
102
  /* parse */
103
25.0k
  st = fu_struct_efi_device_path_parse_stream(stream, 0x0, error);
104
25.0k
  if (st == NULL)
105
0
    return FALSE;
106
25.0k
  if (fu_struct_efi_device_path_get_length(st) < FU_STRUCT_EFI_DEVICE_PATH_SIZE) {
107
24
    g_set_error(error,
108
24
          FWUPD_ERROR,
109
24
          FWUPD_ERROR_INVALID_DATA,
110
24
          "EFI DEVICE_PATH length invalid: 0x%x",
111
24
          fu_struct_efi_device_path_get_length(st));
112
24
    return FALSE;
113
24
  }
114
25.0k
  fu_firmware_set_idx(firmware, fu_struct_efi_device_path_get_type(st));
115
25.0k
  priv->subtype = fu_struct_efi_device_path_get_subtype(st);
116
117
  /* work around a efiboot bug */
118
25.0k
  if (!fu_input_stream_size(stream, &streamsz, error))
119
0
    return FALSE;
120
25.0k
  dp_length = fu_struct_efi_device_path_get_length(st);
121
25.0k
  if (streamsz > 4 && dp_length > streamsz) {
122
1.39k
    dp_length = streamsz - 0x4;
123
1.39k
    g_debug("fixing up DP length from 0x%x to 0x%x, because of a bug in efiboot",
124
1.39k
      fu_struct_efi_device_path_get_length(st),
125
1.39k
      (guint)dp_length);
126
1.39k
  }
127
25.0k
  if (dp_length > st->len) {
128
10.2k
    g_autoptr(GBytes) payload =
129
10.2k
        fu_input_stream_read_bytes(stream, st->len, dp_length - st->len, NULL, error);
130
10.2k
    if (payload == NULL)
131
126
      return FALSE;
132
10.1k
    fu_firmware_set_bytes(firmware, payload);
133
10.1k
  }
134
24.8k
  fu_firmware_set_size(firmware, dp_length);
135
136
  /* success */
137
24.8k
  return TRUE;
138
25.0k
}
139
140
static GByteArray *
141
fu_efi_device_path_write(FuFirmware *firmware, GError **error)
142
7.29k
{
143
7.29k
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware);
144
7.29k
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
145
7.29k
  g_autoptr(GByteArray) st = fu_struct_efi_device_path_new();
146
7.29k
  g_autoptr(GBytes) payload = NULL;
147
148
  /* required */
149
7.29k
  payload = fu_firmware_get_bytes(firmware, error);
150
7.29k
  if (payload == NULL)
151
444
    return NULL;
152
6.84k
  fu_struct_efi_device_path_set_type(st, fu_firmware_get_idx(firmware));
153
6.84k
  fu_struct_efi_device_path_set_subtype(st, priv->subtype);
154
6.84k
  fu_struct_efi_device_path_set_length(st, st->len + g_bytes_get_size(payload));
155
6.84k
  fu_byte_array_append_bytes(st, payload);
156
157
  /* success */
158
6.84k
  return g_steal_pointer(&st);
159
7.29k
}
160
161
static gboolean
162
fu_efi_device_path_build(FuFirmware *firmware, XbNode *n, GError **error)
163
0
{
164
0
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware);
165
0
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
166
0
  guint64 tmp;
167
168
  /* optional properties */
169
0
  tmp = xb_node_query_text_as_uint(n, "subtype", NULL);
170
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8)
171
0
    priv->subtype = tmp;
172
173
  /* success */
174
0
  return TRUE;
175
0
}
176
177
static void
178
fu_efi_device_path_codec_iface_init(FwupdCodecInterface *iface)
179
1
{
180
1
  iface->add_json = fu_efi_device_path_add_json;
181
1
}
182
183
static void
184
fu_efi_device_path_init(FuEfiDevicePath *self)
185
25.6k
{
186
25.6k
}
187
188
static void
189
fu_efi_device_path_class_init(FuEfiDevicePathClass *klass)
190
1
{
191
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
192
1
  firmware_class->export = fu_efi_device_path_export;
193
1
  firmware_class->parse = fu_efi_device_path_parse;
194
1
  firmware_class->write = fu_efi_device_path_write;
195
1
  firmware_class->build = fu_efi_device_path_build;
196
1
}
197
198
/**
199
 * fu_efi_device_path_new:
200
 *
201
 * Creates a new EFI DEVICE_PATH
202
 *
203
 * Since: 1.9.3
204
 **/
205
FuEfiDevicePath *
206
fu_efi_device_path_new(void)
207
22.7k
{
208
22.7k
  return g_object_new(FU_TYPE_EFI_DEVICE_PATH, NULL);
209
22.7k
}