Coverage Report

Created: 2025-07-11 06:31

/src/fwupd/libfwupdplugin/fu-oprom-firmware.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2022 Richard Hughes <richard@hughsie.com>
3
 * Copyright 2022 Intel
4
 *
5
 * SPDX-License-Identifier: LGPL-2.1-or-later
6
 */
7
8
0
#define G_LOG_DOMAIN "FuFirmware"
9
10
#include "config.h"
11
12
#include <string.h>
13
14
#include "fu-byte-array.h"
15
#include "fu-common.h"
16
#include "fu-ifwi-cpd-firmware.h"
17
#include "fu-oprom-firmware.h"
18
#include "fu-string.h"
19
20
/**
21
 * FuOpromFirmware:
22
 *
23
 * An OptionROM can be found in nearly every PCI device. Multiple OptionROM images may be appended.
24
 *
25
 * See also: [class@FuFirmware]
26
 */
27
28
typedef struct {
29
  FuOpromMachineType machine_type;
30
  FuOpromSubsystem subsystem;
31
  FuOpromCompressionType compression_type;
32
  guint16 vendor_id;
33
  guint16 device_id;
34
} FuOpromFirmwarePrivate;
35
36
G_DEFINE_TYPE_WITH_PRIVATE(FuOpromFirmware, fu_oprom_firmware, FU_TYPE_FIRMWARE)
37
1.61k
#define GET_PRIVATE(o) (fu_oprom_firmware_get_instance_private(o))
38
39
2.26k
#define FU_OPROM_FIRMWARE_ALIGN_LEN      512u
40
41
/**
42
 * fu_oprom_firmware_get_machine_type:
43
 * @self: a #FuFirmware
44
 *
45
 * Gets the machine type.
46
 *
47
 * Returns: a #FuOpromMachineType
48
 *
49
 * Since: 1.8.2
50
 **/
51
FuOpromMachineType
52
fu_oprom_firmware_get_machine_type(FuOpromFirmware *self)
53
0
{
54
0
  FuOpromFirmwarePrivate *priv = GET_PRIVATE(self);
55
0
  g_return_val_if_fail(FU_IS_OPROM_FIRMWARE(self), G_MAXUINT16);
56
0
  return priv->machine_type;
57
0
}
58
59
/**
60
 * fu_oprom_firmware_get_subsystem:
61
 * @self: a #FuFirmware
62
 *
63
 * Gets the machine type.
64
 *
65
 * Returns: a #FuOpromSubsystem
66
 *
67
 * Since: 1.8.2
68
 **/
69
FuOpromSubsystem
70
fu_oprom_firmware_get_subsystem(FuOpromFirmware *self)
71
0
{
72
0
  FuOpromFirmwarePrivate *priv = GET_PRIVATE(self);
73
0
  g_return_val_if_fail(FU_IS_OPROM_FIRMWARE(self), G_MAXUINT16);
74
0
  return priv->subsystem;
75
0
}
76
77
/**
78
 * fu_oprom_firmware_get_compression_type:
79
 * @self: a #FuFirmware
80
 *
81
 * Gets the machine type.
82
 *
83
 * Returns: a #FuOpromCompressionType
84
 *
85
 * Since: 1.8.2
86
 **/
87
FuOpromCompressionType
88
fu_oprom_firmware_get_compression_type(FuOpromFirmware *self)
89
0
{
90
0
  FuOpromFirmwarePrivate *priv = GET_PRIVATE(self);
91
0
  g_return_val_if_fail(FU_IS_OPROM_FIRMWARE(self), G_MAXUINT16);
92
0
  return priv->compression_type;
93
0
}
94
95
static void
96
fu_oprom_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
97
0
{
98
0
  FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware);
99
0
  FuOpromFirmwarePrivate *priv = GET_PRIVATE(self);
100
0
  fu_xmlb_builder_insert_kx(bn, "machine_type", priv->machine_type);
101
0
  fu_xmlb_builder_insert_kx(bn, "subsystem", priv->subsystem);
102
0
  fu_xmlb_builder_insert_kx(bn, "compression_type", priv->compression_type);
103
0
  fu_xmlb_builder_insert_kx(bn, "vendor_id", priv->vendor_id);
104
0
  fu_xmlb_builder_insert_kx(bn, "device_id", priv->device_id);
105
0
}
106
107
static gboolean
108
fu_oprom_firmware_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error)
109
4.20M
{
110
4.20M
  return fu_struct_oprom_validate_stream(stream, offset, error);
111
4.20M
}
112
113
static gboolean
114
fu_oprom_firmware_parse(FuFirmware *firmware,
115
      GInputStream *stream,
116
      FuFirmwareParseFlags flags,
117
      GError **error)
