1 /*
2 * SMBIOS legacy support
3 *
4 * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
5 * Copyright (C) 2013 Red Hat, Inc.
6 *
7 * Authors:
8 * Alex Williamson <alex.williamson@hp.com>
9 * Markus Armbruster <armbru@redhat.com>
10 *
11 * This work is licensed under the terms of the GNU GPL, version 2. See
12 * the COPYING file in the top-level directory.
13 *
14 * Contributions after 2012-01-13 are licensed under the terms of the
15 * GNU GPL, version 2 or (at your option) any later version.
16 */
17
18 #include "qemu/osdep.h"
19 #include "qemu/bswap.h"
20 #include "hw/firmware/smbios.h"
21 #include "sysemu/sysemu.h"
22 #include "qapi/error.h"
23
24 struct smbios_header {
25 uint16_t length;
26 uint8_t type;
27 } QEMU_PACKED;
28
29 struct smbios_field {
30 struct smbios_header header;
31 uint8_t type;
32 uint16_t offset;
33 uint8_t data[];
34 } QEMU_PACKED;
35
36 struct smbios_table {
37 struct smbios_header header;
38 uint8_t data[];
39 } QEMU_PACKED;
40
41 #define SMBIOS_FIELD_ENTRY 0
42 #define SMBIOS_TABLE_ENTRY 1
43
44 static uint8_t *smbios_entries;
45 static size_t smbios_entries_len;
46 GArray *usr_blobs_sizes;
47
smbios_add_usr_blob_size(size_t size)48 void smbios_add_usr_blob_size(size_t size)
49 {
50 if (!usr_blobs_sizes) {
51 usr_blobs_sizes = g_array_new(false, false, sizeof(size_t));
52 }
53 g_array_append_val(usr_blobs_sizes, size);
54 }
55
smbios_add_field(int type,int offset,const void * data,size_t len)56 static void smbios_add_field(int type, int offset, const void *data, size_t len)
57 {
58 struct smbios_field *field;
59
60 if (!smbios_entries) {
61 smbios_entries_len = sizeof(uint16_t);
62 smbios_entries = g_malloc0(smbios_entries_len);
63 }
64 smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
65 sizeof(*field) + len);
66 field = (struct smbios_field *)(smbios_entries + smbios_entries_len);
67 field->header.type = SMBIOS_FIELD_ENTRY;
68 field->header.length = cpu_to_le16(sizeof(*field) + len);
69
70 field->type = type;
71 field->offset = cpu_to_le16(offset);
72 memcpy(field->data, data, len);
73
74 smbios_entries_len += sizeof(*field) + len;
75 (*(uint16_t *)smbios_entries) =
76 cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
77 }
78
smbios_maybe_add_str(int type,int offset,const char * data)79 static void smbios_maybe_add_str(int type, int offset, const char *data)
80 {
81 if (data) {
82 smbios_add_field(type, offset, data, strlen(data) + 1);
83 }
84 }
85
smbios_build_type_0_fields(void)86 static void smbios_build_type_0_fields(void)
87 {
88 smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str),
89 smbios_type0.vendor);
90 smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str),
91 smbios_type0.version);
92 smbios_maybe_add_str(0, offsetof(struct smbios_type_0,
93 bios_release_date_str),
94 smbios_type0.date);
95 if (smbios_type0.have_major_minor) {
96 smbios_add_field(0, offsetof(struct smbios_type_0,
97 system_bios_major_release),
98 &smbios_type0.major, 1);
99 smbios_add_field(0, offsetof(struct smbios_type_0,
100 system_bios_minor_release),
101 &smbios_type0.minor, 1);
102 }
103 }
104
smbios_build_type_1_fields(void)105 static void smbios_build_type_1_fields(void)
106 {
107 smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str),
108 smbios_type1.manufacturer);
109 smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str),
110 smbios_type1.product);
111 smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str),
112 smbios_type1.version);
113 smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str),
114 smbios_type1.serial);
115 smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str),
116 smbios_type1.sku);
117 smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str),
118 smbios_type1.family);
119 if (qemu_uuid_set) {
120 /*
121 * We don't encode the UUID in the "wire format" here because this
122 * function is for legacy mode and needs to keep the guest ABI, and
123 * because we don't know what's the SMBIOS version advertised by the
124 * BIOS.
125 */
126 smbios_add_field(1, offsetof(struct smbios_type_1, uuid),
127 &qemu_uuid, 16);
128 }
129 }
130
smbios_get_table_legacy(size_t * length,Error ** errp)131 uint8_t *smbios_get_table_legacy(size_t *length, Error **errp)
132 {
133 int i;
134 size_t usr_offset;
135
136 /* complain if fields were given for types > 1 */
137 if (find_next_bit(smbios_have_fields_bitmap,
138 SMBIOS_MAX_TYPE + 1, 2) < SMBIOS_MAX_TYPE + 1) {
139 error_setg(errp, "can't process fields for smbios "
140 "types > 1 on machine versions < 2.1!");
141 goto err_exit;
142 }
143
144 if (test_bit(4, smbios_have_binfile_bitmap)) {
145 error_setg(errp, "can't process table for smbios "
146 "type 4 on machine versions < 2.1!");
147 goto err_exit;
148 }
149
150 g_free(smbios_entries);
151 smbios_entries_len = sizeof(uint16_t);
152 smbios_entries = g_malloc0(smbios_entries_len);
153
154 /*
155 * build a set of legacy smbios_table entries using user provided blobs
156 */
157 for (i = 0, usr_offset = 0; usr_blobs_sizes && i < usr_blobs_sizes->len;
158 i++)
159 {
160 struct smbios_table *table;
161 struct smbios_structure_header *header;
162 size_t size = g_array_index(usr_blobs_sizes, size_t, i);
163
164 header = (struct smbios_structure_header *)(usr_blobs + usr_offset);
165 smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
166 size + sizeof(*table));
167 table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
168 table->header.type = SMBIOS_TABLE_ENTRY;
169 table->header.length = cpu_to_le16(sizeof(*table) + size);
170 memcpy(table->data, header, size);
171 smbios_entries_len += sizeof(*table) + size;
172 /*
173 * update number of entries in the blob,
174 * see SeaBIOS: qemu_cfg_legacy():QEMU_CFG_SMBIOS_ENTRIES
175 */
176 (*(uint16_t *)smbios_entries) =
177 cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
178 usr_offset += size;
179 }
180
181 smbios_build_type_0_fields();
182 smbios_build_type_1_fields();
183 if (!smbios_validate_table(SMBIOS_ENTRY_POINT_TYPE_32, errp)) {
184 goto err_exit;
185 }
186
187 *length = smbios_entries_len;
188 return smbios_entries;
189 err_exit:
190 g_free(smbios_entries);
191 return NULL;
192 }
193