/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 | } |