ia64/xen-unstable

view tools/firmware/hvmloader/smbios.c @ 11732:7a7e8cf9a4f9

[HVMLOADER] Fix SMBIOS table code. One of the string lengths is incorrectly computed.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
author kaf24@firebug.cl.cam.ac.uk
date Wed Oct 04 09:32:01 2006 +0100 (2006-10-04)
parents f426f6e646eb
children 5339785f8be8
line source
1 /*
2 * smbios.c - Generate SMBIOS tables for Xen HVM domU's.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) IBM Corporation, 2006
19 *
20 * Authors: Andrew D. Ball <aball@us.ibm.com>
21 */
23 #include <stdint.h>
24 #include <xen/version.h>
25 #include <xen/hvm/e820.h>
26 #include "smbios.h"
27 #include "smbios_types.h"
28 #include "util.h"
29 #include "hypercall.h"
31 static size_t
32 write_smbios_tables(void *start,
33 uint32_t vcpus, uint64_t memsize,
34 uint8_t uuid[16], char *xen_version,
35 uint32_t xen_major_version, uint32_t xen_minor_version);
37 static void
38 get_cpu_manufacturer(char *buf, int len);
39 static void
40 smbios_entry_point_init(void *start,
41 uint16_t max_structure_size,
42 uint16_t structure_table_length,
43 uint32_t structure_table_address,
44 uint16_t number_of_structures);
45 static void *
46 smbios_type_0_init(void *start, const char *xen_version,
47 uint32_t xen_major_version, uint32_t xen_minor_version);
48 static void *
49 smbios_type_1_init(void *start, const char *xen_version,
50 uint8_t uuid[16]);
51 static void *
52 smbios_type_3_init(void *start);
53 static void *
54 smbios_type_4_init(void *start, unsigned int cpu_number,
55 char *cpu_manufacturer);
56 static void *
57 smbios_type_16_init(void *start, uint32_t memory_size_mb);
58 static void *
59 smbios_type_17_init(void *start, uint32_t memory_size_mb);
60 static void *
61 smbios_type_19_init(void *start, uint32_t memory_size_mb);
62 static void *
63 smbios_type_20_init(void *start, uint32_t memory_size_mb);
64 static void *
65 smbios_type_32_init(void *start);
66 static void *
67 smbios_type_127_init(void *start);
69 static void
70 get_cpu_manufacturer(char *buf, int len)
71 {
72 char id[12];
73 uint32_t eax = 0;
75 cpuid(0, &eax, (uint32_t *)&id[0], (uint32_t *)&id[8],
76 (uint32_t *)&id[4]);
78 if (memcmp(id, "GenuineIntel", 12) == 0)
79 strncpy(buf, "Intel", len);
80 else if (memcmp(id, "AuthenticAMD", 12) == 0)
81 strncpy(buf, "AMD", len);
82 else
83 strncpy(buf, "unknown", len);
84 }
86 static size_t
87 write_smbios_tables(void *start,
88 uint32_t vcpus, uint64_t memsize,
89 uint8_t uuid[16], char *xen_version,
90 uint32_t xen_major_version, uint32_t xen_minor_version)
91 {
92 unsigned cpu_num, nr_structs = 0, max_struct_size = 0;
93 char *p, *q;
94 char cpu_manufacturer[15];
95 size_t structure_table_length;
97 get_cpu_manufacturer(cpu_manufacturer, 15);
99 p = (char *)start + sizeof(struct smbios_entry_point);
101 #define do_struct(fn) do { \
102 q = (fn); \
103 nr_structs++; \
104 if ((q - p) > max_struct_size) \
105 max_struct_size = q - p; \
106 p = q; \
107 } while (0)
109 do_struct(smbios_type_0_init(p, xen_version, xen_major_version,
110 xen_minor_version));
111 do_struct(smbios_type_1_init(p, xen_version, uuid));
112 do_struct(smbios_type_3_init(p));
113 for (cpu_num = 1; cpu_num <= vcpus; cpu_num++)
114 do_struct(smbios_type_4_init(p, cpu_num, cpu_manufacturer));
115 do_struct(smbios_type_16_init(p, memsize));
116 do_struct(smbios_type_17_init(p, memsize));
117 do_struct(smbios_type_19_init(p, memsize));
118 do_struct(smbios_type_20_init(p, memsize));
119 do_struct(smbios_type_32_init(p));
120 do_struct(smbios_type_127_init(p));
122 #undef do_struct
124 smbios_entry_point_init(
125 start, max_struct_size,
126 (p - (char *)start) - sizeof(struct smbios_entry_point),
127 SMBIOS_PHYSICAL_ADDRESS + sizeof(struct smbios_entry_point),
128 nr_structs);
130 return (size_t)((char *)p - (char *)start);
131 }
133 /* This tries to figure out how much pseudo-physical memory (in MB)
134 is allocated to the current domU.
136 It iterates through the e820 table, adding up the 'usable' and
137 'reserved' entries and rounding up to the nearest MB.
139 The e820map is not at e820 in hvmloader, so this uses the
140 E820_MAP_* constants from e820.h to pick it up where libxenguest
141 left it.
142 */
143 static uint64_t
144 get_memsize(void)
145 {
146 struct e820entry *map = NULL;
147 uint8_t num_entries = 0;
148 uint64_t memsize = 0;
149 uint8_t i;
151 map = (struct e820entry *) (E820_MAP_PAGE + E820_MAP_OFFSET);
152 num_entries = *((uint8_t *) (E820_MAP_PAGE + E820_MAP_NR_OFFSET));
154 /* walk through e820map, ignoring any entries that aren't marked
155 as usable or reserved. */
157 for (i = 0; i < num_entries; i++) {
158 if (map->type == E820_RAM || map->type == E820_RESERVED)
159 memsize += map->size;
160 map++;
161 }
163 /* Round up to the nearest MB. The user specifies domU
164 pseudo-physical memory in megabytes, so not doing this
165 could easily lead to reporting one less MB than the user
166 specified. */
167 if (memsize & ((1<<20)-1))
168 memsize = (memsize >> 20) + 1;
169 else
170 memsize = (memsize >> 20);
172 return memsize;
173 }
175 void
176 hvm_write_smbios_tables(void)
177 {
178 uint8_t uuid[16]; /* ** This will break if xen_domain_handle_t is
179 not uint8_t[16]. ** */
180 uint16_t xen_major_version, xen_minor_version;
181 uint32_t xen_version;
182 char xen_extra_version[XEN_EXTRAVERSION_LEN];
183 /* guess conservatively on buffer length for Xen version string */
184 char xen_version_str[80];
185 /* temporary variables used to build up Xen version string */
186 char *p = NULL; /* points to next point of insertion */
187 unsigned len = 0; /* length of string already composed */
188 char *tmp = NULL; /* holds result of itoa() */
189 unsigned tmp_len; /* length of next string to add */
191 hypercall_xen_version(XENVER_guest_handle, uuid);
193 /* xen_version major and minor */
194 xen_version = hypercall_xen_version(XENVER_version, NULL);
195 xen_major_version = (uint16_t) (xen_version >> 16);
196 xen_minor_version = (uint16_t) xen_version;
198 hypercall_xen_version(XENVER_extraversion, xen_extra_version);
200 /* build up human-readable Xen version string */
201 p = xen_version_str;
202 len = 0;
204 itoa(tmp, xen_major_version);
205 tmp_len = strlen(tmp);
206 len += tmp_len;
207 if (len >= sizeof(xen_version_str))
208 goto error_out;
209 strcpy(p, tmp);
210 p += tmp_len;
212 len++;
213 if (len >= sizeof(xen_version_str))
214 goto error_out;
215 *p = '.';
216 p++;
218 itoa(tmp, xen_minor_version);
219 tmp_len = strlen(tmp);
220 len += tmp_len;
221 if (len >= sizeof(xen_version_str))
222 goto error_out;
223 strcpy(p, tmp);
224 p += tmp_len;
226 tmp_len = strlen(xen_extra_version);
227 len += tmp_len;
228 if (len >= sizeof(xen_version_str))
229 goto error_out;
230 strcpy(p, xen_extra_version);
231 p += tmp_len;
233 xen_version_str[sizeof(xen_version_str)-1] = '\0';
235 /* NB. 0xC0000 is a safe large memory area for scratch. */
236 len = write_smbios_tables((void *)0xC0000,
237 get_vcpu_nr(), get_memsize(),
238 uuid, xen_version_str,
239 xen_major_version, xen_minor_version);
240 if (len > SMBIOS_SIZE_LIMIT)
241 goto error_out;
242 /* Okay, not too large: copy out of scratch to final location. */
243 memcpy((void *)SMBIOS_PHYSICAL_ADDRESS, (void *)0xC0000, len);
245 return;
247 error_out:
248 puts("Could not write SMBIOS tables, error in hvmloader.c:"
249 "hvm_write_smbios_tables()\n");
250 }
253 static void
254 smbios_entry_point_init(void *start,
255 uint16_t max_structure_size,
256 uint16_t structure_table_length,
257 uint32_t structure_table_address,
258 uint16_t number_of_structures)
259 {
260 uint8_t sum;
261 int i;
262 struct smbios_entry_point *ep = (struct smbios_entry_point *)start;
264 strncpy(ep->anchor_string, "_SM_", 4);
265 ep->length = 0x1f;
266 ep->smbios_major_version = 2;
267 ep->smbios_minor_version = 4;
268 ep->max_structure_size = max_structure_size;
269 ep->entry_point_revision = 0;
270 memset(ep->formatted_area, 0, 5);
271 strncpy(ep->intermediate_anchor_string, "_DMI_", 5);
273 ep->structure_table_length = structure_table_length;
274 ep->structure_table_address = structure_table_address;
275 ep->number_of_structures = number_of_structures;
276 ep->smbios_bcd_revision = 0x24;
278 ep->checksum = 0;
279 ep->intermediate_checksum = 0;
281 sum = 0;
282 for (i = 0; i < 0x10; ++i)
283 sum += ((int8_t *)start)[i];
284 ep->checksum = -sum;
286 sum = 0;
287 for (i = 0x10; i < ep->length; ++i)
288 sum += ((int8_t *)start)[i];
289 ep->intermediate_checksum = -sum;
290 }
292 /* Type 0 -- BIOS Information */
293 static void *
294 smbios_type_0_init(void *start, const char *xen_version,
295 uint32_t xen_major_version, uint32_t xen_minor_version)
296 {
297 struct smbios_type_0 *p = (struct smbios_type_0 *)start;
299 p->header.type = 0;
300 p->header.length = sizeof(struct smbios_type_0);
301 p->header.handle = 0;
303 p->vendor_str = 1;
304 p->version_str = 2;
305 p->starting_address_segment = 0xe800;
306 p->release_date_str = 0;
307 p->rom_size = 0;
309 memset(p->characteristics, 0, 8);
310 p->characteristics[7] = 0x08; /* BIOS characteristics not supported */
311 p->characteristics_extension_bytes[0] = 0;
312 p->characteristics_extension_bytes[1] = 0;
314 p->major_release = (uint8_t) xen_major_version;
315 p->minor_release = (uint8_t) xen_minor_version;
316 p->embedded_controller_major = 0xff;
317 p->embedded_controller_minor = 0xff;
319 start += sizeof(struct smbios_type_0);
320 strcpy((char *)start, "Xen");
321 start += strlen("Xen") + 1;
322 strcpy((char *)start, xen_version);
323 start += strlen(xen_version) + 1;
325 *((uint8_t *)start) = 0;
326 return start + 1;
327 }
329 /* Type 1 -- System Information */
330 static void *
331 smbios_type_1_init(void *start, const char *xen_version,
332 uint8_t uuid[16])
333 {
334 char uuid_str[37];
335 struct smbios_type_1 *p = (struct smbios_type_1 *)start;
336 p->header.type = 1;
337 p->header.length = sizeof(struct smbios_type_1);
338 p->header.handle = 0x100;
340 p->manufacturer_str = 1;
341 p->product_name_str = 2;
342 p->version_str = 3;
343 p->serial_number_str = 4;
345 memcpy(p->uuid, uuid, 16);
347 p->wake_up_type = 0x06; /* power switch */
348 p->sku_str = 0;
349 p->family_str = 0;
351 start += sizeof(struct smbios_type_1);
353 strcpy((char *)start, "Xen");
354 start += strlen("Xen") + 1;
355 strcpy((char *)start, "HVM domU");
356 start += strlen("HVM domU") + 1;
357 strcpy((char *)start, xen_version);
358 start += strlen(xen_version) + 1;
359 uuid_to_string(uuid_str, uuid);
360 strcpy((char *)start, uuid_str);
361 start += strlen(uuid_str) + 1;
362 *((uint8_t *)start) = 0;
364 return start+1;
365 }
367 /* Type 3 -- System Enclosure */
368 static void *
369 smbios_type_3_init(void *start)
370 {
371 struct smbios_type_3 *p = (struct smbios_type_3 *)start;
373 p->header.type = 3;
374 p->header.length = sizeof(struct smbios_type_3);
375 p->header.handle = 0x300;
377 p->manufacturer_str = 1;
378 p->type = 0x01; /* other */
379 p->version_str = 0;
380 p->serial_number_str = 0;
381 p->asset_tag_str = 0;
382 p->boot_up_state = 0x03; /* safe */
383 p->power_supply_state = 0x03; /* safe */
384 p->thermal_state = 0x03; /* safe */
385 p->security_status = 0x02; /* unknown */
387 start += sizeof(struct smbios_type_3);
389 strcpy((char *)start, "Xen");
390 start += strlen("Xen") + 1;
391 *((uint8_t *)start) = 0;
392 return start+1;
393 }
395 /* Type 4 -- Processor Information */
396 static void *
397 smbios_type_4_init(void *start, unsigned int cpu_number, char *cpu_manufacturer)
398 {
399 char buf[80];
400 struct smbios_type_4 *p = (struct smbios_type_4 *)start;
401 uint32_t eax, ebx, ecx, edx;
403 p->header.type = 4;
404 p->header.length = sizeof(struct smbios_type_4);
405 p->header.handle = 0x400 + cpu_number;
407 p->socket_designation_str = 1;
408 p->processor_type = 0x03; /* CPU */
409 p->processor_family = 0x01; /* other */
410 p->manufacturer_str = 2;
412 cpuid(1, &eax, &ebx, &ecx, &edx);
414 p->cpuid[0] = eax;
415 p->cpuid[1] = edx;
417 p->version_str = 0;
418 p->voltage = 0;
419 p->external_clock = 0;
421 p->max_speed = 0; /* unknown */
422 p->current_speed = 0; /* unknown */
424 p->status = 0x41; /* socket populated, CPU enabled */
425 p->upgrade = 0x01; /* other */
427 start += sizeof(struct smbios_type_4);
429 strncpy(buf, "CPU ", sizeof(buf));
430 if ((sizeof(buf) - strlen("CPU ")) >= 3)
431 itoa(buf + strlen("CPU "), cpu_number);
433 strcpy((char *)start, buf);
434 start += strlen(buf) + 1;
436 strcpy((char *)start, cpu_manufacturer);
437 start += strlen(cpu_manufacturer) + 1;
439 *((uint8_t *)start) = 0;
440 return start+1;
441 }
443 /* Type 16 -- Physical Memory Array */
444 static void *
445 smbios_type_16_init(void *start, uint32_t memsize)
446 {
447 struct smbios_type_16 *p = (struct smbios_type_16*)start;
449 p->header.type = 16;
450 p->header.handle = 0x1000;
451 p->header.length = sizeof(struct smbios_type_16);
453 p->location = 0x01; /* other */
454 p->use = 0x03; /* system memory */
455 p->error_correction = 0x01; /* other */
456 p->maximum_capacity = memsize * 1024;
457 p->memory_error_information_handle = 0xfffe; /* none provided */
458 p->number_of_memory_devices = 1;
460 start += sizeof(struct smbios_type_16);
461 *((uint16_t *)start) = 0;
462 return start + 2;
463 }
465 /* Type 17 -- Memory Device */
466 static void *
467 smbios_type_17_init(void *start, uint32_t memory_size_mb)
468 {
469 struct smbios_type_17 *p = (struct smbios_type_17 *)start;
471 p->header.type = 17;
472 p->header.length = sizeof(struct smbios_type_17);
473 p->header.handle = 0x1100;
475 p->physical_memory_array_handle = 0x1000;
476 p->total_width = 64;
477 p->data_width = 64;
478 /* truncate memory_size_mb to 16 bits and clear most significant
479 bit [indicates size in MB] */
480 p->size = (uint16_t) memory_size_mb & 0x7fff;
481 p->form_factor = 0x09; /* DIMM */
482 p->device_set = 0;
483 p->device_locator_str = 1;
484 p->bank_locator_str = 0;
485 p->memory_type = 0x07; /* RAM */
486 p->type_detail = 0;
488 start += sizeof(struct smbios_type_17);
489 strcpy((char *)start, "DIMM 1");
490 start += strlen("DIMM 1") + 1;
491 *((uint8_t *)start) = 0;
493 return start+1;
494 }
496 /* Type 19 -- Memory Array Mapped Address */
497 static void *
498 smbios_type_19_init(void *start, uint32_t memory_size_mb)
499 {
500 struct smbios_type_19 *p = (struct smbios_type_19 *)start;
502 p->header.type = 19;
503 p->header.length = sizeof(struct smbios_type_19);
504 p->header.handle = 0x1300;
506 p->starting_address = 0;
507 p->ending_address = (memory_size_mb-1) * 1024;
508 p->memory_array_handle = 0x1000;
509 p->partition_width = 1;
511 start += sizeof(struct smbios_type_19);
512 *((uint16_t *)start) = 0;
513 return start + 2;
514 }
516 /* Type 20 -- Memory Device Mapped Address */
517 static void *
518 smbios_type_20_init(void *start, uint32_t memory_size_mb)
519 {
520 struct smbios_type_20 *p = (struct smbios_type_20 *)start;
522 p->header.type = 20;
523 p->header.length = sizeof(struct smbios_type_20);
524 p->header.handle = 0x1400;
526 p->starting_address = 0;
527 p->ending_address = (memory_size_mb-1)*1024;
528 p->memory_device_handle = 0x1100;
529 p->memory_array_mapped_address_handle = 0x1300;
530 p->partition_row_position = 1;
531 p->interleave_position = 0;
532 p->interleaved_data_depth = 0;
534 start += sizeof(struct smbios_type_20);
536 *((uint16_t *)start) = 0;
537 return start+2;
538 }
540 /* Type 32 -- System Boot Information */
541 static void *
542 smbios_type_32_init(void *start)
543 {
544 struct smbios_type_32 *p = (struct smbios_type_32 *)start;
546 p->header.type = 32;
547 p->header.length = sizeof(struct smbios_type_32);
548 p->header.handle = 0x2000;
549 memset(p->reserved, 0, 6);
550 p->boot_status = 0; /* no errors detected */
552 start += sizeof(struct smbios_type_32);
553 *((uint16_t *)start) = 0;
554 return start+2;
555 }
557 /* Type 127 -- End of Table */
558 static void *
559 smbios_type_127_init(void *start)
560 {
561 struct smbios_type_127 *p = (struct smbios_type_127 *)start;
563 p->header.type = 127;
564 p->header.length = sizeof(struct smbios_type_127);
565 p->header.handle = 0x7f00;
567 start += sizeof(struct smbios_type_127);
568 *((uint16_t *)start) = 0;
569 return start + 2;
570 }