Coverage Report

Created: 2025-06-22 06:29

/src/fwupd/plugins/redfish/fu-redfish-smbios.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2017 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-redfish-common.h"
10
#include "fu-redfish-smbios.h"
11
#include "fu-redfish-struct.h"
12
13
struct _FuRedfishSmbios {
14
  FuFirmwareClass parent_instance;
15
  FuRedfishSmbiosInterfaceType interface_type;
16
  guint16 port;
17
  gchar *hostname;
18
  gchar *mac_addr;
19
  gchar *ip_addr;
20
  guint16 vid;
21
  guint16 pid;
22
};
23
24
G_DEFINE_TYPE(FuRedfishSmbios, fu_redfish_smbios, FU_TYPE_FIRMWARE)
25
26
FuRedfishSmbiosInterfaceType
27
fu_redfish_smbios_get_interface_type(FuRedfishSmbios *self)
28
0
{
29
0
  return self->interface_type;
30
0
}
31
32
guint16
33
fu_redfish_smbios_get_port(FuRedfishSmbios *self)
34
0
{
35
0
  return self->port;
36
0
}
37
38
guint16
39
fu_redfish_smbios_get_vid(FuRedfishSmbios *self)
40
0
{
41
0
  return self->vid;
42
0
}
43
44
guint16
45
fu_redfish_smbios_get_pid(FuRedfishSmbios *self)
46
0
{
47
0
  return self->pid;
48
0
}
49
50
const gchar *
51
fu_redfish_smbios_get_hostname(FuRedfishSmbios *self)
52
0
{
53
0
  return self->hostname;
54
0
}
55
56
const gchar *
57
fu_redfish_smbios_get_mac_addr(FuRedfishSmbios *self)
58
0
{
59
0
  return self->mac_addr;
60
0
}
61
62
const gchar *
63
fu_redfish_smbios_get_ip_addr(FuRedfishSmbios *self)
64
0
{
65
0
  return self->ip_addr;
66
0
}
67
68
static void
69
fu_redfish_smbios_set_hostname(FuRedfishSmbios *self, const gchar *hostname)
70
1.00k
{
71
1.00k
  g_free(self->hostname);
72
1.00k
  self->hostname = g_strdup(hostname);
73
1.00k
}
74
75
static void
76
fu_redfish_smbios_set_mac_addr(FuRedfishSmbios *self, const gchar *mac_addr)
77
61
{
78
61
  g_free(self->mac_addr);
79
61
  self->mac_addr = g_strdup(mac_addr);
80
61
}
81
82
static void
83
fu_redfish_smbios_set_ip_addr(FuRedfishSmbios *self, const gchar *ip_addr)
84
1.46k
{
85
1.46k
  g_free(self->ip_addr);
86
1.46k
  self->ip_addr = g_strdup(ip_addr);
87
1.46k
}
88
89
static void
90
fu_redfish_smbios_set_port(FuRedfishSmbios *self, guint16 port)
91
1.47k
{
92
1.47k
  self->port = port;
93
1.47k
}
94
95
static void
96
fu_redfish_smbios_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
97
0
{
98
0
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
99
0
  fu_xmlb_builder_insert_kv(bn,
100
0
          "interface_type",
101
0
          fu_redfish_smbios_interface_type_to_string(self->interface_type));
102
0
  fu_xmlb_builder_insert_kx(bn, "port", self->port);
103
0
  fu_xmlb_builder_insert_kv(bn, "hostname", self->hostname);
104
0
  fu_xmlb_builder_insert_kv(bn, "mac_addr", self->mac_addr);
105
0
  fu_xmlb_builder_insert_kv(bn, "ip_addr", self->ip_addr);
106
0
  fu_xmlb_builder_insert_kx(bn, "vid", self->vid);
107
0
  fu_xmlb_builder_insert_kx(bn, "pid", self->pid);
108
0
}
109
110
static gboolean
111
fu_redfish_smbios_build(FuFirmware *firmware, XbNode *n, GError **error)
112
0
{
113
0
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
114
0
  const gchar *tmp;
115
0
  guint64 tmpu;
116
117
  /* optional properties */
118
0
  tmpu = xb_node_query_text_as_uint(n, "port", NULL);
119
0
  if (tmpu != G_MAXUINT64)
120
0
    fu_redfish_smbios_set_port(self, (guint16)tmpu);
121
0
  tmpu = xb_node_query_text_as_uint(n, "vid", NULL);
122
0
  if (tmpu != G_MAXUINT64)
123
0
    self->vid = (guint16)tmpu;
124
0
  tmpu = xb_node_query_text_as_uint(n, "pid", NULL);
125
0
  if (tmpu != G_MAXUINT64)
126
0
    self->pid = (guint16)tmpu;
127
0
  tmp = xb_node_query_text(n, "hostname", NULL);
128
0
  if (tmp != NULL)
129
0
    fu_redfish_smbios_set_hostname(self, tmp);
130
0
  tmp = xb_node_query_text(n, "mac_addr", NULL);
131
0
  if (tmp != NULL)
132
0
    fu_redfish_smbios_set_mac_addr(self, tmp);
133
0
  tmp = xb_node_query_text(n, "ip_addr", NULL);
134
0
  if (tmp != NULL)
135
0
    fu_redfish_smbios_set_ip_addr(self, tmp);
136
137
  /* success */
138
0
  return TRUE;
139
0
}
140
141
static gboolean
142
fu_redfish_smbios_parse_interface_data(FuRedfishSmbios *self,
143
               GInputStream *stream,
144
               gsize offset,
145
               GError **error)
