blob: fc84aade13e6bbe71b9cec94a9cf29560013e387 [file] [log] [blame]
Kevin O'Connor0525d292008-07-04 06:18:30 -04001// smbios table generation (on emulators)
Kevin O'Connora4d35762008-03-08 15:43:03 -05002//
Kevin O'Connor2929c352009-07-25 13:48:27 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connor0525d292008-07-04 06:18:30 -04004// Copyright (C) 2006 Fabrice Bellard
Kevin O'Connora4d35762008-03-08 15:43:03 -05005//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connora4d35762008-03-08 15:43:03 -05007
Kevin O'Connorac8df8c2008-05-24 23:46:33 -04008#include "util.h" // dprintf
Kevin O'Connor01a85202009-10-18 09:49:59 -04009#include "paravirt.h" // qemu_cfg_smbios_load_field
10#include "smbios.h" // struct smbios_entry_point
Kevin O'Connor0525d292008-07-04 06:18:30 -040011
Kevin O'Connor83012de2011-08-28 12:42:15 -040012struct smbios_entry_point *SMBiosAddr;
13
Kevin O'Connora4d35762008-03-08 15:43:03 -050014static void
Kevin O'Connor2929c352009-07-25 13:48:27 -040015smbios_entry_point_init(u16 max_structure_size,
Kevin O'Connore51313d2008-03-12 21:19:34 -040016 u16 structure_table_length,
Kevin O'Connor2929c352009-07-25 13:48:27 -040017 void *structure_table_address,
Kevin O'Connore51313d2008-03-12 21:19:34 -040018 u16 number_of_structures)
Kevin O'Connora4d35762008-03-08 15:43:03 -050019{
Kevin O'Connor2929c352009-07-25 13:48:27 -040020 struct smbios_entry_point *ep = malloc_fseg(sizeof(*ep));
Kevin O'Connor32f03872011-08-03 20:45:32 -040021 void *finaltable;
22 if (structure_table_length <= BUILD_MAX_SMBIOS_FSEG)
23 // Table is small enough for f-seg - allocate there. This
24 // works around a bug in JunOS (at least for small SMBIOS tables).
25 finaltable = malloc_fseg(structure_table_length);
26 else
27 finaltable = malloc_high(structure_table_length);
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -050028 if (!ep || !finaltable) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050029 warn_noalloc();
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -050030 free(ep);
31 free(finaltable);
Kevin O'Connor2929c352009-07-25 13:48:27 -040032 return;
33 }
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -050034 memcpy(finaltable, structure_table_address, structure_table_length);
Kevin O'Connora4d35762008-03-08 15:43:03 -050035
36 memcpy(ep->anchor_string, "_SM_", 4);
37 ep->length = 0x1f;
38 ep->smbios_major_version = 2;
39 ep->smbios_minor_version = 4;
40 ep->max_structure_size = max_structure_size;
41 ep->entry_point_revision = 0;
42 memset(ep->formatted_area, 0, 5);
43 memcpy(ep->intermediate_anchor_string, "_DMI_", 5);
44
45 ep->structure_table_length = structure_table_length;
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -050046 ep->structure_table_address = (u32)finaltable;
Kevin O'Connora4d35762008-03-08 15:43:03 -050047 ep->number_of_structures = number_of_structures;
48 ep->smbios_bcd_revision = 0x24;
49
Kevin O'Connor2929c352009-07-25 13:48:27 -040050 ep->checksum -= checksum(ep, 0x10);
Kevin O'Connora4d35762008-03-08 15:43:03 -050051
Kevin O'Connor2929c352009-07-25 13:48:27 -040052 ep->intermediate_checksum -= checksum((void*)ep + 0x10, ep->length - 0x10);
53
Kevin O'Connor83012de2011-08-28 12:42:15 -040054 SMBiosAddr = ep;
Kevin O'Connor32f03872011-08-03 20:45:32 -040055 dprintf(1, "SMBIOS ptr=%p table=%p size=%d\n"
56 , ep, finaltable, structure_table_length);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -040057}
Kevin O'Connora4d35762008-03-08 15:43:03 -050058
Kevin O'Connor4e4b4102009-10-08 21:21:59 -040059#define load_str_field_with_default(type, field, def) \
60 do { \
61 size = qemu_cfg_smbios_load_field(type, \
62 offsetof(struct smbios_type_##type, \
63 field), end); \
64 if (size > 0) { \
65 end += size; \
66 } else { \
67 memcpy(end, def, sizeof(def)); \
68 end += sizeof(def); \
69 } \
70 p->field = ++str_index; \
71 } while (0)
72
Alex Williamson17d3e462010-06-21 09:46:19 -060073#define load_str_field_or_skip(type, field) \
Kevin O'Connor4e4b4102009-10-08 21:21:59 -040074 do { \
75 size = qemu_cfg_smbios_load_field(type, \
76 offsetof(struct smbios_type_##type, \
77 field), end); \
78 if (size > 0) { \
79 end += size; \
80 p->field = ++str_index; \
81 } else { \
82 p->field = 0; \
83 } \
84 } while (0)
85
Alex Williamson17d3e462010-06-21 09:46:19 -060086#define set_field_with_default(type, field, def) \
87 do { \
88 if (!qemu_cfg_smbios_load_field(type, \
89 offsetof(struct smbios_type_##type, \
90 field), &p->field)) { \
91 p->field = def; \
92 } \
93 } while (0)
94
Kevin O'Connora4d35762008-03-08 15:43:03 -050095/* Type 0 -- BIOS Information */
Gerd Hoffmann46ee8342012-06-07 10:34:31 +020096#define RELEASE_DATE_STR "01/01/2011"
Kevin O'Connora4d35762008-03-08 15:43:03 -050097static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -040098smbios_init_type_0(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -050099{
100 struct smbios_type_0 *p = (struct smbios_type_0 *)start;
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400101 char *end = (char *)start + sizeof(struct smbios_type_0);
102 size_t size;
103 int str_index = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500104
105 p->header.type = 0;
106 p->header.length = sizeof(struct smbios_type_0);
107 p->header.handle = 0;
108
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400109 load_str_field_with_default(0, vendor_str, CONFIG_APPNAME);
110 load_str_field_with_default(0, bios_version_str, CONFIG_APPNAME);
111
Kevin O'Connora4d35762008-03-08 15:43:03 -0500112 p->bios_starting_address_segment = 0xe800;
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400113
114 load_str_field_with_default(0, bios_release_date_str, RELEASE_DATE_STR);
115
Kevin O'Connora4d35762008-03-08 15:43:03 -0500116 p->bios_rom_size = 0; /* FIXME */
117
Alex Williamson17d3e462010-06-21 09:46:19 -0600118 if (!qemu_cfg_smbios_load_field(0, offsetof(struct smbios_type_0,
119 bios_characteristics),
120 &p->bios_characteristics)) {
121 memset(p->bios_characteristics, 0, 8);
122 /* BIOS characteristics not supported */
123 p->bios_characteristics[0] = 0x08;
124 }
Kevin O'Connora4d35762008-03-08 15:43:03 -0500125
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400126 if (!qemu_cfg_smbios_load_field(0, offsetof(struct smbios_type_0,
Alex Williamson17d3e462010-06-21 09:46:19 -0600127 bios_characteristics_extension_bytes),
128 &p->bios_characteristics_extension_bytes)) {
129 p->bios_characteristics_extension_bytes[0] = 0;
130 /* Enable targeted content distribution. Needed for SVVP */
131 p->bios_characteristics_extension_bytes[1] = 4;
132 }
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400133
Alex Williamson17d3e462010-06-21 09:46:19 -0600134 set_field_with_default(0, system_bios_major_release, 1);
135 set_field_with_default(0, system_bios_minor_release, 0);
136 set_field_with_default(0, embedded_controller_major_release, 0xff);
137 set_field_with_default(0, embedded_controller_minor_release, 0xff);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500138
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400139 *end = 0;
140 end++;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500141
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400142 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500143}
144
145/* Type 1 -- System Information */
146static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400147smbios_init_type_1(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500148{
149 struct smbios_type_1 *p = (struct smbios_type_1 *)start;
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400150 char *end = (char *)start + sizeof(struct smbios_type_1);
151 size_t size;
152 int str_index = 0;
153
Kevin O'Connora4d35762008-03-08 15:43:03 -0500154 p->header.type = 1;
155 p->header.length = sizeof(struct smbios_type_1);
156 p->header.handle = 0x100;
157
Kevin O'Connorcf2affa2009-12-10 20:13:57 -0500158 load_str_field_with_default(1, manufacturer_str, CONFIG_APPNAME);
159 load_str_field_with_default(1, product_name_str, CONFIG_APPNAME);
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400160 load_str_field_or_skip(1, version_str);
161 load_str_field_or_skip(1, serial_number_str);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500162
Alex Williamson17d3e462010-06-21 09:46:19 -0600163 if (!qemu_cfg_smbios_load_field(1, offsetof(struct smbios_type_1,
164 uuid), &p->uuid)) {
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400165 memset(p->uuid, 0, 16);
Alex Williamson17d3e462010-06-21 09:46:19 -0600166 }
Kevin O'Connora4d35762008-03-08 15:43:03 -0500167
Alex Williamson17d3e462010-06-21 09:46:19 -0600168 set_field_with_default(1, wake_up_type, 0x06); /* power switch */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500169
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400170 load_str_field_or_skip(1, sku_number_str);
171 load_str_field_or_skip(1, family_str);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500172
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400173 *end = 0;
174 end++;
175 if (!str_index) {
176 *end = 0;
177 end++;
178 }
179
180 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500181}
182
183/* Type 3 -- System Enclosure */
184static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400185smbios_init_type_3(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500186{
187 struct smbios_type_3 *p = (struct smbios_type_3 *)start;
Alex Williamson17d3e462010-06-21 09:46:19 -0600188 char *end = (char *)start + sizeof(struct smbios_type_3);
189 size_t size;
190 int str_index = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500191
192 p->header.type = 3;
193 p->header.length = sizeof(struct smbios_type_3);
194 p->header.handle = 0x300;
195
Alex Williamson17d3e462010-06-21 09:46:19 -0600196 load_str_field_with_default(3, manufacturer_str, CONFIG_APPNAME);
197 set_field_with_default(3, type, 0x01); /* other */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500198
Alex Williamson17d3e462010-06-21 09:46:19 -0600199 load_str_field_or_skip(3, version_str);
200 load_str_field_or_skip(3, serial_number_str);
201 load_str_field_or_skip(3, asset_tag_number_str);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500202
Alex Williamson17d3e462010-06-21 09:46:19 -0600203 set_field_with_default(3, boot_up_state, 0x03); /* safe */
204 set_field_with_default(3, power_supply_state, 0x03); /* safe */
205 set_field_with_default(3, thermal_state, 0x03); /* safe */
206 set_field_with_default(3, security_status, 0x02); /* unknown */
207
208 set_field_with_default(3, oem_defined, 0);
209 set_field_with_default(3, height, 0);
210 set_field_with_default(3, number_of_power_cords, 0);
211 set_field_with_default(3, contained_element_count, 0);
212
213 *end = 0;
214 end++;
215 if (!str_index) {
216 *end = 0;
217 end++;
218 }
219
220 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500221}
222
223/* Type 4 -- Processor Information */
224static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400225smbios_init_type_4(void *start, unsigned int cpu_number)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500226{
227 struct smbios_type_4 *p = (struct smbios_type_4 *)start;
Alex Williamson17d3e462010-06-21 09:46:19 -0600228 char *end = (char *)start + sizeof(struct smbios_type_4);
229 size_t size;
230 int str_index = 0;
231 char name[1024];
Kevin O'Connora4d35762008-03-08 15:43:03 -0500232
233 p->header.type = 4;
234 p->header.length = sizeof(struct smbios_type_4);
235 p->header.handle = 0x400 + cpu_number;
236
Alex Williamson17d3e462010-06-21 09:46:19 -0600237 size = qemu_cfg_smbios_load_field(4, offsetof(struct smbios_type_4,
238 socket_designation_str),
239 name);
240 if (size)
241 snprintf(name + size - 1, sizeof(name) - size, "%2x", cpu_number);
242 else
243 snprintf(name, sizeof(name), "CPU%2x", cpu_number);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500244
Alex Williamson17d3e462010-06-21 09:46:19 -0600245 memcpy(end, name, strlen(name) + 1);
246 end += strlen(name) + 1;
247 p->socket_designation_str = ++str_index;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500248
Alex Williamson17d3e462010-06-21 09:46:19 -0600249 set_field_with_default(4, processor_type, 0x03); /* CPU */
250 set_field_with_default(4, processor_family, 0x01); /* other */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500251
Alex Williamson17d3e462010-06-21 09:46:19 -0600252 load_str_field_with_default(4, processor_manufacturer_str, CONFIG_APPNAME);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500253
Alex Williamson17d3e462010-06-21 09:46:19 -0600254 if (!qemu_cfg_smbios_load_field(4, offsetof(struct smbios_type_4,
255 processor_id), p->processor_id)) {
256 u32 cpuid_signature, ebx, ecx, cpuid_features;
257 cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features);
258 p->processor_id[0] = cpuid_signature;
259 p->processor_id[1] = cpuid_features;
260 }
Kevin O'Connora4d35762008-03-08 15:43:03 -0500261
Alex Williamson17d3e462010-06-21 09:46:19 -0600262 load_str_field_or_skip(4, processor_version_str);
263 set_field_with_default(4, voltage, 0);
264 set_field_with_default(4, external_clock, 0);
Kevin O'Connorf2ce1912008-10-25 15:23:53 -0400265
Alex Williamson17d3e462010-06-21 09:46:19 -0600266 set_field_with_default(4, max_speed, 2000);
267 set_field_with_default(4, current_speed, 2000);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500268
Alex Williamson17d3e462010-06-21 09:46:19 -0600269 set_field_with_default(4, status, 0x41); /* socket populated, CPU enabled */
270 set_field_with_default(4, processor_upgrade, 0x01); /* other */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500271
Alex Williamson17d3e462010-06-21 09:46:19 -0600272 /* cache information structure not provided */
273 p->l1_cache_handle = 0xffff;
274 p->l2_cache_handle = 0xffff;
275 p->l3_cache_handle = 0xffff;
276
277 *end = 0;
278 end++;
279 if (!str_index) {
280 *end = 0;
281 end++;
282 }
283
284 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500285}
286
287/* Type 16 -- Physical Memory Array */
288static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400289smbios_init_type_16(void *start, u32 memory_size_mb, int nr_mem_devs)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500290{
291 struct smbios_type_16 *p = (struct smbios_type_16*)start;
292
293 p->header.type = 16;
294 p->header.length = sizeof(struct smbios_type_16);
295 p->header.handle = 0x1000;
296
Alex Williamson17d3e462010-06-21 09:46:19 -0600297 set_field_with_default(16, location, 0x01); /* other */
298 set_field_with_default(16, use, 0x03); /* system memory */
299 /* Multi-bit ECC to make Microsoft happy */
300 set_field_with_default(16, error_correction, 0x06);
Alex Williamson6d663162010-05-07 13:38:55 -0600301 /* 0x80000000 = unknown, accept sizes < 2TB - TODO multiple arrays */
302 p->maximum_capacity = memory_size_mb < 2 << 20 ?
303 memory_size_mb << 10 : 0x80000000;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500304 p->memory_error_information_handle = 0xfffe; /* none provided */
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400305 p->number_of_memory_devices = nr_mem_devs;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500306
307 start += sizeof(struct smbios_type_16);
Kevin O'Connore51313d2008-03-12 21:19:34 -0400308 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500309
310 return start + 2;
311}
312
313/* Type 17 -- Memory Device */
314static void *
Alex Williamson6d663162010-05-07 13:38:55 -0600315smbios_init_type_17(void *start, u32 size_mb, int instance)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500316{
317 struct smbios_type_17 *p = (struct smbios_type_17 *)start;
Alex Williamson17d3e462010-06-21 09:46:19 -0600318 char *end = (char *)start + sizeof(struct smbios_type_17);
319 size_t size;
320 int str_index = 0;
321 char name[1024];
Kevin O'Connora4d35762008-03-08 15:43:03 -0500322
323 p->header.type = 17;
324 p->header.length = sizeof(struct smbios_type_17);
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400325 p->header.handle = 0x1100 + instance;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500326
327 p->physical_memory_array_handle = 0x1000;
Alex Williamson17d3e462010-06-21 09:46:19 -0600328 set_field_with_default(17, total_width, 64);
329 set_field_with_default(17, data_width, 64);
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400330/* TODO: should assert in case something is wrong ASSERT((memory_size_mb & ~0x7fff) == 0); */
Alex Williamson6d663162010-05-07 13:38:55 -0600331 p->size = size_mb;
Alex Williamson17d3e462010-06-21 09:46:19 -0600332 set_field_with_default(17, form_factor, 0x09); /* DIMM */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500333 p->device_set = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500334
Alex Williamson17d3e462010-06-21 09:46:19 -0600335 size = qemu_cfg_smbios_load_field(17, offsetof(struct smbios_type_17,
336 device_locator_str),
337 name);
338 if (size)
339 snprintf(name + size - 1, sizeof(name) - size, "%d", instance);
340 else
341 snprintf(name, sizeof(name), "DIMM %d", instance);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500342
Alex Williamson17d3e462010-06-21 09:46:19 -0600343 memcpy(end, name, strlen(name) + 1);
344 end += strlen(name) + 1;
345 p->device_locator_str = ++str_index;
346
347 load_str_field_or_skip(17, bank_locator_str);
348 set_field_with_default(17, memory_type, 0x07); /* RAM */
349 set_field_with_default(17, type_detail, 0);
350
351 *end = 0;
352 end++;
353 if (!str_index) {
354 *end = 0;
355 end++;
356 }
357
358 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500359}
360
361/* Type 19 -- Memory Array Mapped Address */
362static void *
Alex Williamson6d663162010-05-07 13:38:55 -0600363smbios_init_type_19(void *start, u32 start_mb, u32 size_mb, int instance)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500364{
365 struct smbios_type_19 *p = (struct smbios_type_19 *)start;
366
367 p->header.type = 19;
368 p->header.length = sizeof(struct smbios_type_19);
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400369 p->header.handle = 0x1300 + instance;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500370
Alex Williamson6d663162010-05-07 13:38:55 -0600371 p->starting_address = start_mb << 10;
372 p->ending_address = p->starting_address + (size_mb << 10) - 1;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500373 p->memory_array_handle = 0x1000;
374 p->partition_width = 1;
375
376 start += sizeof(struct smbios_type_19);
Kevin O'Connore51313d2008-03-12 21:19:34 -0400377 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500378
379 return start + 2;
380}
381
382/* Type 20 -- Memory Device Mapped Address */
383static void *
Alex Williamson6d663162010-05-07 13:38:55 -0600384smbios_init_type_20(void *start, u32 start_mb, u32 size_mb, int instance,
385 int dev_handle, int array_handle)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500386{
387 struct smbios_type_20 *p = (struct smbios_type_20 *)start;
388
389 p->header.type = 20;
390 p->header.length = sizeof(struct smbios_type_20);
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400391 p->header.handle = 0x1400 + instance;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500392
Alex Williamson6d663162010-05-07 13:38:55 -0600393 p->starting_address = start_mb << 10;
394 p->ending_address = p->starting_address + (size_mb << 10) - 1;
395 p->memory_device_handle = 0x1100 + dev_handle;
396 p->memory_array_mapped_address_handle = 0x1300 + array_handle;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500397 p->partition_row_position = 1;
398 p->interleave_position = 0;
399 p->interleaved_data_depth = 0;
400
401 start += sizeof(struct smbios_type_20);
402
Kevin O'Connore51313d2008-03-12 21:19:34 -0400403 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500404 return start+2;
405}
406
407/* Type 32 -- System Boot Information */
408static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400409smbios_init_type_32(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500410{
411 struct smbios_type_32 *p = (struct smbios_type_32 *)start;
412
413 p->header.type = 32;
414 p->header.length = sizeof(struct smbios_type_32);
415 p->header.handle = 0x2000;
416 memset(p->reserved, 0, 6);
Alex Williamson17d3e462010-06-21 09:46:19 -0600417 set_field_with_default(32, boot_status, 0); /* no errors detected */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500418
419 start += sizeof(struct smbios_type_32);
Kevin O'Connore51313d2008-03-12 21:19:34 -0400420 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500421
422 return start+2;
423}
424
425/* Type 127 -- End of Table */
426static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400427smbios_init_type_127(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500428{
429 struct smbios_type_127 *p = (struct smbios_type_127 *)start;
430
431 p->header.type = 127;
432 p->header.length = sizeof(struct smbios_type_127);
433 p->header.handle = 0x7f00;
434
435 start += sizeof(struct smbios_type_127);
Kevin O'Connore51313d2008-03-12 21:19:34 -0400436 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500437
438 return start + 2;
439}
440
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -0500441#define TEMPSMBIOSSIZE (32 * 1024)
442
Kevin O'Connor0525d292008-07-04 06:18:30 -0400443void
444smbios_init(void)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500445{
Kevin O'Connor6cb8ba92008-08-17 11:03:24 -0400446 if (! CONFIG_SMBIOS)
447 return;
448
Kevin O'Connor7b49cd92008-11-08 10:35:26 -0500449 dprintf(3, "init SMBIOS tables\n");
450
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -0500451 char *start = malloc_tmphigh(TEMPSMBIOSSIZE);
Kevin O'Connor2929c352009-07-25 13:48:27 -0400452 if (! start) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500453 warn_noalloc();
Kevin O'Connor2929c352009-07-25 13:48:27 -0400454 return;
455 }
Kevin O'Connora4d35762008-03-08 15:43:03 -0500456
Kevin O'Connor2929c352009-07-25 13:48:27 -0400457 u32 nr_structs = 0, max_struct_size = 0;
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -0500458 char *q, *p = start;
459 char *end = start + TEMPSMBIOSSIZE - sizeof(struct smbios_type_127);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500460
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400461#define add_struct(type, args...) \
462 do { \
463 if (!qemu_cfg_smbios_load_external(type, &p, &nr_structs, \
464 &max_struct_size, end)) { \
465 q = smbios_init_type_##type(args); \
466 nr_structs++; \
467 if ((q - p) > max_struct_size) \
468 max_struct_size = q - p; \
469 p = q; \
470 } \
471 } while (0)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500472
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400473 add_struct(0, p);
474 add_struct(1, p);
475 add_struct(3, p);
476
Kevin O'Connora26df9b2009-10-09 09:42:11 -0400477 int cpu_num;
478 for (cpu_num = 1; cpu_num <= MaxCountCPUs; cpu_num++)
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400479 add_struct(4, p, cpu_num);
Alex Williamson6d663162010-05-07 13:38:55 -0600480
481 int ram_mb = (RamSize + RamSizeOver4G) >> 20;
482 int nr_mem_devs = (ram_mb + 0x3fff) >> 14;
483 add_struct(16, p, ram_mb, nr_mem_devs);
484
485 int i, j;
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400486 for (i = 0; i < nr_mem_devs; i++) {
Alex Williamson6d663162010-05-07 13:38:55 -0600487 u32 dev_mb = ((i == (nr_mem_devs - 1))
488 ? (((ram_mb - 1) & 0x3fff) + 1)
489 : 16384);
490 add_struct(17, p, dev_mb, i);
491 }
Kevin O'Connor7d09d0e2010-05-10 21:51:38 -0400492
Alex Williamson6d663162010-05-07 13:38:55 -0600493 add_struct(19, p, 0, RamSize >> 20, 0);
494 if (RamSizeOver4G)
495 add_struct(19, p, 4096, RamSizeOver4G >> 20, 1);
496
497 add_struct(20, p, 0, RamSize >> 20, 0, 0, 0);
498 if (RamSizeOver4G) {
499 u32 start_mb = 4096;
500 for (j = 1, i = 0; i < nr_mem_devs; i++, j++) {
501 u32 dev_mb = ((i == (nr_mem_devs - 1))
502 ? (((ram_mb - 1) & 0x3fff) + 1)
503 : 16384);
504 if (i == 0)
505 dev_mb -= RamSize >> 20;
506
507 add_struct(20, p, start_mb, dev_mb, j, i, 1);
508 start_mb += dev_mb;
509 }
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400510 }
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400511
512 add_struct(32, p);
513 /* Add any remaining provided entries before the end marker */
514 for (i = 0; i < 256; i++)
515 qemu_cfg_smbios_load_external(i, &p, &nr_structs, &max_struct_size,
516 end);
517 add_struct(127, p);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500518
519#undef add_struct
520
Kevin O'Connor2929c352009-07-25 13:48:27 -0400521 smbios_entry_point_init(max_struct_size, p - start, start, nr_structs);
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -0500522 free(start);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500523}