blob: fd63afb5ad1b08828a78f45a0a320e37a07ecf32 [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'Connorde9e7052013-02-09 19:09:20 -05009#include "config.h" // CONFIG_*
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -050010#include "paravirt.h" // RamSize
Kevin O'Connor01a85202009-10-18 09:49:59 -040011#include "smbios.h" // struct smbios_entry_point
Kevin O'Connor0525d292008-07-04 06:18:30 -040012
Kevin O'Connor83012de2011-08-28 12:42:15 -040013struct smbios_entry_point *SMBiosAddr;
14
Kevin O'Connora4d35762008-03-08 15:43:03 -050015static void
Kevin O'Connord83c87b2013-01-21 01:14:12 -050016smbios_entry_point_setup(u16 max_structure_size,
17 u16 structure_table_length,
18 void *structure_table_address,
19 u16 number_of_structures)
Kevin O'Connora4d35762008-03-08 15:43:03 -050020{
Kevin O'Connor2929c352009-07-25 13:48:27 -040021 struct smbios_entry_point *ep = malloc_fseg(sizeof(*ep));
Kevin O'Connor32f03872011-08-03 20:45:32 -040022 void *finaltable;
23 if (structure_table_length <= BUILD_MAX_SMBIOS_FSEG)
24 // Table is small enough for f-seg - allocate there. This
25 // works around a bug in JunOS (at least for small SMBIOS tables).
26 finaltable = malloc_fseg(structure_table_length);
27 else
28 finaltable = malloc_high(structure_table_length);
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -050029 if (!ep || !finaltable) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -050030 warn_noalloc();
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -050031 free(ep);
32 free(finaltable);
Kevin O'Connor2929c352009-07-25 13:48:27 -040033 return;
34 }
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -050035 memcpy(finaltable, structure_table_address, structure_table_length);
Kevin O'Connora4d35762008-03-08 15:43:03 -050036
37 memcpy(ep->anchor_string, "_SM_", 4);
38 ep->length = 0x1f;
39 ep->smbios_major_version = 2;
40 ep->smbios_minor_version = 4;
41 ep->max_structure_size = max_structure_size;
42 ep->entry_point_revision = 0;
43 memset(ep->formatted_area, 0, 5);
44 memcpy(ep->intermediate_anchor_string, "_DMI_", 5);
45
46 ep->structure_table_length = structure_table_length;
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -050047 ep->structure_table_address = (u32)finaltable;
Kevin O'Connora4d35762008-03-08 15:43:03 -050048 ep->number_of_structures = number_of_structures;
49 ep->smbios_bcd_revision = 0x24;
50
Kevin O'Connor2929c352009-07-25 13:48:27 -040051 ep->checksum -= checksum(ep, 0x10);
Kevin O'Connora4d35762008-03-08 15:43:03 -050052
Kevin O'Connor2929c352009-07-25 13:48:27 -040053 ep->intermediate_checksum -= checksum((void*)ep + 0x10, ep->length - 0x10);
54
Kevin O'Connor83012de2011-08-28 12:42:15 -040055 SMBiosAddr = ep;
Kevin O'Connor32f03872011-08-03 20:45:32 -040056 dprintf(1, "SMBIOS ptr=%p table=%p size=%d\n"
57 , ep, finaltable, structure_table_length);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -040058}
Kevin O'Connora4d35762008-03-08 15:43:03 -050059
Kevin O'Connorde9e7052013-02-09 19:09:20 -050060static int
61get_field(int type, int offset, void *dest)
62{
63 char name[128];
64 snprintf(name, sizeof(name), "smbios/field%d-%d", type, offset);
65 struct romfile_s *file = romfile_find(name);
66 if (!file)
67 return 0;
68 file->copy(file, dest, file->size);
69 return file->size;
70}
71
72static int
73get_external(int type, char **p, unsigned *nr_structs,
74 unsigned *max_struct_size, char *end)
75{
76 static u64 used_bitmap[4] = { 0 };
77 char *start = *p;
78
79 /* Check if we've already reported these tables */
80 if (used_bitmap[(type >> 6) & 0x3] & (1ULL << (type & 0x3f)))
81 return 1;
82
83 /* Don't introduce spurious end markers */
84 if (type == 127)
85 return 0;
86
87 char prefix[128];
88 snprintf(prefix, sizeof(prefix), "smbios/table%d-", type);
89 struct romfile_s *file = NULL;
90 for (;;) {
91 file = romfile_findprefix(prefix, file);
92 if (!file)
93 break;
94
95 if (end - *p < file->size) {
96 warn_noalloc();
97 break;
98 }
99
100 struct smbios_structure_header *header = (void*)*p;
101 file->copy(file, header, file->size);
102 *p += file->size;
103
104 /* Entries end with a double NULL char, if there's a string at
105 * the end (length is greater than formatted length), the string
106 * terminator provides the first NULL. */
107 *((u8*)*p) = 0;
108 (*p)++;
109 if (header->length >= file->size) {
110 *((u8*)*p) = 0;
111 (*p)++;
112 }
113
114 (*nr_structs)++;
115 if (*p - (char*)header > *max_struct_size)
116 *max_struct_size = *p - (char*)header;
117 }
118
119 if (start == *p)
120 return 0;
121
122 /* Mark that we've reported on this type */
123 used_bitmap[(type >> 6) & 0x3] |= (1ULL << (type & 0x3f));
124 return 1;
125}
126
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400127#define load_str_field_with_default(type, field, def) \
128 do { \
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500129 size = get_field(type, offsetof(struct smbios_type_##type, \
130 field), end); \
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400131 if (size > 0) { \
132 end += size; \
133 } else { \
134 memcpy(end, def, sizeof(def)); \
135 end += sizeof(def); \
136 } \
137 p->field = ++str_index; \
138 } while (0)
139
Alex Williamson17d3e462010-06-21 09:46:19 -0600140#define load_str_field_or_skip(type, field) \
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400141 do { \
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500142 size = get_field(type, offsetof(struct smbios_type_##type, \
143 field), end); \
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400144 if (size > 0) { \
145 end += size; \
146 p->field = ++str_index; \
147 } else { \
148 p->field = 0; \
149 } \
150 } while (0)
151
Alex Williamson17d3e462010-06-21 09:46:19 -0600152#define set_field_with_default(type, field, def) \
153 do { \
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500154 if (!get_field(type, offsetof(struct smbios_type_##type, \
155 field), &p->field)) { \
Alex Williamson17d3e462010-06-21 09:46:19 -0600156 p->field = def; \
157 } \
158 } while (0)
159
Kevin O'Connora4d35762008-03-08 15:43:03 -0500160/* Type 0 -- BIOS Information */
Gerd Hoffmann46ee8342012-06-07 10:34:31 +0200161#define RELEASE_DATE_STR "01/01/2011"
Kevin O'Connora4d35762008-03-08 15:43:03 -0500162static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400163smbios_init_type_0(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500164{
165 struct smbios_type_0 *p = (struct smbios_type_0 *)start;
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400166 char *end = (char *)start + sizeof(struct smbios_type_0);
167 size_t size;
168 int str_index = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500169
170 p->header.type = 0;
171 p->header.length = sizeof(struct smbios_type_0);
172 p->header.handle = 0;
173
Kevin O'Connore52ad392013-02-20 23:48:22 -0500174 load_str_field_with_default(0, vendor_str, BUILD_APPNAME);
175 load_str_field_with_default(0, bios_version_str, BUILD_APPNAME);
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400176
Kevin O'Connora4d35762008-03-08 15:43:03 -0500177 p->bios_starting_address_segment = 0xe800;
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400178
179 load_str_field_with_default(0, bios_release_date_str, RELEASE_DATE_STR);
180
Kevin O'Connora4d35762008-03-08 15:43:03 -0500181 p->bios_rom_size = 0; /* FIXME */
182
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500183 if (!get_field(0, offsetof(struct smbios_type_0, bios_characteristics),
184 &p->bios_characteristics)) {
Alex Williamson17d3e462010-06-21 09:46:19 -0600185 memset(p->bios_characteristics, 0, 8);
186 /* BIOS characteristics not supported */
187 p->bios_characteristics[0] = 0x08;
188 }
Kevin O'Connora4d35762008-03-08 15:43:03 -0500189
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500190 if (!get_field(0, offsetof(struct smbios_type_0,
191 bios_characteristics_extension_bytes),
192 &p->bios_characteristics_extension_bytes)) {
Alex Williamson17d3e462010-06-21 09:46:19 -0600193 p->bios_characteristics_extension_bytes[0] = 0;
194 /* Enable targeted content distribution. Needed for SVVP */
195 p->bios_characteristics_extension_bytes[1] = 4;
196 }
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400197
Alex Williamson17d3e462010-06-21 09:46:19 -0600198 set_field_with_default(0, system_bios_major_release, 1);
199 set_field_with_default(0, system_bios_minor_release, 0);
200 set_field_with_default(0, embedded_controller_major_release, 0xff);
201 set_field_with_default(0, embedded_controller_minor_release, 0xff);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500202
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400203 *end = 0;
204 end++;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500205
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400206 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500207}
208
209/* Type 1 -- System Information */
210static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400211smbios_init_type_1(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500212{
213 struct smbios_type_1 *p = (struct smbios_type_1 *)start;
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400214 char *end = (char *)start + sizeof(struct smbios_type_1);
215 size_t size;
216 int str_index = 0;
217
Kevin O'Connora4d35762008-03-08 15:43:03 -0500218 p->header.type = 1;
219 p->header.length = sizeof(struct smbios_type_1);
220 p->header.handle = 0x100;
221
Kevin O'Connore52ad392013-02-20 23:48:22 -0500222 load_str_field_with_default(1, manufacturer_str, BUILD_APPNAME);
223 load_str_field_with_default(1, product_name_str, BUILD_APPNAME);
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400224 load_str_field_or_skip(1, version_str);
225 load_str_field_or_skip(1, serial_number_str);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500226
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500227 if (!get_field(1, offsetof(struct smbios_type_1, uuid), &p->uuid))
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400228 memset(p->uuid, 0, 16);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500229
Alex Williamson17d3e462010-06-21 09:46:19 -0600230 set_field_with_default(1, wake_up_type, 0x06); /* power switch */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500231
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400232 load_str_field_or_skip(1, sku_number_str);
233 load_str_field_or_skip(1, family_str);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500234
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400235 *end = 0;
236 end++;
237 if (!str_index) {
238 *end = 0;
239 end++;
240 }
241
242 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500243}
244
245/* Type 3 -- System Enclosure */
246static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400247smbios_init_type_3(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500248{
249 struct smbios_type_3 *p = (struct smbios_type_3 *)start;
Alex Williamson17d3e462010-06-21 09:46:19 -0600250 char *end = (char *)start + sizeof(struct smbios_type_3);
251 size_t size;
252 int str_index = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500253
254 p->header.type = 3;
255 p->header.length = sizeof(struct smbios_type_3);
256 p->header.handle = 0x300;
257
Kevin O'Connore52ad392013-02-20 23:48:22 -0500258 load_str_field_with_default(3, manufacturer_str, BUILD_APPNAME);
Alex Williamson17d3e462010-06-21 09:46:19 -0600259 set_field_with_default(3, type, 0x01); /* other */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500260
Alex Williamson17d3e462010-06-21 09:46:19 -0600261 load_str_field_or_skip(3, version_str);
262 load_str_field_or_skip(3, serial_number_str);
263 load_str_field_or_skip(3, asset_tag_number_str);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500264
Alex Williamson17d3e462010-06-21 09:46:19 -0600265 set_field_with_default(3, boot_up_state, 0x03); /* safe */
266 set_field_with_default(3, power_supply_state, 0x03); /* safe */
267 set_field_with_default(3, thermal_state, 0x03); /* safe */
268 set_field_with_default(3, security_status, 0x02); /* unknown */
269
270 set_field_with_default(3, oem_defined, 0);
271 set_field_with_default(3, height, 0);
272 set_field_with_default(3, number_of_power_cords, 0);
273 set_field_with_default(3, contained_element_count, 0);
274
275 *end = 0;
276 end++;
277 if (!str_index) {
278 *end = 0;
279 end++;
280 }
281
282 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500283}
284
285/* Type 4 -- Processor Information */
286static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400287smbios_init_type_4(void *start, unsigned int cpu_number)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500288{
289 struct smbios_type_4 *p = (struct smbios_type_4 *)start;
Alex Williamson17d3e462010-06-21 09:46:19 -0600290 char *end = (char *)start + sizeof(struct smbios_type_4);
291 size_t size;
292 int str_index = 0;
293 char name[1024];
Kevin O'Connora4d35762008-03-08 15:43:03 -0500294
295 p->header.type = 4;
296 p->header.length = sizeof(struct smbios_type_4);
297 p->header.handle = 0x400 + cpu_number;
298
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500299 size = get_field(4, offsetof(struct smbios_type_4, socket_designation_str),
300 name);
Alex Williamson17d3e462010-06-21 09:46:19 -0600301 if (size)
302 snprintf(name + size - 1, sizeof(name) - size, "%2x", cpu_number);
303 else
304 snprintf(name, sizeof(name), "CPU%2x", cpu_number);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500305
Alex Williamson17d3e462010-06-21 09:46:19 -0600306 memcpy(end, name, strlen(name) + 1);
307 end += strlen(name) + 1;
308 p->socket_designation_str = ++str_index;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500309
Alex Williamson17d3e462010-06-21 09:46:19 -0600310 set_field_with_default(4, processor_type, 0x03); /* CPU */
311 set_field_with_default(4, processor_family, 0x01); /* other */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500312
Kevin O'Connore52ad392013-02-20 23:48:22 -0500313 load_str_field_with_default(4, processor_manufacturer_str, BUILD_APPNAME);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500314
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500315 if (!get_field(4, offsetof(struct smbios_type_4, processor_id)
316 , p->processor_id)) {
Alex Williamson17d3e462010-06-21 09:46:19 -0600317 u32 cpuid_signature, ebx, ecx, cpuid_features;
318 cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features);
319 p->processor_id[0] = cpuid_signature;
320 p->processor_id[1] = cpuid_features;
321 }
Kevin O'Connora4d35762008-03-08 15:43:03 -0500322
Alex Williamson17d3e462010-06-21 09:46:19 -0600323 load_str_field_or_skip(4, processor_version_str);
324 set_field_with_default(4, voltage, 0);
325 set_field_with_default(4, external_clock, 0);
Kevin O'Connorf2ce1912008-10-25 15:23:53 -0400326
Alex Williamson17d3e462010-06-21 09:46:19 -0600327 set_field_with_default(4, max_speed, 2000);
328 set_field_with_default(4, current_speed, 2000);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500329
Alex Williamson17d3e462010-06-21 09:46:19 -0600330 set_field_with_default(4, status, 0x41); /* socket populated, CPU enabled */
331 set_field_with_default(4, processor_upgrade, 0x01); /* other */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500332
Alex Williamson17d3e462010-06-21 09:46:19 -0600333 /* cache information structure not provided */
334 p->l1_cache_handle = 0xffff;
335 p->l2_cache_handle = 0xffff;
336 p->l3_cache_handle = 0xffff;
337
338 *end = 0;
339 end++;
340 if (!str_index) {
341 *end = 0;
342 end++;
343 }
344
345 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500346}
347
348/* Type 16 -- Physical Memory Array */
349static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400350smbios_init_type_16(void *start, u32 memory_size_mb, int nr_mem_devs)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500351{
352 struct smbios_type_16 *p = (struct smbios_type_16*)start;
353
354 p->header.type = 16;
355 p->header.length = sizeof(struct smbios_type_16);
356 p->header.handle = 0x1000;
357
Alex Williamson17d3e462010-06-21 09:46:19 -0600358 set_field_with_default(16, location, 0x01); /* other */
359 set_field_with_default(16, use, 0x03); /* system memory */
360 /* Multi-bit ECC to make Microsoft happy */
361 set_field_with_default(16, error_correction, 0x06);
Alex Williamson6d663162010-05-07 13:38:55 -0600362 /* 0x80000000 = unknown, accept sizes < 2TB - TODO multiple arrays */
363 p->maximum_capacity = memory_size_mb < 2 << 20 ?
364 memory_size_mb << 10 : 0x80000000;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500365 p->memory_error_information_handle = 0xfffe; /* none provided */
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400366 p->number_of_memory_devices = nr_mem_devs;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500367
368 start += sizeof(struct smbios_type_16);
Kevin O'Connore51313d2008-03-12 21:19:34 -0400369 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500370
371 return start + 2;
372}
373
374/* Type 17 -- Memory Device */
375static void *
Alex Williamson6d663162010-05-07 13:38:55 -0600376smbios_init_type_17(void *start, u32 size_mb, int instance)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500377{
378 struct smbios_type_17 *p = (struct smbios_type_17 *)start;
Alex Williamson17d3e462010-06-21 09:46:19 -0600379 char *end = (char *)start + sizeof(struct smbios_type_17);
380 size_t size;
381 int str_index = 0;
382 char name[1024];
Kevin O'Connora4d35762008-03-08 15:43:03 -0500383
384 p->header.type = 17;
385 p->header.length = sizeof(struct smbios_type_17);
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400386 p->header.handle = 0x1100 + instance;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500387
388 p->physical_memory_array_handle = 0x1000;
Alex Williamson17d3e462010-06-21 09:46:19 -0600389 set_field_with_default(17, total_width, 64);
390 set_field_with_default(17, data_width, 64);
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400391/* TODO: should assert in case something is wrong ASSERT((memory_size_mb & ~0x7fff) == 0); */
Alex Williamson6d663162010-05-07 13:38:55 -0600392 p->size = size_mb;
Alex Williamson17d3e462010-06-21 09:46:19 -0600393 set_field_with_default(17, form_factor, 0x09); /* DIMM */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500394 p->device_set = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500395
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500396 size = get_field(17, offsetof(struct smbios_type_17, device_locator_str),
397 name);
Alex Williamson17d3e462010-06-21 09:46:19 -0600398 if (size)
399 snprintf(name + size - 1, sizeof(name) - size, "%d", instance);
400 else
401 snprintf(name, sizeof(name), "DIMM %d", instance);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500402
Alex Williamson17d3e462010-06-21 09:46:19 -0600403 memcpy(end, name, strlen(name) + 1);
404 end += strlen(name) + 1;
405 p->device_locator_str = ++str_index;
406
407 load_str_field_or_skip(17, bank_locator_str);
408 set_field_with_default(17, memory_type, 0x07); /* RAM */
409 set_field_with_default(17, type_detail, 0);
410
411 *end = 0;
412 end++;
413 if (!str_index) {
414 *end = 0;
415 end++;
416 }
417
418 return end;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500419}
420
421/* Type 19 -- Memory Array Mapped Address */
422static void *
Alex Williamson6d663162010-05-07 13:38:55 -0600423smbios_init_type_19(void *start, u32 start_mb, u32 size_mb, int instance)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500424{
425 struct smbios_type_19 *p = (struct smbios_type_19 *)start;
426
427 p->header.type = 19;
428 p->header.length = sizeof(struct smbios_type_19);
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400429 p->header.handle = 0x1300 + instance;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500430
Alex Williamson6d663162010-05-07 13:38:55 -0600431 p->starting_address = start_mb << 10;
432 p->ending_address = p->starting_address + (size_mb << 10) - 1;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500433 p->memory_array_handle = 0x1000;
434 p->partition_width = 1;
435
436 start += sizeof(struct smbios_type_19);
Kevin O'Connore51313d2008-03-12 21:19:34 -0400437 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500438
439 return start + 2;
440}
441
442/* Type 20 -- Memory Device Mapped Address */
443static void *
Alex Williamson6d663162010-05-07 13:38:55 -0600444smbios_init_type_20(void *start, u32 start_mb, u32 size_mb, int instance,
445 int dev_handle, int array_handle)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500446{
447 struct smbios_type_20 *p = (struct smbios_type_20 *)start;
448
449 p->header.type = 20;
450 p->header.length = sizeof(struct smbios_type_20);
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400451 p->header.handle = 0x1400 + instance;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500452
Alex Williamson6d663162010-05-07 13:38:55 -0600453 p->starting_address = start_mb << 10;
454 p->ending_address = p->starting_address + (size_mb << 10) - 1;
455 p->memory_device_handle = 0x1100 + dev_handle;
456 p->memory_array_mapped_address_handle = 0x1300 + array_handle;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500457 p->partition_row_position = 1;
458 p->interleave_position = 0;
459 p->interleaved_data_depth = 0;
460
461 start += sizeof(struct smbios_type_20);
462
Kevin O'Connore51313d2008-03-12 21:19:34 -0400463 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500464 return start+2;
465}
466
467/* Type 32 -- System Boot Information */
468static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400469smbios_init_type_32(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500470{
471 struct smbios_type_32 *p = (struct smbios_type_32 *)start;
472
473 p->header.type = 32;
474 p->header.length = sizeof(struct smbios_type_32);
475 p->header.handle = 0x2000;
476 memset(p->reserved, 0, 6);
Alex Williamson17d3e462010-06-21 09:46:19 -0600477 set_field_with_default(32, boot_status, 0); /* no errors detected */
Kevin O'Connora4d35762008-03-08 15:43:03 -0500478
479 start += sizeof(struct smbios_type_32);
Kevin O'Connore51313d2008-03-12 21:19:34 -0400480 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500481
482 return start+2;
483}
484
485/* Type 127 -- End of Table */
486static void *
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400487smbios_init_type_127(void *start)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500488{
489 struct smbios_type_127 *p = (struct smbios_type_127 *)start;
490
491 p->header.type = 127;
492 p->header.length = sizeof(struct smbios_type_127);
493 p->header.handle = 0x7f00;
494
495 start += sizeof(struct smbios_type_127);
Kevin O'Connore51313d2008-03-12 21:19:34 -0400496 *((u16 *)start) = 0;
Kevin O'Connora4d35762008-03-08 15:43:03 -0500497
498 return start + 2;
499}
500
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -0500501#define TEMPSMBIOSSIZE (32 * 1024)
502
Kevin O'Connor0525d292008-07-04 06:18:30 -0400503void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500504smbios_setup(void)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500505{
Kevin O'Connor6cb8ba92008-08-17 11:03:24 -0400506 if (! CONFIG_SMBIOS)
507 return;
508
Kevin O'Connor7b49cd92008-11-08 10:35:26 -0500509 dprintf(3, "init SMBIOS tables\n");
510
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -0500511 char *start = malloc_tmphigh(TEMPSMBIOSSIZE);
Kevin O'Connor2929c352009-07-25 13:48:27 -0400512 if (! start) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500513 warn_noalloc();
Kevin O'Connor2929c352009-07-25 13:48:27 -0400514 return;
515 }
Kevin O'Connora4d35762008-03-08 15:43:03 -0500516
Kevin O'Connor2929c352009-07-25 13:48:27 -0400517 u32 nr_structs = 0, max_struct_size = 0;
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -0500518 char *q, *p = start;
519 char *end = start + TEMPSMBIOSSIZE - sizeof(struct smbios_type_127);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500520
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400521#define add_struct(type, args...) \
522 do { \
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500523 if (!get_external(type, &p, &nr_structs, &max_struct_size, end)) { \
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400524 q = smbios_init_type_##type(args); \
525 nr_structs++; \
526 if ((q - p) > max_struct_size) \
527 max_struct_size = q - p; \
528 p = q; \
529 } \
530 } while (0)
Kevin O'Connora4d35762008-03-08 15:43:03 -0500531
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400532 add_struct(0, p);
533 add_struct(1, p);
534 add_struct(3, p);
535
Kevin O'Connora26df9b2009-10-09 09:42:11 -0400536 int cpu_num;
537 for (cpu_num = 1; cpu_num <= MaxCountCPUs; cpu_num++)
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400538 add_struct(4, p, cpu_num);
Alex Williamson6d663162010-05-07 13:38:55 -0600539
540 int ram_mb = (RamSize + RamSizeOver4G) >> 20;
541 int nr_mem_devs = (ram_mb + 0x3fff) >> 14;
542 add_struct(16, p, ram_mb, nr_mem_devs);
543
544 int i, j;
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400545 for (i = 0; i < nr_mem_devs; i++) {
Alex Williamson6d663162010-05-07 13:38:55 -0600546 u32 dev_mb = ((i == (nr_mem_devs - 1))
547 ? (((ram_mb - 1) & 0x3fff) + 1)
548 : 16384);
549 add_struct(17, p, dev_mb, i);
550 }
Kevin O'Connor7d09d0e2010-05-10 21:51:38 -0400551
Alex Williamson6d663162010-05-07 13:38:55 -0600552 add_struct(19, p, 0, RamSize >> 20, 0);
553 if (RamSizeOver4G)
554 add_struct(19, p, 4096, RamSizeOver4G >> 20, 1);
555
556 add_struct(20, p, 0, RamSize >> 20, 0, 0, 0);
557 if (RamSizeOver4G) {
558 u32 start_mb = 4096;
559 for (j = 1, i = 0; i < nr_mem_devs; i++, j++) {
560 u32 dev_mb = ((i == (nr_mem_devs - 1))
561 ? (((ram_mb - 1) & 0x3fff) + 1)
562 : 16384);
563 if (i == 0)
564 dev_mb -= RamSize >> 20;
565
566 add_struct(20, p, start_mb, dev_mb, j, i, 1);
567 start_mb += dev_mb;
568 }
Kevin O'Connor2a3dfea2009-10-07 19:44:23 -0400569 }
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400570
571 add_struct(32, p);
572 /* Add any remaining provided entries before the end marker */
573 for (i = 0; i < 256; i++)
Kevin O'Connorde9e7052013-02-09 19:09:20 -0500574 get_external(i, &p, &nr_structs, &max_struct_size, end);
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400575 add_struct(127, p);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500576
577#undef add_struct
578
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500579 smbios_entry_point_setup(max_struct_size, p - start, start, nr_structs);
Kevin O'Connorfe2c3ee2009-12-26 23:26:44 -0500580 free(start);
Kevin O'Connora4d35762008-03-08 15:43:03 -0500581}
Laszlo Ersek37676f82012-12-14 12:59:39 +0100582
583void
584display_uuid(void)
585{
586 u32 addr, end;
587 u8 *uuid;
588 u8 empty_uuid[16] = { 0 };
589
590 if (SMBiosAddr == NULL)
591 return;
592
593 addr = SMBiosAddr->structure_table_address;
594 end = addr + SMBiosAddr->structure_table_length;
595
596 /* the following takes care of any initial wraparound too */
597 while (addr < end) {
598 const struct smbios_structure_header *hdr;
599
600 /* partial structure header */
601 if (end - addr < sizeof(struct smbios_structure_header))
602 return;
603
604 hdr = (struct smbios_structure_header *)addr;
605
606 /* partial structure */
607 if (end - addr < hdr->length)
608 return;
609
610 /* any Type 1 structure version will do that has the UUID */
611 if (hdr->type == 1 &&
612 hdr->length >= offsetof(struct smbios_type_1, uuid) + 16)
613 break;
614
615 /* done with formatted area, skip string-set */
616 addr += hdr->length;
617
618 while (end - addr >= 2 &&
619 (*(u8 *)addr != '\0' ||
620 *(u8 *)(addr+1) != '\0'))
621 ++addr;
622
623 /* structure terminator not found */
624 if (end - addr < 2)
625 return;
626
627 addr += 2;
628 }
629
Laszlo Ersek9c6fb722012-12-18 05:11:39 +0100630 /* parsing finished or skipped entirely, UUID not found */
631 if (addr >= end)
Laszlo Ersek37676f82012-12-14 12:59:39 +0100632 return;
633
634 uuid = (u8 *)(addr + offsetof(struct smbios_type_1, uuid));
635 if (memcmp(uuid, empty_uuid, sizeof empty_uuid) == 0)
636 return;
637
638 printf("Machine UUID"
639 " %02x%02x%02x%02x"
640 "-%02x%02x"
641 "-%02x%02x"
642 "-%02x%02x"
643 "-%02x%02x%02x%02x%02x%02x\n"
644 , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3]
645 , uuid[ 4], uuid[ 5]
646 , uuid[ 6], uuid[ 7]
647 , uuid[ 8], uuid[ 9]
648 , uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
649}