Coverage Report

Created: 2026-03-11 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-efi-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
386
#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
89.9k
G_DEFINE_TYPE_EXTENDED(FuEfiDevicePath,
31
89.9k
           fu_efi_device_path,
32
89.9k
           FU_TYPE_FIRMWARE,
33
89.9k
           0,
34
89.9k
           G_ADD_PRIVATE(FuEfiDevicePath)
35
89.9k
         G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC,
36
89.9k
             fu_efi_device_path_codec_iface_init))
37
89.9k
#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, FwupdJsonObject *json_obj, FwupdCodecFlags flags)
49
0
{
50
0
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(codec);
51
0
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
52
0
  fwupd_json_object_add_integer(json_obj, "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
18.4k
{
85
18.4k
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
86
18.4k
  g_return_if_fail(FU_IS_EFI_DEVICE_PATH(self));
87
18.4k
  priv->subtype = subtype;
88
18.4k
}
89
90
static gboolean
91
fu_efi_device_path_parse(FuFirmware *firmware,
92
       GInputStream *stream,
93
       FuFirmwareParseFlags flags,
94
       GError **error)
95
22.7k
{
96
22.7k
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware);
97
22.7k
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
98
22.7k
  gsize dp_length;
99
22.7k
  gsize streamsz = 0;
100
22.7k
  g_autoptr(FuStructEfiDevicePath) st = NULL;
101
102
  /* parse */
103
22.7k
  st = fu_struct_efi_device_path_parse_stream(stream, 0x0, error);
104
22.7k
  if (st == NULL)
105
0
    return FALSE;
106
22.7k
  if (fu_struct_efi_device_path_get_length(st) < FU_STRUCT_EFI_DEVICE_PATH_SIZE) {
107
40
    g_set_error(error,
108
40
          FWUPD_ERROR,
109
40
          FWUPD_ERROR_INVALID_DATA,
110
40
          "EFI DEVICE_PATH length invalid: 0x%x",
111
40
          fu_struct_efi_device_path_get_length(st));
112
40
    return FALSE;
113
40
  }
114
22.6k
  fu_firmware_set_idx(firmware, fu_struct_efi_device_path_get_type(st));
115
22.6k
  priv->subtype = fu_struct_efi_device_path_get_subtype(st);
116
117
  /* work around a efiboot bug */
118
22.6k
  if (!fu_input_stream_size(stream, &streamsz, error))
119
0
    return FALSE;
120
22.6k
  dp_length = fu_struct_efi_device_path_get_length(st);
121
22.6k
  if (streamsz > 4 && dp_length > streamsz) {
122
386
    dp_length = streamsz - 0x4;
123
386
    g_debug("fixing up DP length from 0x%x to 0x%x, because of a bug in efiboot",
124
386
      fu_struct_efi_device_path_get_length(st),
125
386
      (guint)dp_length);
126
386
  }
127
22.6k
  if (dp_length > st->buf->len) {
128
6.82k
    g_autoptr(GBytes) payload = NULL;
129
6.82k
    payload = fu_input_stream_read_bytes(stream,
130
6.82k
                 st->buf->len,
131
6.82k
                 dp_length - st->buf->len,
132
6.82k
                 NULL,
133
6.82k
                 error);
134
6.82k
    if (payload == NULL)
135
153
      return FALSE;
136
6.67k
    fu_firmware_set_bytes(firmware, payload);
137
6.67k
  }
138
22.5k
  fu_firmware_set_size(firmware, dp_length);
139
140
  /* success */
141
22.5k
  return TRUE;
142
22.6k
}
143
144
static GByteArray *
145
fu_efi_device_path_write(FuFirmware *firmware, GError **error)
146
5.04k
{
147
5.04k
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware);
148
5.04k
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
149
5.04k
  g_autoptr(FuStructEfiDevicePath) st = fu_struct_efi_device_path_new();
150
5.04k
  g_autoptr(GBytes) payload = NULL;
151
152
  /* required */
153
5.04k
  payload = fu_firmware_get_bytes(firmware, error);
154
5.04k
  if (payload == NULL)
155
245
    return NULL;
156
4.80k
  fu_struct_efi_device_path_set_type(st, fu_firmware_get_idx(firmware));
157
4.80k
  fu_struct_efi_device_path_set_subtype(st, priv->subtype);
158
4.80k
  fu_struct_efi_device_path_set_length(st, st->buf->len + g_bytes_get_size(payload));
159
4.80k
  fu_byte_array_append_bytes(st->buf, payload);
160
161
  /* success */
162
4.80k
  return g_steal_pointer(&st->buf);
163
5.04k
}
164
165
static gboolean
166
fu_efi_device_path_build(FuFirmware *firmware, XbNode *n, GError **error)
167
0
{
168
0
  FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware);
169
0
  FuEfiDevicePathPrivate *priv = GET_PRIVATE(self);
170
0
  guint64 tmp;
171
172
  /* optional properties */
173
0
  tmp = xb_node_query_text_as_uint(n, "subtype", NULL);
174
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8)
175
0
    priv->subtype = tmp;
176
177
  /* success */
178
0
  return TRUE;
179
0
}
180
181
static void
182
fu_efi_device_path_codec_iface_init(FwupdCodecInterface *iface)
183
1
{
184
1
  iface->add_json = fu_efi_device_path_add_json;
185
1
}
186
187
static void
188
fu_efi_device_path_init(FuEfiDevicePath *self)
189
23.3k
{
190
23.3k
}
191
192
static void
193
fu_efi_device_path_class_init(FuEfiDevicePathClass *klass)
194
1
{
195
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
196
1
  firmware_class->export = fu_efi_device_path_export;
197
1
  firmware_class->parse = fu_efi_device_path_parse;
198
1
  firmware_class->write = fu_efi_device_path_write;
199
1
  firmware_class->build = fu_efi_device_path_build;
200
1
}
201
202
/**
203
 * fu_efi_device_path_new:
204
 *
205
 * Creates a new EFI DEVICE_PATH
206
 *
207
 * Since: 1.9.3
208
 **/
209
FuEfiDevicePath *
210
fu_efi_device_path_new(void)
211
4.95k
{
212
4.95k
  return g_object_new(FU_TYPE_EFI_DEVICE_PATH, NULL);
213
4.95k
}