146
151
{
147
151
  gsize offset_mac_addr = G_MAXSIZE;
148
151
  gsize offset_vid_pid = G_MAXSIZE;
149
151
  guint8 interface_type = 0x0;
150
151
  /* parse the data depending on the interface type */
152
151
  if (!fu_input_stream_read_u8(stream, offset, &interface_type, error))
153
0
    return FALSE;
154
151
  g_debug("interface_type: %s [0x%x]",
155
151
    fu_redfish_interface_type_to_string(interface_type),
156
151
    interface_type);
157
151
  offset++;
158
151
  switch (interface_type) {
159
8
  case FU_REDFISH_INTERFACE_TYPE_USB_NETWORK:
160
22
  case FU_REDFISH_INTERFACE_TYPE_PCI_NETWORK:
161
22
    offset_vid_pid = 0x00;
162
22
    break;
163
28
  case FU_REDFISH_INTERFACE_TYPE_USB_NETWORK_V2:
164
28
    offset_vid_pid = 0x01;
165
28
    offset_mac_addr = 0x06;
166
28
    break;
167
37
  case FU_REDFISH_INTERFACE_TYPE_PCI_NETWORK_V2:
168
37
    offset_vid_pid = 0x01;
169
37
    offset_mac_addr = 0x09;
170
37
    break;
171
64
  default:
172
64
    g_debug("unknown Network Interface");
173
64
    break;
174
151
  }
175
176
  /* MAC address */
177
151
  if (offset_mac_addr != G_MAXSIZE) {
178
65
    guint8 mac_addr[6] = {0x0};
179
65
    g_autofree gchar *mac_addr_str = NULL;
180
65
    if (!fu_input_stream_read_safe(stream,
181
65
                 mac_addr,
182
65
                 sizeof(mac_addr),
183
65
                 0x0,      /* dst */
184
65
                 offset + offset_mac_addr, /* src */
185
65
                 sizeof(mac_addr),
186
65
                 error))
187
4
      return FALSE;
188
61
    mac_addr_str = fu_redfish_common_buffer_to_mac(mac_addr);
189
61
    fu_redfish_smbios_set_mac_addr(self, mac_addr_str);
190
61
  }
191
192
  /* VID:PID */
193
147
  if (offset_vid_pid != G_MAXSIZE) {
194
83
    if (!fu_input_stream_read_u16(stream,
195
83
                offset + offset_vid_pid,
196
83
                &self->vid,
197
83
                G_LITTLE_ENDIAN,
198
83
                error))
199
0
      return FALSE;
200
83
    if (!fu_input_stream_read_u16(stream,
201
83
                offset + offset_vid_pid + 0x02,
202
83
                &self->pid,
203
83
                G_LITTLE_ENDIAN,
204
83
                error))
205
3
      return FALSE;
206
83
  }
207
208
  /* success */
209
144
  return TRUE;
210
147
}
211
212
static gboolean
213
fu_redfish_smbios_parse_over_ip(FuRedfishSmbios *self,
214
        GInputStream *stream,
215
        gsize offset,
216
        GError **error)