118
866
{
119
866
  FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware);
120
866
  FuOpromFirmwarePrivate *priv = GET_PRIVATE(self);
121
866
  guint16 expansion_header_offset = 0;
122
866
  guint16 pci_header_offset;
123
866
  guint16 image_length = 0;
124
866
  g_autoptr(GByteArray) st_hdr = NULL;
125
866
  g_autoptr(GByteArray) st_pci = NULL;
126
127
  /* parse header */
128
866
  st_hdr = fu_struct_oprom_parse_stream(stream, 0x0, error);
129
866
  if (st_hdr == NULL)
130
0
    return FALSE;
131
866
  priv->subsystem = fu_struct_oprom_get_subsystem(st_hdr);
132
866
  priv->compression_type = fu_struct_oprom_get_compression_type(st_hdr);
133
866
  priv->machine_type = fu_struct_oprom_get_machine_type(st_hdr);
134
135
  /* get PCI offset */
136
866
  pci_header_offset = fu_struct_oprom_get_pci_header_offset(st_hdr);
137
866
  if (pci_header_offset == 0x0) {
138
11
    g_set_error(error,
139
11
          FWUPD_ERROR,
140
11
          FWUPD_ERROR_INVALID_DATA,
141
11
          "no PCI data structure offset provided");
142
11
    return FALSE;
143
11
  }
144
145
  /* verify signature */
146
855
  st_pci = fu_struct_oprom_pci_parse_stream(stream, pci_header_offset, error);
147
855
  if (st_pci == NULL)
148
87
    return FALSE;
149
768
  priv->vendor_id = fu_struct_oprom_pci_get_vendor_id(st_pci);
150
768
  priv->device_id = fu_struct_oprom_pci_get_device_id(st_pci);
151
152
  /* get length */
153
768
  image_length = fu_struct_oprom_pci_get_image_length(st_pci);
154
768
  if (image_length == 0x0) {
155
3
    g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid image length");
156
3
    return FALSE;
157
3
  }
158
765
  fu_firmware_set_size(firmware, image_length * FU_OPROM_FIRMWARE_ALIGN_LEN);
159
765
  fu_firmware_set_idx(firmware, fu_struct_oprom_pci_get_code_type(st_pci));
160
161
  /* is last image */
162
765
  if (fu_struct_oprom_pci_get_indicator(st_pci) & FU_OPROM_INDICATOR_FLAG_LAST)
163
249
    fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_IS_LAST_IMAGE);
164
165
  /* get CPD offset */
166
765
  expansion_header_offset = fu_struct_oprom_get_expansion_header_offset(st_hdr);
167
765
  if (expansion_header_offset != 0x0) {
168
764
    g_autoptr(FuFirmware) img = NULL;
169
764
    img = fu_firmware_new_from_gtypes(stream,
170
764
              expansion_header_offset,
171
764
              flags,
172
764
              error,
173
764
              FU_TYPE_IFWI_CPD_FIRMWARE,
174
764
              FU_TYPE_FIRMWARE,
175
764
              G_TYPE_INVALID);
176
764
    if (img == NULL) {
177
16
      g_prefix_error(error, "failed to build firmware: ");
178
16
      return FALSE;
179
16
    }
180
748
    fu_firmware_set_id(img, "cpd");
181
748
    fu_firmware_set_offset(img, expansion_header_offset);
182
748
    fu_firmware_add_image(firmware, img);
183
748
  }
184
185
  /* success */
186
749
  return TRUE;
187
765
}
188
189
static GByteArray *
190
fu_oprom_firmware_write(FuFirmware *firmware, GError **error)
191
749
{
192
749
  FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware);
193
749
  FuOpromFirmwarePrivate *priv = GET_PRIVATE(self);
194
749
  gsize image_size = 0;
195
749
  g_autoptr(GByteArray) buf = g_byte_array_new();
196
749
  g_autoptr(GByteArray) st_hdr = fu_struct_oprom_new();
197
749
  g_autoptr(GByteArray) st_pci = fu_struct_oprom_pci_new();
198
749
  g_autoptr(GBytes) blob_cpd = NULL;
199
200
  /* the smallest each image (and header) can be is 512 bytes */
201
749
  image_size += fu_common_align_up(st_hdr->len, FU_FIRMWARE_ALIGNMENT_512);
202
749
  blob_cpd = fu_firmware_get_image_by_id_bytes(firmware, "cpd", NULL);
203
749
  if (blob_cpd != NULL) {
204
4
    image_size +=
205
4
        fu_common_align_up(g_bytes_get_size(blob_cpd), FU_FIRMWARE_ALIGNMENT_512);
206
4
  }