217
1.52k
{
218
1.52k
  guint8 hostname_length;
219
1.52k
  guint8 service_ip_address_format;
220
1.52k
  g_autoptr(GByteArray) st = NULL;
221
222
  /* port + IP address */
223
1.52k
  st = fu_struct_redfish_protocol_over_ip_parse_stream(stream, offset, error);
224
1.52k
  if (st == NULL)
225
55
    return FALSE;
226
1.47k
  fu_redfish_smbios_set_port(self,
227
1.47k
           fu_struct_redfish_protocol_over_ip_get_service_ip_port(st));
228
1.47k
  service_ip_address_format =
229
1.47k
      fu_struct_redfish_protocol_over_ip_get_service_ip_address_format(st);
230
1.47k
  if (service_ip_address_format == FU_REDFISH_IP_ADDRESS_FORMAT_V4) {
231
998
    const guint8 *ip_address =
232
998
        fu_struct_redfish_protocol_over_ip_get_service_ip_address(st, NULL);
233
998
    g_autofree gchar *tmp = fu_redfish_common_buffer_to_ipv4(ip_address);
234
998
    fu_redfish_smbios_set_ip_addr(self, tmp);
235
998
  } else if (service_ip_address_format == FU_REDFISH_IP_ADDRESS_FORMAT_V6) {
236
467
    const guint8 *ip_address =
237
467
        fu_struct_redfish_protocol_over_ip_get_service_ip_address(st, NULL);
238
467
    g_autofree gchar *tmp = fu_redfish_common_buffer_to_ipv6(ip_address);
239
467
    fu_redfish_smbios_set_ip_addr(self, tmp);
240
467
  } else {
241
8
    g_set_error_literal(error,
242
8
            FWUPD_ERROR,
243
8
            FWUPD_ERROR_INVALID_FILE,
244
8
            "address format is invalid");
245
8
    return FALSE;
246
8
  }
247
248
  /* hostname */
249
1.46k
  hostname_length = fu_struct_redfish_protocol_over_ip_get_service_hostname_len(st);
250
1.46k
  if (hostname_length > 0) {
251
1.03k
    g_autofree gchar *hostname = g_malloc0(hostname_length + 1);
252
1.03k
    if (!fu_input_stream_read_safe(stream,
253
1.03k
                 (guint8 *)hostname,
254
1.03k
                 hostname_length,
255
1.03k
                 0x0,    /* dst */
256
1.03k
                 offset + st->len, /* seek */
257
1.03k
                 hostname_length,
258
1.03k
                 error))
259
33
      return FALSE;
260
1.00k
    fu_redfish_smbios_set_hostname(self, hostname);
261
1.00k
  }
262
263
  /* success */
264
1.43k
  return TRUE;
265
1.46k
}
266
267
static gboolean
268
fu_redfish_smbios_parse(FuFirmware *firmware,
269
      GInputStream *stream,
270
      FuFirmwareParseFlags flags,
271
      GError **error)