207
208
  /* write the header */
209
749
  fu_struct_oprom_set_image_size(st_hdr, image_size / FU_OPROM_FIRMWARE_ALIGN_LEN);
210
749
  fu_struct_oprom_set_subsystem(st_hdr, priv->subsystem);
211
749
  fu_struct_oprom_set_machine_type(st_hdr, priv->machine_type);
212
749
  fu_struct_oprom_set_compression_type(st_hdr, priv->compression_type);
213
749
  if (blob_cpd != NULL) {
214
4
    fu_struct_oprom_set_expansion_header_offset(st_hdr,
215
4
                  image_size -
216
4
                FU_OPROM_FIRMWARE_ALIGN_LEN);
217
4
  }
218
749
  g_byte_array_append(buf, st_hdr->data, st_hdr->len);
219
220
  /* add PCI section */
221
749
  fu_struct_oprom_pci_set_vendor_id(st_pci, priv->vendor_id);
222
749
  fu_struct_oprom_pci_set_device_id(st_pci, priv->device_id);
223
749
  fu_struct_oprom_pci_set_image_length(st_pci, image_size / FU_OPROM_FIRMWARE_ALIGN_LEN);
224
749
  fu_struct_oprom_pci_set_code_type(st_pci, fu_firmware_get_idx(firmware));
225
749
  if (fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_IS_LAST_IMAGE))
226
240
    fu_struct_oprom_pci_set_indicator(st_pci, FU_OPROM_INDICATOR_FLAG_LAST);
227
749
  g_byte_array_append(buf, st_pci->data, st_pci->len);
228
749
  fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_512, 0xFF);
229
230
  /* add CPD */
231
749
  if (blob_cpd != NULL) {
232
4
    fu_byte_array_append_bytes(buf, blob_cpd);
233
4
    fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_512, 0xFF);
234
4
  }
235
236
  /* success */
237
749
  return g_steal_pointer(&buf);
238
749
}
239
240
static gboolean
241
fu_oprom_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
242
0
{
243
0
  FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware);
244
0
  FuOpromFirmwarePrivate *priv = GET_PRIVATE(self);
245
0
  const gchar *tmp;
246
247
  /* simple properties */
248
0
  tmp = xb_node_query_text(n, "machine_type", NULL);
249
0
  if (tmp != NULL) {
250
0
    guint64 val = 0;
251
0
    if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error))
252
0
      return FALSE;
253
0
    priv->machine_type = val;
254
0
  }
255
0
  tmp = xb_node_query_text(n, "subsystem", NULL);
256
0
  if (tmp != NULL) {
257
0
    guint64 val = 0;
258
0
    if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error))
259
0
      return FALSE;
260
0
    priv->subsystem = val;
261
0
  }
262
0
  tmp = xb_node_query_text(n, "compression_type", NULL);
263
0
  if (tmp != NULL) {
264
0
    guint64 val = 0;
265
0
    if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error))
266
0
      return FALSE;
267
0
    priv->compression_type = val;
268
0
  }
269
0
  tmp = xb_node_query_text(n, "vendor_id", NULL);
270
0
  if (tmp != NULL) {
271
0
    guint64 val = 0;
272
0
    if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error))
273
0
      return FALSE;
274
0
    priv->vendor_id = val;
275
0
  }
276
0
  tmp = xb_node_query_text(n, "device_id", NULL);
277
0
  if (tmp != NULL) {
278
0
    guint64 val = 0;
279
0
    if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error))
280
0
      return FALSE;
281
0
    priv->device_id = val;
282
0
  }
283
284
  /* success */
285
0
  return TRUE;
286
0
}
287
288
static void
289
fu_oprom_firmware_init(FuOpromFirmware *self)
290
948
{
291
948
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
292
948
}
293
294
static void
295
fu_oprom_firmware_class_init(FuOpromFirmwareClass *klass)
296
1
{
297
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
298
1
  firmware_class->validate = fu_oprom_firmware_validate;
299
1
  firmware_class->export = fu_oprom_firmware_export;
300
1
  firmware_class->parse = fu_oprom_firmware_parse;
301
1
  firmware_class->write = fu_oprom_firmware_write;
302
1
  firmware_class->build = fu_oprom_firmware_build;
303
1
}
304
305
/**
306
 * fu_oprom_firmware_new:
307
 *
308
 * Creates a new #FuFirmware of OptionROM format
309
 *
310
 * Since: 1.8.2
311
 **/
312
FuFirmware *
313
fu_oprom_firmware_new(void)
314
0
{
315
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_OPROM_FIRMWARE, NULL));
316
0
}