272
420
{
273
420
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
274
420
  gsize offset = 0;
275
420
  gsize streamsz = 0;
276
420
  g_autoptr(GByteArray) st = NULL;
277
278
  /* check size */
279
420
  if (!fu_input_stream_size(stream, &streamsz, error))
280
0
    return FALSE;
281
420
  if (streamsz < 0x09) {
282
14
    g_set_error(error,
283
14
          FWUPD_ERROR,
284
14
          FWUPD_ERROR_INVALID_FILE,
285
14
          "SMBIOS entry too small: %" G_GSIZE_FORMAT,
286
14
          streamsz);
287
14
    return FALSE;
288
14
  }
289
290
  /* parse */
291
406
  st = fu_struct_redfish_smbios_type42_parse_stream(stream, offset, error);
292
406
  if (st == NULL)
293
26
    return FALSE;
294
295
  /* check length */
296
380
  if (fu_struct_redfish_smbios_type42_get_length(st) != streamsz) {
297
91
    g_set_error(error,
298
91
          FWUPD_ERROR,
299
91
          FWUPD_ERROR_INVALID_FILE,
300
91
          "size of table 0x%x does not match binary 0x%x",
301
91
          fu_struct_redfish_smbios_type42_get_length(st),
302
91
          (guint)streamsz);
303
91
    return FALSE;
304
91
  }
305
306
  /* check length */
307
289
  offset += FU_STRUCT_REDFISH_SMBIOS_TYPE42_SIZE;
308
289
  if (fu_struct_redfish_smbios_type42_get_data_length(st) > 0) {
309
151
    if (!fu_redfish_smbios_parse_interface_data(self, stream, offset, error))
310
7
      return FALSE;
311
151
  }
312
282
  offset += fu_struct_redfish_smbios_type42_get_data_length(st);
313
314
  /* parse protocol records */
315
282
  self->interface_type = fu_struct_redfish_smbios_type42_get_interface_type(st);
316
282
  if (self->interface_type == FU_REDFISH_SMBIOS_INTERFACE_TYPE_NETWORK) {
317
264
    guint8 protocol_rcds = 0;
318
264
    if (!fu_input_stream_read_u8(stream, offset, &protocol_rcds, error))
319
4
      return FALSE;
320
260
    offset += 1;
321
260
    g_debug("protocol_rcds: %u", protocol_rcds);
322
7.78k
    for (guint i = 0; i < protocol_rcds; i++) {
323
7.74k
      guint8 protocol_id = 0;
324
7.74k
      guint8 protocol_sz = 0;
325
7.74k
      if (!fu_input_stream_read_u8(stream, offset, &protocol_id, error))
326
67
        return FALSE;
327
7.68k
      if (!fu_input_stream_read_u8(stream, offset + 0x1, &protocol_sz, error))
328
60
        return FALSE;
329
7.62k
      if (protocol_id == REDFISH_PROTOCOL_REDFISH_OVER_IP) {
330
1.52k
        if (!fu_redfish_smbios_parse_over_ip(self,
331
1.52k
                     stream,
332
1.52k
                     offset + 0x2,
333
1.52k
                     error))
334
96
          return FALSE;
335
6.09k
      } else {
336
6.09k
        g_debug("ignoring protocol ID 0x%02x", protocol_id);
337
6.09k
      }
338
7.52k
      offset += protocol_sz + 1;
339
7.52k
    }
340
260
  }
341
342
  /* success */
343
55
  return TRUE;
344
282
}
345
346
static GByteArray *
347
fu_redfish_smbios_write(FuFirmware *firmware, GError **error)
348
55
{
349
55
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
350
55
  gsize hostname_sz = 0;
351
55
  g_autoptr(GByteArray) st = fu_struct_redfish_protocol_over_ip_new();
352
55
  g_autoptr(GByteArray) buf = g_byte_array_new();
353
354
55
  if (self->hostname != NULL)
355
23
    hostname_sz = strlen(self->hostname);
356
55
  fu_byte_array_append_uint8(buf, REDFISH_SMBIOS_TABLE_TYPE);
357
55
  fu_byte_array_append_uint8(buf, 0x6D + hostname_sz);     /* length */
358
55
  fu_byte_array_append_uint16(buf, 0x1234, G_LITTLE_ENDIAN); /* handle */
359
55
  fu_byte_array_append_uint8(buf, FU_REDFISH_SMBIOS_INTERFACE_TYPE_NETWORK);
360
55
  fu_byte_array_append_uint8(buf, 0x09);          /* iface datalen */
361
55
  fu_byte_array_append_uint8(buf, FU_REDFISH_INTERFACE_TYPE_USB_NETWORK); /* iface */
362
55
  fu_byte_array_append_uint16(buf, self->vid, G_LITTLE_ENDIAN);    /* iface:VID */
363
55
  fu_byte_array_append_uint16(buf, self->pid, G_LITTLE_ENDIAN);    /* iface:PID */
364
55
  fu_byte_array_append_uint8(buf, 0x02);          /* iface:serialsz */
365
55
  fu_byte_array_append_uint8(buf, 0x03);          /* iType */
366
55
  fu_byte_array_append_uint8(buf, 'S');         /* iface:serial */
367
55
  fu_byte_array_append_uint8(buf, 'n');         /* iface:serial */
368
55
  fu_byte_array_append_uint8(buf, 0x1); /* nr protocol rcds */
369
370
  /* protocol record */
371
55
  fu_byte_array_append_uint8(buf, REDFISH_PROTOCOL_REDFISH_OVER_IP);
372
55
  fu_byte_array_append_uint8(buf, st->len + hostname_sz);
373
374
55
  if (self->hostname != NULL)
375
23
    hostname_sz = strlen(self->hostname);
376
55
  fu_struct_redfish_protocol_over_ip_set_service_ip_port(st, self->port);
377
55
  fu_struct_redfish_protocol_over_ip_set_service_ip_address_format(
378
55
      st,
379
55
      FU_REDFISH_IP_ADDRESS_FORMAT_V4);
380
55
  fu_struct_redfish_protocol_over_ip_set_service_ip_assignment_type(
381
55
      st,
382
55
      FU_REDFISH_IP_ASSIGNMENT_TYPE_STATIC);
383
55
  fu_struct_redfish_protocol_over_ip_set_service_hostname_len(st, hostname_sz);
384
55
  g_byte_array_append(buf, st->data, st->len);
385
55
  if (hostname_sz > 0)
386
20
    g_byte_array_append(buf, (guint8 *)self->hostname, hostname_sz);
387
55
  return g_steal_pointer(&buf);
388
55
}
389
390
static void
391
fu_redfish_smbios_finalize(GObject *object)
392
420
{
393
420
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(object);
394
420
  g_free(self->hostname);
395
420
  g_free(self->mac_addr);
396
420
  g_free(self->ip_addr);
397
420
  G_OBJECT_CLASS(fu_redfish_smbios_parent_class)->finalize(object);
398
420
}
399
400
static void
401
fu_redfish_smbios_init(FuRedfishSmbios *self)
402
420
{
403
420
}
404
405
static void
406
fu_redfish_smbios_class_init(FuRedfishSmbiosClass *klass)
407
1
{
408
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
409
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
410
1
  object_class->finalize = fu_redfish_smbios_finalize;
411
1
  firmware_class->parse = fu_redfish_smbios_parse;
412
1
  firmware_class->write = fu_redfish_smbios_write;
413
1
  firmware_class->build = fu_redfish_smbios_build;
414
1
  firmware_class->export = fu_redfish_smbios_export;
415
1
}
416
417
FuRedfishSmbios *
418
fu_redfish_smbios_new(void)
419
0
{
420
0
  return FU_REDFISH_SMBIOS(g_object_new(FU_TYPE_REDFISH_SMBIOS, NULL));
421
0
}