| 1 | /* |
| 2 | * SMBIOS 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/units.h" |
| 20 | #include "qemu/bswap.h" |
| 21 | #include "qapi/error.h" |
| 22 | #include "qemu/config-file.h" |
| 23 | #include "qemu/module.h" |
| 24 | #include "qemu/option.h" |
| 25 | #include "system/system.h" |
| 26 | #include "qemu/uuid.h" |
| 27 | #include "hw/firmware/smbios.h" |
| 28 | #include "hw/core/loader.h" |
| 29 | #include "hw/core/boards.h" |
| 30 | #include "hw/pci/pci_bus.h" |
| 31 | #include "hw/pci/pci_device.h" |
| 32 | #include "smbios_build.h" |
| 33 | |
| 34 | /* |
| 35 | * SMBIOS tables provided by user with '-smbios file=<foo>' option |
| 36 | */ |
| 37 | uint8_t *usr_blobs; |
| 38 | size_t usr_blobs_len; |
| 39 | static unsigned usr_table_max; |
| 40 | static unsigned usr_table_cnt; |
| 41 | |
| 42 | uint8_t *smbios_tables; |
| 43 | size_t smbios_tables_len; |
| 44 | unsigned smbios_table_max; |
| 45 | unsigned smbios_table_cnt; |
| 46 | |
| 47 | static SmbiosEntryPoint ep; |
| 48 | |
| 49 | static int smbios_type4_count = 0; |
| 50 | static bool smbios_have_defaults; |
| 51 | static uint32_t smbios_cpuid_version, smbios_cpuid_features; |
| 52 | |
| 53 | DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1); |
| 54 | DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1); |
| 55 | |
| 56 | smbios_type0_t smbios_type0; |
| 57 | smbios_type1_t smbios_type1; |
| 58 | |
| 59 | static struct { |
| 60 | const char *manufacturer, *product, *version, *serial, *asset, *location; |
| 61 | } type2; |
| 62 | |
| 63 | static struct { |
| 64 | const char *manufacturer, *version, *serial, *asset, *sku; |
| 65 | } type3; |
| 66 | |
| 67 | /* |
| 68 | * SVVP requires max_speed and current_speed to be set and not being |
| 69 | * 0 which counts as unknown (SMBIOS 3.1.0/Table 21). Set the |
| 70 | * default value to 2000MHz as we did before. |
| 71 | */ |
| 72 | #define DEFAULT_CPU_SPEED 2000 |
| 73 | |
| 74 | static struct { |
| 75 | uint16_t processor_family; |
| 76 | const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; |
| 77 | uint64_t max_speed; |
| 78 | uint64_t current_speed; |
| 79 | uint64_t processor_id; |
| 80 | } type4 = { |
| 81 | .max_speed = DEFAULT_CPU_SPEED, |
| 82 | .current_speed = DEFAULT_CPU_SPEED, |
| 83 | .processor_id = 0, |
| 84 | .processor_family = 0x01, /* Other */ |
| 85 | }; |
| 86 | |
| 87 | struct type8_instance { |
| 88 | const char *internal_reference, *external_reference; |
| 89 | uint8_t connector_type, port_type; |
| 90 | QTAILQ_ENTRY(type8_instance) next; |
| 91 | }; |
| 92 | static QTAILQ_HEAD(, type8_instance) type8 = QTAILQ_HEAD_INITIALIZER(type8); |
| 93 | |
| 94 | /* type 9 instance for parsing */ |
| 95 | struct type9_instance { |
| 96 | const char *slot_designation, *pcidev; |
| 97 | uint8_t slot_type, slot_data_bus_width, current_usage, slot_length, |
| 98 | slot_characteristics1, slot_characteristics2; |
| 99 | uint16_t slot_id; |
| 100 | QTAILQ_ENTRY(type9_instance) next; |
| 101 | }; |
| 102 | static QTAILQ_HEAD(, type9_instance) type9 = QTAILQ_HEAD_INITIALIZER(type9); |
| 103 | |
| 104 | static struct { |
| 105 | size_t nvalues; |
| 106 | char **values; |
| 107 | } type11; |
| 108 | |
| 109 | static struct { |
| 110 | const char *loc_pfx, *bank, *manufacturer, *serial, *asset, *part; |
| 111 | uint16_t speed; |
| 112 | } type17; |
| 113 | |
| 114 | static QEnumLookup type41_kind_lookup = { |
| 115 | .array = (const char *const[]) { |
| 116 | "other", |
| 117 | "unknown", |
| 118 | "video", |
| 119 | "scsi", |
| 120 | "ethernet", |
| 121 | "tokenring", |
| 122 | "sound", |
| 123 | "pata", |
| 124 | "sata", |
| 125 | "sas", |
| 126 | }, |
| 127 | .size = 10 |
| 128 | }; |
| 129 | struct type41_instance { |
| 130 | const char *designation, *pcidev; |
| 131 | uint8_t instance, kind; |
| 132 | QTAILQ_ENTRY(type41_instance) next; |
| 133 | }; |
| 134 | static QTAILQ_HEAD(, type41_instance) type41 = QTAILQ_HEAD_INITIALIZER(type41); |
| 135 | |
| 136 | static QemuOptsList qemu_smbios_opts = { |
| 137 | .name = "smbios", |
| 138 | .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), |
| 139 | .desc = { |
| 140 | /* |
| 141 | * no elements => accept any params |
| 142 | * validation will happen later |
| 143 | */ |
| 144 | { /* end of list */ } |
| 145 | } |
| 146 | }; |
| 147 | |
| 148 | static const QemuOptDesc qemu_smbios_file_opts[] = { |
| 149 | { |
| 150 | .name = "file", |
| 151 | .type = QEMU_OPT_STRING, |
| 152 | .help = "binary file containing an SMBIOS element", |
| 153 | }, |
| 154 | { /* end of list */ } |
| 155 | }; |
| 156 | |
| 157 | static const QemuOptDesc qemu_smbios_type0_opts[] = { |
| 158 | { |
| 159 | .name = "type", |
| 160 | .type = QEMU_OPT_NUMBER, |
| 161 | .help = "SMBIOS element type", |
| 162 | },{ |
| 163 | .name = "vendor", |
| 164 | .type = QEMU_OPT_STRING, |
| 165 | .help = "vendor name", |
| 166 | },{ |
| 167 | .name = "version", |
| 168 | .type = QEMU_OPT_STRING, |
| 169 | .help = "version number", |
| 170 | },{ |
| 171 | .name = "date", |
| 172 | .type = QEMU_OPT_STRING, |
| 173 | .help = "release date", |
| 174 | },{ |
| 175 | .name = "release", |
| 176 | .type = QEMU_OPT_STRING, |
| 177 | .help = "revision number", |
| 178 | },{ |
| 179 | .name = "uefi", |
| 180 | .type = QEMU_OPT_BOOL, |
| 181 | .help = "uefi support", |
| 182 | },{ |
| 183 | .name = "vm", |
| 184 | .type = QEMU_OPT_BOOL, |
| 185 | .help = "virtual machine", |
| 186 | }, |
| 187 | { /* end of list */ } |
| 188 | }; |
| 189 | |
| 190 | static const QemuOptDesc qemu_smbios_type1_opts[] = { |
| 191 | { |
| 192 | .name = "type", |
| 193 | .type = QEMU_OPT_NUMBER, |
| 194 | .help = "SMBIOS element type", |
| 195 | },{ |
| 196 | .name = "manufacturer", |
| 197 | .type = QEMU_OPT_STRING, |
| 198 | .help = "manufacturer name", |
| 199 | },{ |
| 200 | .name = "product", |
| 201 | .type = QEMU_OPT_STRING, |
| 202 | .help = "product name", |
| 203 | },{ |
| 204 | .name = "version", |
| 205 | .type = QEMU_OPT_STRING, |
| 206 | .help = "version number", |
| 207 | },{ |
| 208 | .name = "serial", |
| 209 | .type = QEMU_OPT_STRING, |
| 210 | .help = "serial number", |
| 211 | },{ |
| 212 | .name = "uuid", |
| 213 | .type = QEMU_OPT_STRING, |
| 214 | .help = "UUID", |
| 215 | },{ |
| 216 | .name = "sku", |
| 217 | .type = QEMU_OPT_STRING, |
| 218 | .help = "SKU number", |
| 219 | },{ |
| 220 | .name = "family", |
| 221 | .type = QEMU_OPT_STRING, |
| 222 | .help = "family name", |
| 223 | }, |
| 224 | { /* end of list */ } |
| 225 | }; |
| 226 | |
| 227 | static const QemuOptDesc qemu_smbios_type2_opts[] = { |
| 228 | { |
| 229 | .name = "type", |
| 230 | .type = QEMU_OPT_NUMBER, |
| 231 | .help = "SMBIOS element type", |
| 232 | },{ |
| 233 | .name = "manufacturer", |
| 234 | .type = QEMU_OPT_STRING, |
| 235 | .help = "manufacturer name", |
| 236 | },{ |
| 237 | .name = "product", |
| 238 | .type = QEMU_OPT_STRING, |
| 239 | .help = "product name", |
| 240 | },{ |
| 241 | .name = "version", |
| 242 | .type = QEMU_OPT_STRING, |
| 243 | .help = "version number", |
| 244 | },{ |
| 245 | .name = "serial", |
| 246 | .type = QEMU_OPT_STRING, |
| 247 | .help = "serial number", |
| 248 | },{ |
| 249 | .name = "asset", |
| 250 | .type = QEMU_OPT_STRING, |
| 251 | .help = "asset tag number", |
| 252 | },{ |
| 253 | .name = "location", |
| 254 | .type = QEMU_OPT_STRING, |
| 255 | .help = "location in chassis", |
| 256 | }, |
| 257 | { /* end of list */ } |
| 258 | }; |
| 259 | |
| 260 | static const QemuOptDesc qemu_smbios_type3_opts[] = { |
| 261 | { |
| 262 | .name = "type", |
| 263 | .type = QEMU_OPT_NUMBER, |
| 264 | .help = "SMBIOS element type", |
| 265 | },{ |
| 266 | .name = "manufacturer", |
| 267 | .type = QEMU_OPT_STRING, |
| 268 | .help = "manufacturer name", |
| 269 | },{ |
| 270 | .name = "version", |
| 271 | .type = QEMU_OPT_STRING, |
| 272 | .help = "version number", |
| 273 | },{ |
| 274 | .name = "serial", |
| 275 | .type = QEMU_OPT_STRING, |
| 276 | .help = "serial number", |
| 277 | },{ |
| 278 | .name = "asset", |
| 279 | .type = QEMU_OPT_STRING, |
| 280 | .help = "asset tag number", |
| 281 | },{ |
| 282 | .name = "sku", |
| 283 | .type = QEMU_OPT_STRING, |
| 284 | .help = "SKU number", |
| 285 | }, |
| 286 | { /* end of list */ } |
| 287 | }; |
| 288 | |
| 289 | static const QemuOptDesc qemu_smbios_type4_opts[] = { |
| 290 | { |
| 291 | .name = "type", |
| 292 | .type = QEMU_OPT_NUMBER, |
| 293 | .help = "SMBIOS element type", |
| 294 | },{ |
| 295 | .name = "sock_pfx", |
| 296 | .type = QEMU_OPT_STRING, |
| 297 | .help = "socket designation string prefix", |
| 298 | },{ |
| 299 | .name = "manufacturer", |
| 300 | .type = QEMU_OPT_STRING, |
| 301 | .help = "manufacturer name", |
| 302 | },{ |
| 303 | .name = "version", |
| 304 | .type = QEMU_OPT_STRING, |
| 305 | .help = "version number", |
| 306 | },{ |
| 307 | .name = "max-speed", |
| 308 | .type = QEMU_OPT_NUMBER, |
| 309 | .help = "max speed in MHz", |
| 310 | },{ |
| 311 | .name = "current-speed", |
| 312 | .type = QEMU_OPT_NUMBER, |
| 313 | .help = "speed at system boot in MHz", |
| 314 | },{ |
| 315 | .name = "serial", |
| 316 | .type = QEMU_OPT_STRING, |
| 317 | .help = "serial number", |
| 318 | },{ |
| 319 | .name = "asset", |
| 320 | .type = QEMU_OPT_STRING, |
| 321 | .help = "asset tag number", |
| 322 | },{ |
| 323 | .name = "part", |
| 324 | .type = QEMU_OPT_STRING, |
| 325 | .help = "part number", |
| 326 | }, { |
| 327 | .name = "processor-family", |
| 328 | .type = QEMU_OPT_NUMBER, |
| 329 | .help = "processor family", |
| 330 | }, { |
| 331 | .name = "processor-id", |
| 332 | .type = QEMU_OPT_NUMBER, |
| 333 | .help = "processor id", |
| 334 | }, |
| 335 | { /* end of list */ } |
| 336 | }; |
| 337 | |
| 338 | static const QemuOptDesc qemu_smbios_type8_opts[] = { |
| 339 | { |
| 340 | .name = "type", |
| 341 | .type = QEMU_OPT_NUMBER, |
| 342 | .help = "SMBIOS element type", |
| 343 | }, |
| 344 | { |
| 345 | .name = "internal_reference", |
| 346 | .type = QEMU_OPT_STRING, |
| 347 | .help = "internal reference designator", |
| 348 | }, |
| 349 | { |
| 350 | .name = "external_reference", |
| 351 | .type = QEMU_OPT_STRING, |
| 352 | .help = "external reference designator", |
| 353 | }, |
| 354 | { |
| 355 | .name = "connector_type", |
| 356 | .type = QEMU_OPT_NUMBER, |
| 357 | .help = "connector type", |
| 358 | }, |
| 359 | { |
| 360 | .name = "port_type", |
| 361 | .type = QEMU_OPT_NUMBER, |
| 362 | .help = "port type", |
| 363 | }, |
| 364 | { /* end of list */ } |
| 365 | }; |
| 366 | |
| 367 | static const QemuOptDesc qemu_smbios_type9_opts[] = { |
| 368 | { |
| 369 | .name = "type", |
| 370 | .type = QEMU_OPT_NUMBER, |
| 371 | .help = "SMBIOS element type", |
| 372 | }, |
| 373 | { |
| 374 | .name = "slot_designation", |
| 375 | .type = QEMU_OPT_STRING, |
| 376 | .help = "string number for reference designation", |
| 377 | }, |
| 378 | { |
| 379 | .name = "slot_type", |
| 380 | .type = QEMU_OPT_NUMBER, |
| 381 | .help = "connector type", |
| 382 | }, |
| 383 | { |
| 384 | .name = "slot_data_bus_width", |
| 385 | .type = QEMU_OPT_NUMBER, |
| 386 | .help = "port type", |
| 387 | }, |
| 388 | { |
| 389 | .name = "current_usage", |
| 390 | .type = QEMU_OPT_NUMBER, |
| 391 | .help = "current usage", |
| 392 | }, |
| 393 | { |
| 394 | .name = "slot_length", |
| 395 | .type = QEMU_OPT_NUMBER, |
| 396 | .help = "system slot length", |
| 397 | }, |
| 398 | { |
| 399 | .name = "slot_id", |
| 400 | .type = QEMU_OPT_NUMBER, |
| 401 | .help = "system slot id", |
| 402 | }, |
| 403 | { |
| 404 | .name = "slot_characteristics1", |
| 405 | .type = QEMU_OPT_NUMBER, |
| 406 | .help = "slot characteristics1, see the spec", |
| 407 | }, |
| 408 | { |
| 409 | .name = "slot_characteristics2", |
| 410 | .type = QEMU_OPT_NUMBER, |
| 411 | .help = "slot characteristics2, see the spec", |
| 412 | }, |
| 413 | { |
| 414 | .name = "pci_device", |
| 415 | .type = QEMU_OPT_STRING, |
| 416 | .help = "PCI device, if provided." |
| 417 | } |
| 418 | }; |
| 419 | |
| 420 | static const QemuOptDesc qemu_smbios_type11_opts[] = { |
| 421 | { |
| 422 | .name = "type", |
| 423 | .type = QEMU_OPT_NUMBER, |
| 424 | .help = "SMBIOS element type", |
| 425 | }, |
| 426 | { |
| 427 | .name = "value", |
| 428 | .type = QEMU_OPT_STRING, |
| 429 | .help = "OEM string data", |
| 430 | }, |
| 431 | { |
| 432 | .name = "path", |
| 433 | .type = QEMU_OPT_STRING, |
| 434 | .help = "OEM string data from file", |
| 435 | }, |
| 436 | { /* end of list */ } |
| 437 | }; |
| 438 | |
| 439 | static const QemuOptDesc qemu_smbios_type17_opts[] = { |
| 440 | { |
| 441 | .name = "type", |
| 442 | .type = QEMU_OPT_NUMBER, |
| 443 | .help = "SMBIOS element type", |
| 444 | },{ |
| 445 | .name = "loc_pfx", |
| 446 | .type = QEMU_OPT_STRING, |
| 447 | .help = "device locator string prefix", |
| 448 | },{ |
| 449 | .name = "bank", |
| 450 | .type = QEMU_OPT_STRING, |
| 451 | .help = "bank locator string", |
| 452 | },{ |
| 453 | .name = "manufacturer", |
| 454 | .type = QEMU_OPT_STRING, |
| 455 | .help = "manufacturer name", |
| 456 | },{ |
| 457 | .name = "serial", |
| 458 | .type = QEMU_OPT_STRING, |
| 459 | .help = "serial number", |
| 460 | },{ |
| 461 | .name = "asset", |
| 462 | .type = QEMU_OPT_STRING, |
| 463 | .help = "asset tag number", |
| 464 | },{ |
| 465 | .name = "part", |
| 466 | .type = QEMU_OPT_STRING, |
| 467 | .help = "part number", |
| 468 | },{ |
| 469 | .name = "speed", |
| 470 | .type = QEMU_OPT_NUMBER, |
| 471 | .help = "maximum capable speed", |
| 472 | }, |
| 473 | { /* end of list */ } |
| 474 | }; |
| 475 | |
| 476 | static const QemuOptDesc qemu_smbios_type41_opts[] = { |
| 477 | { |
| 478 | .name = "type", |
| 479 | .type = QEMU_OPT_NUMBER, |
| 480 | .help = "SMBIOS element type", |
| 481 | },{ |
| 482 | .name = "designation", |
| 483 | .type = QEMU_OPT_STRING, |
| 484 | .help = "reference designation string", |
| 485 | },{ |
| 486 | .name = "kind", |
| 487 | .type = QEMU_OPT_STRING, |
| 488 | .help = "device type", |
| 489 | .def_value_str = "other", |
| 490 | },{ |
| 491 | .name = "instance", |
| 492 | .type = QEMU_OPT_NUMBER, |
| 493 | .help = "device type instance", |
| 494 | },{ |
| 495 | .name = "pcidev", |
| 496 | .type = QEMU_OPT_STRING, |
| 497 | .help = "PCI device", |
| 498 | }, |
| 499 | { /* end of list */ } |
| 500 | }; |
| 501 | |
| 502 | static void smbios_register_config(void) |
| 503 | { |
| 504 | qemu_add_opts(&qemu_smbios_opts); |
| 505 | } |
| 506 | |
| 507 | opts_init(smbios_register_config); |
| 508 | |
| 509 | /* |
| 510 | * The SMBIOS 2.1 "structure table length" field in the |
| 511 | * entry point uses a 16-bit integer, so we're limited |
| 512 | * in total table size |
| 513 | */ |
| 514 | #define SMBIOS_21_MAX_TABLES_LEN 0xffff |
| 515 | |
| 516 | static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp) |
| 517 | { |
| 518 | if (smbios_type4_count && smbios_type4_count != expected_t4_count) { |
| 519 | error_setg(errp, "Expected %d SMBIOS Type 4 tables, got %d instead", |
| 520 | expected_t4_count, smbios_type4_count); |
| 521 | return false; |
| 522 | } |
| 523 | return true; |
| 524 | } |
| 525 | |
| 526 | bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp) |
| 527 | { |
| 528 | if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && |
| 529 | smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) { |
| 530 | error_setg(errp, "SMBIOS 2.1 table length %zu exceeds %d", |
| 531 | smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); |
| 532 | return false; |
| 533 | } |
| 534 | return true; |
| 535 | } |
| 536 | |
| 537 | bool smbios_skip_table(uint8_t type, bool required_table) |
| 538 | { |
| 539 | if (test_bit(type, smbios_have_binfile_bitmap)) { |
| 540 | return true; /* user provided their own binary blob(s) */ |
| 541 | } |
| 542 | if (test_bit(type, smbios_have_fields_bitmap)) { |
| 543 | return false; /* user provided fields via command line */ |
| 544 | } |
| 545 | if (smbios_have_defaults && required_table) { |
| 546 | return false; /* we're building tables, and this one's required */ |
| 547 | } |
| 548 | return true; |
| 549 | } |
| 550 | |
| 551 | #define T0_BASE 0x000 |
| 552 | #define T1_BASE 0x100 |
| 553 | #define T2_BASE 0x200 |
| 554 | #define T3_BASE 0x300 |
| 555 | #define T4_BASE 0x400 |
| 556 | #define T9_BASE 0x900 |
| 557 | #define T11_BASE 0xe00 |
| 558 | |
| 559 | #define T16_BASE 0x1000 |
| 560 | #define T17_BASE 0x1100 |
| 561 | #define T19_BASE 0x1300 |
| 562 | #define T32_BASE 0x2000 |
| 563 | #define T41_BASE 0x2900 |
| 564 | #define T127_BASE 0x7F00 |
| 565 | |
| 566 | static void smbios_build_type_0_table(void) |
| 567 | { |
| 568 | SMBIOS_BUILD_TABLE_PRE(0, T0_BASE, false); /* optional, leave up to BIOS */ |
| 569 | |
| 570 | SMBIOS_TABLE_SET_STR(0, vendor_str, smbios_type0.vendor); |
| 571 | SMBIOS_TABLE_SET_STR(0, bios_version_str, smbios_type0.version); |
| 572 | |
| 573 | t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */ |
| 574 | |
| 575 | SMBIOS_TABLE_SET_STR(0, bios_release_date_str, smbios_type0.date); |
| 576 | |
| 577 | t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ |
| 578 | |
| 579 | t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ |
| 580 | t->bios_characteristics_extension_bytes[0] = 0; |
| 581 | |
| 582 | t->bios_characteristics_extension_bytes[1] = 0x04; /* TCD/SVVP */ |
| 583 | if (smbios_type0.uefi) { |
| 584 | t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ |
| 585 | } |
| 586 | if (smbios_type0.vm) { |
| 587 | t->bios_characteristics_extension_bytes[1] |= 0x10; /* |= VM */ |
| 588 | } |
| 589 | |
| 590 | if (smbios_type0.have_major_minor) { |
| 591 | t->system_bios_major_release = smbios_type0.major; |
| 592 | t->system_bios_minor_release = smbios_type0.minor; |
| 593 | } else { |
| 594 | t->system_bios_major_release = 0; |
| 595 | t->system_bios_minor_release = 0; |
| 596 | } |
| 597 | |
| 598 | /* hardcoded in SeaBIOS */ |
| 599 | t->embedded_controller_major_release = 0xFF; |
| 600 | t->embedded_controller_minor_release = 0xFF; |
| 601 | |
| 602 | SMBIOS_BUILD_TABLE_POST; |
| 603 | } |
| 604 | |
| 605 | /* Encode UUID from the big endian encoding described on RFC4122 to the wire |
| 606 | * format specified by SMBIOS version 2.6. |
| 607 | */ |
| 608 | static void smbios_encode_uuid(struct smbios_uuid *uuid, QemuUUID *in) |
| 609 | { |
| 610 | memcpy(uuid, in, 16); |
| 611 | uuid->time_low = bswap32(uuid->time_low); |
| 612 | uuid->time_mid = bswap16(uuid->time_mid); |
| 613 | uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); |
| 614 | } |
| 615 | |
| 616 | static void smbios_build_type_1_table(void) |
| 617 | { |
| 618 | SMBIOS_BUILD_TABLE_PRE(1, T1_BASE, true); /* required */ |
| 619 | |
| 620 | SMBIOS_TABLE_SET_STR(1, manufacturer_str, smbios_type1.manufacturer); |
| 621 | SMBIOS_TABLE_SET_STR(1, product_name_str, smbios_type1.product); |
| 622 | SMBIOS_TABLE_SET_STR(1, version_str, smbios_type1.version); |
| 623 | SMBIOS_TABLE_SET_STR(1, serial_number_str, smbios_type1.serial); |
| 624 | if (qemu_uuid_set) { |
| 625 | smbios_encode_uuid(&t->uuid, &qemu_uuid); |
| 626 | } else { |
| 627 | memset(&t->uuid, 0, 16); |
| 628 | } |
| 629 | t->wake_up_type = 0x06; /* power switch */ |
| 630 | SMBIOS_TABLE_SET_STR(1, sku_number_str, smbios_type1.sku); |
| 631 | SMBIOS_TABLE_SET_STR(1, family_str, smbios_type1.family); |
| 632 | |
| 633 | SMBIOS_BUILD_TABLE_POST; |
| 634 | } |
| 635 | |
| 636 | static void smbios_build_type_2_table(void) |
| 637 | { |
| 638 | SMBIOS_BUILD_TABLE_PRE(2, T2_BASE, false); /* optional */ |
| 639 | |
| 640 | SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer); |
| 641 | SMBIOS_TABLE_SET_STR(2, product_str, type2.product); |
| 642 | SMBIOS_TABLE_SET_STR(2, version_str, type2.version); |
| 643 | SMBIOS_TABLE_SET_STR(2, serial_number_str, type2.serial); |
| 644 | SMBIOS_TABLE_SET_STR(2, asset_tag_number_str, type2.asset); |
| 645 | t->feature_flags = 0x01; /* Motherboard */ |
| 646 | SMBIOS_TABLE_SET_STR(2, location_str, type2.location); |
| 647 | t->chassis_handle = cpu_to_le16(0x300); /* Type 3 (System enclosure) */ |
| 648 | t->board_type = 0x0A; /* Motherboard */ |
| 649 | t->contained_element_count = 0; |
| 650 | |
| 651 | SMBIOS_BUILD_TABLE_POST; |
| 652 | } |
| 653 | |
| 654 | static void smbios_build_type_3_table(void) |
| 655 | { |
| 656 | SMBIOS_BUILD_TABLE_PRE(3, T3_BASE, true); /* required */ |
| 657 | |
| 658 | SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer); |
| 659 | t->type = 0x01; /* Other */ |
| 660 | SMBIOS_TABLE_SET_STR(3, version_str, type3.version); |
| 661 | SMBIOS_TABLE_SET_STR(3, serial_number_str, type3.serial); |
| 662 | SMBIOS_TABLE_SET_STR(3, asset_tag_number_str, type3.asset); |
| 663 | t->boot_up_state = 0x03; /* Safe */ |
| 664 | t->power_supply_state = 0x03; /* Safe */ |
| 665 | t->thermal_state = 0x03; /* Safe */ |
| 666 | t->security_status = 0x02; /* Unknown */ |
| 667 | t->oem_defined = cpu_to_le32(0); |
| 668 | t->height = 0; |
| 669 | t->number_of_power_cords = 0; |
| 670 | t->contained_element_count = 0; |
| 671 | t->contained_element_record_length = 0; |
| 672 | SMBIOS_TABLE_SET_STR(3, sku_number_str, type3.sku); |
| 673 | |
| 674 | SMBIOS_BUILD_TABLE_POST; |
| 675 | } |
| 676 | |
| 677 | static void smbios_build_type_4_table(MachineState *ms, unsigned instance, |
| 678 | SmbiosEntryPointType ep_type, |
| 679 | Error **errp) |
| 680 | { |
| 681 | char sock_str[128]; |
| 682 | size_t tbl_len = SMBIOS_TYPE_4_LEN_V28; |
| 683 | unsigned threads_per_socket; |
| 684 | unsigned cores_per_socket; |
| 685 | |
| 686 | if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) { |
| 687 | tbl_len = SMBIOS_TYPE_4_LEN_V30; |
| 688 | } |
| 689 | |
| 690 | SMBIOS_BUILD_TABLE_PRE_SIZE(4, T4_BASE + instance, |
| 691 | true, tbl_len); /* required */ |
| 692 | |
| 693 | snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); |
| 694 | SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); |
| 695 | t->processor_type = 0x03; /* CPU */ |
| 696 | t->processor_family = 0xfe; /* use Processor Family 2 field */ |
| 697 | SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); |
| 698 | if (type4.processor_id == 0) { |
| 699 | t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); |
| 700 | t->processor_id[1] = cpu_to_le32(smbios_cpuid_features); |
| 701 | } else { |
| 702 | t->processor_id[0] = cpu_to_le32((uint32_t)type4.processor_id); |
| 703 | t->processor_id[1] = cpu_to_le32(type4.processor_id >> 32); |
| 704 | } |
| 705 | SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); |
| 706 | t->voltage = 0; |
| 707 | t->external_clock = cpu_to_le16(0); /* Unknown */ |
| 708 | t->max_speed = cpu_to_le16(type4.max_speed); |
| 709 | t->current_speed = cpu_to_le16(type4.current_speed); |
| 710 | t->status = 0x41; /* Socket populated, CPU enabled */ |
| 711 | t->processor_upgrade = 0x01; /* Other */ |
| 712 | t->l1_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ |
| 713 | t->l2_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ |
| 714 | t->l3_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ |
| 715 | SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); |
| 716 | SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); |
| 717 | SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); |
| 718 | |
| 719 | threads_per_socket = machine_topo_get_threads_per_socket(ms); |
| 720 | cores_per_socket = machine_topo_get_cores_per_socket(ms); |
| 721 | |
| 722 | t->core_count = (cores_per_socket > 255) ? 0xFF : cores_per_socket; |
| 723 | t->core_enabled = t->core_count; |
| 724 | |
| 725 | t->thread_count = (threads_per_socket > 255) ? 0xFF : threads_per_socket; |
| 726 | |
| 727 | t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ |
| 728 | t->processor_family2 = cpu_to_le16(type4.processor_family); |
| 729 | |
| 730 | if (tbl_len == SMBIOS_TYPE_4_LEN_V30) { |
| 731 | t->core_count2 = t->core_enabled2 = cpu_to_le16(cores_per_socket); |
| 732 | t->thread_count2 = cpu_to_le16(threads_per_socket); |
| 733 | } else if (t->core_count == 0xFF || t->thread_count == 0xFF) { |
| 734 | error_setg(errp, "SMBIOS 2.0 doesn't support number of processor " |
| 735 | "cores/threads more than 255, use " |
| 736 | "-machine smbios-entry-point-type=64 option to enable " |
| 737 | "SMBIOS 3.0 support"); |
| 738 | return; |
| 739 | } |
| 740 | |
| 741 | SMBIOS_BUILD_TABLE_POST; |
| 742 | smbios_type4_count++; |
| 743 | } |
| 744 | |
| 745 | static void smbios_build_type_8_table(void) |
| 746 | { |
| 747 | unsigned instance = 0; |
| 748 | struct type8_instance *t8; |
| 749 | |
| 750 | QTAILQ_FOREACH(t8, &type8, next) { |
| 751 | SMBIOS_BUILD_TABLE_PRE(8, T0_BASE + instance, true); |
| 752 | |
| 753 | SMBIOS_TABLE_SET_STR(8, internal_reference_str, t8->internal_reference); |
| 754 | SMBIOS_TABLE_SET_STR(8, external_reference_str, t8->external_reference); |
| 755 | /* most vendors seem to set this to None */ |
| 756 | t->internal_connector_type = 0x0; |
| 757 | t->external_connector_type = t8->connector_type; |
| 758 | t->port_type = t8->port_type; |
| 759 | |
| 760 | SMBIOS_BUILD_TABLE_POST; |
| 761 | instance++; |
| 762 | } |
| 763 | } |
| 764 | |
| 765 | static void smbios_build_type_9_table(Error **errp) |
| 766 | { |
| 767 | unsigned instance = 0; |
| 768 | struct type9_instance *t9; |
| 769 | |
| 770 | QTAILQ_FOREACH(t9, &type9, next) { |
| 771 | SMBIOS_BUILD_TABLE_PRE(9, T9_BASE + instance, true); |
| 772 | |
| 773 | SMBIOS_TABLE_SET_STR(9, slot_designation, t9->slot_designation); |
| 774 | t->slot_type = t9->slot_type; |
| 775 | t->slot_data_bus_width = t9->slot_data_bus_width; |
| 776 | t->current_usage = t9->current_usage; |
| 777 | t->slot_length = t9->slot_length; |
| 778 | t->slot_id = t9->slot_id; |
| 779 | t->slot_characteristics1 = t9->slot_characteristics1; |
| 780 | t->slot_characteristics2 = t9->slot_characteristics2; |
| 781 | |
| 782 | if (t9->pcidev) { |
| 783 | PCIDevice *pdev = NULL; |
| 784 | int rc = pci_qdev_find_device(t9->pcidev, &pdev); |
| 785 | if (rc != 0) { |
| 786 | error_setg(errp, |
| 787 | "No PCI device %s for SMBIOS type 9 entry %s", |
| 788 | t9->pcidev, t9->slot_designation); |
| 789 | return; |
| 790 | } |
| 791 | /* |
| 792 | * We only handle the case were the device is attached to |
| 793 | * the PCI root bus. The general case is more complex as |
| 794 | * bridges are enumerated later and the table would need |
| 795 | * to be updated at this moment. |
| 796 | */ |
| 797 | if (!pci_bus_is_root(pci_get_bus(pdev))) { |
| 798 | error_setg(errp, |
| 799 | "Cannot create type 9 entry for PCI device %s: " |
| 800 | "not attached to the root bus", |
| 801 | t9->pcidev); |
| 802 | return; |
| 803 | } |
| 804 | t->segment_group_number = cpu_to_le16(0); |
| 805 | t->bus_number = pci_dev_bus_num(pdev); |
| 806 | t->device_number = pdev->devfn; |
| 807 | } else { |
| 808 | /* |
| 809 | * Per SMBIOS spec, For slots that are not of the PCI, AGP, PCI-X, |
| 810 | * or PCI-Express type that do not have bus/device/function |
| 811 | * information, 0FFh should be populated in the fields of Segment |
| 812 | * Group Number, Bus Number, Device/Function Number. |
| 813 | */ |
| 814 | t->segment_group_number = 0xff; |
| 815 | t->bus_number = 0xff; |
| 816 | t->device_number = 0xff; |
| 817 | } |
| 818 | |
| 819 | SMBIOS_BUILD_TABLE_POST; |
| 820 | instance++; |
| 821 | } |
| 822 | } |
| 823 | |
| 824 | static void smbios_build_type_11_table(void) |
| 825 | { |
| 826 | char count_str[128]; |
| 827 | size_t i; |
| 828 | |
| 829 | if (type11.nvalues == 0) { |
| 830 | return; |
| 831 | } |
| 832 | |
| 833 | SMBIOS_BUILD_TABLE_PRE(11, T11_BASE, true); /* required */ |
| 834 | |
| 835 | snprintf(count_str, sizeof(count_str), "%zu", type11.nvalues); |
| 836 | t->count = type11.nvalues; |
| 837 | |
| 838 | for (i = 0; i < type11.nvalues; i++) { |
| 839 | SMBIOS_TABLE_SET_STR_LIST(11, type11.values[i]); |
| 840 | g_free(type11.values[i]); |
| 841 | type11.values[i] = NULL; |
| 842 | } |
| 843 | |
| 844 | SMBIOS_BUILD_TABLE_POST; |
| 845 | } |
| 846 | |
| 847 | #define MAX_T16_STD_SZ 0x80000000 /* 2T in Kilobytes */ |
| 848 | |
| 849 | static void smbios_build_type_16_table(unsigned dimm_cnt) |
| 850 | { |
| 851 | uint64_t size_kb; |
| 852 | |
| 853 | SMBIOS_BUILD_TABLE_PRE(16, T16_BASE, true); /* required */ |
| 854 | |
| 855 | t->location = 0x01; /* Other */ |
| 856 | t->use = 0x03; /* System memory */ |
| 857 | t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ |
| 858 | size_kb = QEMU_ALIGN_UP(current_machine->ram_size, KiB) / KiB; |
| 859 | if (size_kb < MAX_T16_STD_SZ) { |
| 860 | t->maximum_capacity = cpu_to_le32(size_kb); |
| 861 | t->extended_maximum_capacity = cpu_to_le64(0); |
| 862 | } else { |
| 863 | t->maximum_capacity = cpu_to_le32(MAX_T16_STD_SZ); |
| 864 | t->extended_maximum_capacity = cpu_to_le64(current_machine->ram_size); |
| 865 | } |
| 866 | t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ |
| 867 | t->number_of_memory_devices = cpu_to_le16(dimm_cnt); |
| 868 | |
| 869 | SMBIOS_BUILD_TABLE_POST; |
| 870 | } |
| 871 | |
| 872 | #define MAX_T17_STD_SZ 0x7FFF /* (32G - 1M), in Megabytes */ |
| 873 | #define MAX_T17_EXT_SZ 0x80000000 /* 2P, in Megabytes */ |
| 874 | |
| 875 | static void smbios_build_type_17_table(unsigned instance, uint64_t size) |
| 876 | { |
| 877 | char loc_str[128]; |
| 878 | uint64_t size_mb; |
| 879 | |
| 880 | SMBIOS_BUILD_TABLE_PRE(17, T17_BASE + instance, true); /* required */ |
| 881 | |
| 882 | t->physical_memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ |
| 883 | t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ |
| 884 | t->total_width = cpu_to_le16(0xFFFF); /* Unknown */ |
| 885 | t->data_width = cpu_to_le16(0xFFFF); /* Unknown */ |
| 886 | size_mb = QEMU_ALIGN_UP(size, MiB) / MiB; |
| 887 | if (size_mb < MAX_T17_STD_SZ) { |
| 888 | t->size = cpu_to_le16(size_mb); |
| 889 | t->extended_size = cpu_to_le32(0); |
| 890 | } else { |
| 891 | assert(size_mb < MAX_T17_EXT_SZ); |
| 892 | t->size = cpu_to_le16(MAX_T17_STD_SZ); |
| 893 | t->extended_size = cpu_to_le32(size_mb); |
| 894 | } |
| 895 | t->form_factor = 0x09; /* DIMM */ |
| 896 | t->device_set = 0; /* Not in a set */ |
| 897 | snprintf(loc_str, sizeof(loc_str), "%s %d", type17.loc_pfx, instance); |
| 898 | SMBIOS_TABLE_SET_STR(17, device_locator_str, loc_str); |
| 899 | SMBIOS_TABLE_SET_STR(17, bank_locator_str, type17.bank); |
| 900 | t->memory_type = 0x07; /* RAM */ |
| 901 | t->type_detail = cpu_to_le16(0x02); /* Other */ |
| 902 | t->speed = cpu_to_le16(type17.speed); |
| 903 | SMBIOS_TABLE_SET_STR(17, manufacturer_str, type17.manufacturer); |
| 904 | SMBIOS_TABLE_SET_STR(17, serial_number_str, type17.serial); |
| 905 | SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset); |
| 906 | SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part); |
| 907 | t->attributes = 0; /* Unknown */ |
| 908 | t->configured_clock_speed = t->speed; /* reuse value for max speed */ |
| 909 | t->minimum_voltage = cpu_to_le16(0); /* Unknown */ |
| 910 | t->maximum_voltage = cpu_to_le16(0); /* Unknown */ |
| 911 | t->configured_voltage = cpu_to_le16(0); /* Unknown */ |
| 912 | |
| 913 | SMBIOS_BUILD_TABLE_POST; |
| 914 | } |
| 915 | |
| 916 | static void smbios_build_type_19_table(unsigned instance, unsigned offset, |
| 917 | uint64_t start, uint64_t size) |
| 918 | { |
| 919 | uint64_t end, start_kb, end_kb; |
| 920 | |
| 921 | SMBIOS_BUILD_TABLE_PRE(19, T19_BASE + offset + instance, |
| 922 | true); /* required */ |
| 923 | |
| 924 | end = start + size - 1; |
| 925 | assert(end > start); |
| 926 | start_kb = start / KiB; |
| 927 | end_kb = end / KiB; |
| 928 | if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { |
| 929 | t->starting_address = cpu_to_le32(start_kb); |
| 930 | t->ending_address = cpu_to_le32(end_kb); |
| 931 | t->extended_starting_address = |
| 932 | t->extended_ending_address = cpu_to_le64(0); |
| 933 | } else { |
| 934 | t->starting_address = t->ending_address = cpu_to_le32(UINT32_MAX); |
| 935 | t->extended_starting_address = cpu_to_le64(start); |
| 936 | t->extended_ending_address = cpu_to_le64(end); |
| 937 | } |
| 938 | t->memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ |
| 939 | t->partition_width = 1; /* One device per row */ |
| 940 | |
| 941 | SMBIOS_BUILD_TABLE_POST; |
| 942 | } |
| 943 | |
| 944 | static void smbios_build_type_32_table(void) |
| 945 | { |
| 946 | SMBIOS_BUILD_TABLE_PRE(32, T32_BASE, true); /* required */ |
| 947 | |
| 948 | memset(t->reserved, 0, 6); |
| 949 | t->boot_status = 0; /* No errors detected */ |
| 950 | |
| 951 | SMBIOS_BUILD_TABLE_POST; |
| 952 | } |
| 953 | |
| 954 | static void smbios_build_type_41_table(Error **errp) |
| 955 | { |
| 956 | unsigned instance = 0; |
| 957 | struct type41_instance *t41; |
| 958 | |
| 959 | QTAILQ_FOREACH(t41, &type41, next) { |
| 960 | SMBIOS_BUILD_TABLE_PRE(41, T41_BASE + instance, true); |
| 961 | |
| 962 | SMBIOS_TABLE_SET_STR(41, reference_designation_str, t41->designation); |
| 963 | t->device_type = t41->kind; |
| 964 | t->device_type_instance = t41->instance; |
| 965 | t->segment_group_number = cpu_to_le16(0); |
| 966 | t->bus_number = 0; |
| 967 | t->device_number = 0; |
| 968 | |
| 969 | if (t41->pcidev) { |
| 970 | PCIDevice *pdev = NULL; |
| 971 | int rc = pci_qdev_find_device(t41->pcidev, &pdev); |
| 972 | if (rc != 0) { |
| 973 | error_setg(errp, |
| 974 | "No PCI device %s for SMBIOS type 41 entry %s", |
| 975 | t41->pcidev, t41->designation); |
| 976 | return; |
| 977 | } |
| 978 | /* |
| 979 | * We only handle the case were the device is attached to |
| 980 | * the PCI root bus. The general case is more complex as |
| 981 | * bridges are enumerated later and the table would need |
| 982 | * to be updated at this moment. |
| 983 | */ |
| 984 | if (!pci_bus_is_root(pci_get_bus(pdev))) { |
| 985 | error_setg(errp, |
| 986 | "Cannot create type 41 entry for PCI device %s: " |
| 987 | "not attached to the root bus", |
| 988 | t41->pcidev); |
| 989 | return; |
| 990 | } |
| 991 | t->segment_group_number = cpu_to_le16(0); |
| 992 | t->bus_number = pci_dev_bus_num(pdev); |
| 993 | t->device_number = pdev->devfn; |
| 994 | } |
| 995 | |
| 996 | SMBIOS_BUILD_TABLE_POST; |
| 997 | instance++; |
| 998 | } |
| 999 | } |
| 1000 | |
| 1001 | static void smbios_build_type_127_table(void) |
| 1002 | { |
| 1003 | SMBIOS_BUILD_TABLE_PRE(127, T127_BASE, true); /* required */ |
| 1004 | SMBIOS_BUILD_TABLE_POST; |
| 1005 | } |
| 1006 | |
| 1007 | void smbios_set_cpuid(uint32_t version, uint32_t features) |
| 1008 | { |
| 1009 | smbios_cpuid_version = version; |
| 1010 | smbios_cpuid_features = features; |
| 1011 | } |
| 1012 | |
| 1013 | #define SMBIOS_SET_DEFAULT(field, value) \ |
| 1014 | if (!field) { \ |
| 1015 | field = value; \ |
| 1016 | } |
| 1017 | |
| 1018 | void smbios_set_default_processor_family(uint16_t processor_family) |
| 1019 | { |
| 1020 | if (type4.processor_family <= 0x01) { |
| 1021 | type4.processor_family = processor_family; |
| 1022 | } |
| 1023 | } |
| 1024 | |
| 1025 | void smbios_set_defaults(const char *manufacturer, const char *product, |
| 1026 | const char *version) |
| 1027 | { |
| 1028 | smbios_have_defaults = true; |
| 1029 | |
| 1030 | SMBIOS_SET_DEFAULT(smbios_type1.manufacturer, manufacturer); |
| 1031 | SMBIOS_SET_DEFAULT(smbios_type1.product, product); |
| 1032 | SMBIOS_SET_DEFAULT(smbios_type1.version, version); |
| 1033 | SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); |
| 1034 | SMBIOS_SET_DEFAULT(type2.product, product); |
| 1035 | SMBIOS_SET_DEFAULT(type2.version, version); |
| 1036 | SMBIOS_SET_DEFAULT(type3.manufacturer, manufacturer); |
| 1037 | SMBIOS_SET_DEFAULT(type3.version, version); |
| 1038 | SMBIOS_SET_DEFAULT(type4.sock_pfx, "CPU"); |
| 1039 | SMBIOS_SET_DEFAULT(type4.manufacturer, manufacturer); |
| 1040 | SMBIOS_SET_DEFAULT(type4.version, version); |
| 1041 | SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); |
| 1042 | SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer); |
| 1043 | } |
| 1044 | |
| 1045 | static void smbios_entry_point_setup(SmbiosEntryPointType ep_type) |
| 1046 | { |
| 1047 | switch (ep_type) { |
| 1048 | case SMBIOS_ENTRY_POINT_TYPE_32: |
| 1049 | memcpy(ep.ep21.anchor_string, "_SM_", 4); |
| 1050 | memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5); |
| 1051 | ep.ep21.length = sizeof(struct smbios_21_entry_point); |
| 1052 | ep.ep21.entry_point_revision = 0; /* formatted_area reserved */ |
| 1053 | memset(ep.ep21.formatted_area, 0, 5); |
| 1054 | |
| 1055 | /* compliant with smbios spec v2.8 */ |
| 1056 | ep.ep21.smbios_major_version = 2; |
| 1057 | ep.ep21.smbios_minor_version = 8; |
| 1058 | ep.ep21.smbios_bcd_revision = 0x28; |
| 1059 | |
| 1060 | /* set during table construction, but BIOS may override: */ |
| 1061 | ep.ep21.structure_table_length = cpu_to_le16(smbios_tables_len); |
| 1062 | ep.ep21.max_structure_size = cpu_to_le16(smbios_table_max); |
| 1063 | ep.ep21.number_of_structures = cpu_to_le16(smbios_table_cnt); |
| 1064 | |
| 1065 | /* BIOS must recalculate */ |
| 1066 | ep.ep21.checksum = 0; |
| 1067 | ep.ep21.intermediate_checksum = 0; |
| 1068 | ep.ep21.structure_table_address = cpu_to_le32(0); |
| 1069 | |
| 1070 | break; |
| 1071 | case SMBIOS_ENTRY_POINT_TYPE_64: |
| 1072 | memcpy(ep.ep30.anchor_string, "_SM3_", 5); |
| 1073 | ep.ep30.length = sizeof(struct smbios_30_entry_point); |
| 1074 | ep.ep30.entry_point_revision = 1; |
| 1075 | ep.ep30.reserved = 0; |
| 1076 | |
| 1077 | /* compliant with smbios spec 3.0 */ |
| 1078 | ep.ep30.smbios_major_version = 3; |
| 1079 | ep.ep30.smbios_minor_version = 0; |
| 1080 | ep.ep30.smbios_doc_rev = 0; |
| 1081 | |
| 1082 | /* set during table construct, but BIOS might override */ |
| 1083 | ep.ep30.structure_table_max_size = cpu_to_le32(smbios_tables_len); |
| 1084 | |
| 1085 | /* BIOS must recalculate */ |
| 1086 | ep.ep30.checksum = 0; |
| 1087 | ep.ep30.structure_table_address = cpu_to_le64(0); |
| 1088 | |
| 1089 | break; |
| 1090 | default: |
| 1091 | abort(); |
| 1092 | break; |
| 1093 | } |
| 1094 | } |
| 1095 | |
| 1096 | static bool smbios_get_tables_ep(MachineState *ms, |
| 1097 | SmbiosEntryPointType ep_type, |
| 1098 | const struct smbios_phys_mem_area *mem_array, |
| 1099 | const unsigned int mem_array_size, |
| 1100 | uint8_t **tables, size_t *tables_len, |
| 1101 | uint8_t **anchor, size_t *anchor_len, |
| 1102 | Error **errp) |
| 1103 | { |
| 1104 | unsigned i, dimm_cnt, offset; |
| 1105 | MachineClass *mc = MACHINE_GET_CLASS(ms); |
| 1106 | ERRP_GUARD(); |
| 1107 | |
| 1108 | assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 || |
| 1109 | ep_type == SMBIOS_ENTRY_POINT_TYPE_64); |
| 1110 | |
| 1111 | g_free(smbios_tables); |
| 1112 | smbios_type4_count = 0; |
| 1113 | smbios_tables = g_memdup2(usr_blobs, usr_blobs_len); |
| 1114 | smbios_tables_len = usr_blobs_len; |
| 1115 | smbios_table_max = usr_table_max; |
| 1116 | smbios_table_cnt = usr_table_cnt; |
| 1117 | |
| 1118 | smbios_build_type_0_table(); |
| 1119 | smbios_build_type_1_table(); |
| 1120 | smbios_build_type_2_table(); |
| 1121 | smbios_build_type_3_table(); |
| 1122 | |
| 1123 | assert(ms->smp.sockets >= 1); |
| 1124 | |
| 1125 | for (i = 0; i < ms->smp.sockets; i++) { |
| 1126 | smbios_build_type_4_table(ms, i, ep_type, errp); |
| 1127 | if (*errp) { |
| 1128 | goto err_exit; |
| 1129 | } |
| 1130 | } |
| 1131 | |
| 1132 | smbios_build_type_8_table(); |
| 1133 | smbios_build_type_9_table(errp); |
| 1134 | smbios_build_type_11_table(); |
| 1135 | |
| 1136 | #define GET_DIMM_SZ ((i < dimm_cnt - 1) ? mc->smbios_memory_device_size \ |
| 1137 | : ((current_machine->ram_size - 1) % mc->smbios_memory_device_size) + 1) |
| 1138 | |
| 1139 | dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, |
| 1140 | mc->smbios_memory_device_size) / |
| 1141 | mc->smbios_memory_device_size; |
| 1142 | |
| 1143 | /* |
| 1144 | * The offset determines if we need to keep additional space between |
| 1145 | * table 17 and table 19 header handle numbers so that they do |
| 1146 | * not overlap. For example, for a VM with larger than 8 TB guest |
| 1147 | * memory and DIMM like chunks of 16 GiB, the default space between |
| 1148 | * the two tables (T19_BASE - T17_BASE = 512) is not enough. |
| 1149 | */ |
| 1150 | offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ |
| 1151 | dimm_cnt - (T19_BASE - T17_BASE) : 0; |
| 1152 | |
| 1153 | smbios_build_type_16_table(dimm_cnt); |
| 1154 | |
| 1155 | for (i = 0; i < dimm_cnt; i++) { |
| 1156 | smbios_build_type_17_table(i, GET_DIMM_SZ); |
| 1157 | } |
| 1158 | |
| 1159 | for (i = 0; i < mem_array_size; i++) { |
| 1160 | smbios_build_type_19_table(i, offset, mem_array[i].address, |
| 1161 | mem_array[i].length); |
| 1162 | } |
| 1163 | |
| 1164 | /* |
| 1165 | * make sure 16 bit handle numbers in the headers of tables 19 |
| 1166 | * and 32 do not overlap. |
| 1167 | */ |
| 1168 | assert((mem_array_size + offset) < (T32_BASE - T19_BASE)); |
| 1169 | |
| 1170 | smbios_build_type_32_table(); |
| 1171 | smbios_build_type_38_table(); |
| 1172 | smbios_build_type_41_table(errp); |
| 1173 | smbios_build_type_127_table(); |
| 1174 | |
| 1175 | if (!smbios_check_type4_count(ms->smp.sockets, errp)) { |
| 1176 | goto err_exit; |
| 1177 | } |
| 1178 | if (!smbios_validate_table(ep_type, errp)) { |
| 1179 | goto err_exit; |
| 1180 | } |
| 1181 | smbios_entry_point_setup(ep_type); |
| 1182 | |
| 1183 | /* return tables blob and entry point (anchor), and their sizes */ |
| 1184 | *tables = smbios_tables; |
| 1185 | *tables_len = smbios_tables_len; |
| 1186 | *anchor = (uint8_t *)&ep; |
| 1187 | /* calculate length based on anchor string */ |
| 1188 | if (!strncmp((char *)&ep, "_SM_", 4)) { |
| 1189 | *anchor_len = sizeof(struct smbios_21_entry_point); |
| 1190 | } else if (!strncmp((char *)&ep, "_SM3_", 5)) { |
| 1191 | *anchor_len = sizeof(struct smbios_30_entry_point); |
| 1192 | } else { |
| 1193 | abort(); |
| 1194 | } |
| 1195 | |
| 1196 | return true; |
| 1197 | err_exit: |
| 1198 | g_free(smbios_tables); |
| 1199 | smbios_tables = NULL; |
| 1200 | return false; |
| 1201 | } |
| 1202 | |
| 1203 | void smbios_get_tables(MachineState *ms, |
| 1204 | SmbiosEntryPointType ep_type, |
| 1205 | const struct smbios_phys_mem_area *mem_array, |
| 1206 | const unsigned int mem_array_size, |
| 1207 | uint8_t **tables, size_t *tables_len, |
| 1208 | uint8_t **anchor, size_t *anchor_len, |
| 1209 | Error **errp) |
| 1210 | { |
| 1211 | Error *local_err = NULL; |
| 1212 | bool is_valid; |
| 1213 | ERRP_GUARD(); |
| 1214 | |
| 1215 | switch (ep_type) { |
| 1216 | case SMBIOS_ENTRY_POINT_TYPE_AUTO: |
| 1217 | case SMBIOS_ENTRY_POINT_TYPE_32: |
| 1218 | is_valid = smbios_get_tables_ep(ms, SMBIOS_ENTRY_POINT_TYPE_32, |
| 1219 | mem_array, mem_array_size, |
| 1220 | tables, tables_len, |
| 1221 | anchor, anchor_len, |
| 1222 | &local_err); |
| 1223 | if (is_valid || ep_type != SMBIOS_ENTRY_POINT_TYPE_AUTO) { |
| 1224 | break; |
| 1225 | } |
| 1226 | /* |
| 1227 | * fall through in case AUTO endpoint is selected and |
| 1228 | * SMBIOS 2.x tables can't be generated, to try if SMBIOS 3.x |
| 1229 | * tables would work |
| 1230 | */ |
| 1231 | case SMBIOS_ENTRY_POINT_TYPE_64: |
| 1232 | error_free(local_err); |
| 1233 | local_err = NULL; |
| 1234 | is_valid = smbios_get_tables_ep(ms, SMBIOS_ENTRY_POINT_TYPE_64, |
| 1235 | mem_array, mem_array_size, |
| 1236 | tables, tables_len, |
| 1237 | anchor, anchor_len, |
| 1238 | &local_err); |
| 1239 | break; |
| 1240 | default: |
| 1241 | abort(); |
| 1242 | } |
| 1243 | if (!is_valid) { |
| 1244 | error_propagate(errp, local_err); |
| 1245 | } |
| 1246 | } |
| 1247 | |
| 1248 | static void save_opt(const char **dest, QemuOpts *opts, const char *name) |
| 1249 | { |
| 1250 | const char *val = qemu_opt_get(opts, name); |
| 1251 | |
| 1252 | if (val) { |
| 1253 | *dest = val; |
| 1254 | } |
| 1255 | } |
| 1256 | |
| 1257 | |
| 1258 | struct opt_list { |
| 1259 | size_t *ndest; |
| 1260 | char ***dest; |
| 1261 | }; |
| 1262 | |
| 1263 | static int save_opt_one(void *opaque, |
| 1264 | const char *name, const char *value, |
| 1265 | Error **errp) |
| 1266 | { |
| 1267 | struct opt_list *opt = opaque; |
| 1268 | |
| 1269 | if (g_str_equal(name, "path")) { |
| 1270 | g_autoptr(GByteArray) data = g_byte_array_new(); |
| 1271 | g_autofree char *buf = g_new(char, 4096); |
| 1272 | ssize_t ret; |
| 1273 | int fd = qemu_open(value, O_RDONLY, errp); |
| 1274 | if (fd < 0) { |
| 1275 | return -1; |
| 1276 | } |
| 1277 | |
| 1278 | while (1) { |
| 1279 | ret = read(fd, buf, 4096); |
| 1280 | if (ret == 0) { |
| 1281 | break; |
| 1282 | } |
| 1283 | if (ret < 0) { |
| 1284 | error_setg_errno(errp, errno, "Unable to read from %s", |
| 1285 | value); |
| 1286 | qemu_close(fd); |
| 1287 | return -1; |
| 1288 | } |
| 1289 | if (memchr(buf, '\0', ret)) { |
| 1290 | error_setg(errp, "NUL in OEM strings value in %s", value); |
| 1291 | qemu_close(fd); |
| 1292 | return -1; |
| 1293 | } |
| 1294 | g_byte_array_append(data, (guint8 *)buf, ret); |
| 1295 | } |
| 1296 | |
| 1297 | buf[0] = '\0'; |
| 1298 | g_byte_array_append(data, (guint8 *)buf, 1); |
| 1299 | |
| 1300 | qemu_close(fd); |
| 1301 | |
| 1302 | *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); |
| 1303 | (*opt->dest)[*opt->ndest] = (char *)g_byte_array_free(data, FALSE); |
| 1304 | (*opt->ndest)++; |
| 1305 | data = NULL; |
| 1306 | } else if (g_str_equal(name, "value")) { |
| 1307 | *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); |
| 1308 | (*opt->dest)[*opt->ndest] = g_strdup(value); |
| 1309 | (*opt->ndest)++; |
| 1310 | } else if (!g_str_equal(name, "type")) { |
| 1311 | error_setg(errp, "Unexpected option %s", name); |
| 1312 | return -1; |
| 1313 | } |
| 1314 | |
| 1315 | return 0; |
| 1316 | } |
| 1317 | |
| 1318 | static bool save_opt_list(size_t *ndest, char ***dest, QemuOpts *opts, |
| 1319 | Error **errp) |
| 1320 | { |
| 1321 | struct opt_list opt = { |
| 1322 | ndest, dest, |
| 1323 | }; |
| 1324 | if (!qemu_opt_foreach(opts, save_opt_one, &opt, errp)) { |
| 1325 | return false; |
| 1326 | } |
| 1327 | return true; |
| 1328 | } |
| 1329 | |
| 1330 | void smbios_entry_add(QemuOpts *opts, Error **errp) |
| 1331 | { |
| 1332 | const char *val; |
| 1333 | |
| 1334 | val = qemu_opt_get(opts, "file"); |
| 1335 | if (val) { |
| 1336 | struct smbios_structure_header *header; |
| 1337 | size_t size; |
| 1338 | |
| 1339 | if (!qemu_opts_validate(opts, qemu_smbios_file_opts, errp)) { |
| 1340 | return; |
| 1341 | } |
| 1342 | |
| 1343 | size = get_image_size(val, NULL); |
| 1344 | if (size == -1 || size < sizeof(struct smbios_structure_header)) { |
| 1345 | error_setg(errp, "Cannot read SMBIOS file %s", val); |
| 1346 | return; |
| 1347 | } |
| 1348 | |
| 1349 | /* |
| 1350 | * NOTE: standard double '\0' terminator expected, per smbios spec. |
| 1351 | * (except in legacy mode, where the second '\0' is implicit and |
| 1352 | * will be inserted by the BIOS). |
| 1353 | */ |
| 1354 | usr_blobs = g_realloc(usr_blobs, usr_blobs_len + size); |
| 1355 | header = (struct smbios_structure_header *)(usr_blobs + |
| 1356 | usr_blobs_len); |
| 1357 | |
| 1358 | if (load_image_size(val, (uint8_t *)header, size) != size) { |
| 1359 | error_setg(errp, "Failed to load SMBIOS file %s", val); |
| 1360 | return; |
| 1361 | } |
| 1362 | |
| 1363 | if (header->type <= SMBIOS_MAX_TYPE) { |
| 1364 | if (test_bit(header->type, smbios_have_fields_bitmap)) { |
| 1365 | error_setg(errp, |
| 1366 | "can't load type %d struct, fields already specified!", |
| 1367 | header->type); |
| 1368 | return; |
| 1369 | } |
| 1370 | set_bit(header->type, smbios_have_binfile_bitmap); |
| 1371 | } |
| 1372 | |
| 1373 | if (header->type == 4) { |
| 1374 | smbios_type4_count++; |
| 1375 | } |
| 1376 | |
| 1377 | /* |
| 1378 | * preserve blob size for legacy mode so it could build its |
| 1379 | * blobs flavor from 'usr_blobs' |
| 1380 | */ |
| 1381 | smbios_add_usr_blob_size(size); |
| 1382 | |
| 1383 | usr_blobs_len += size; |
| 1384 | if (size > usr_table_max) { |
| 1385 | usr_table_max = size; |
| 1386 | } |
| 1387 | usr_table_cnt++; |
| 1388 | |
| 1389 | return; |
| 1390 | } |
| 1391 | |
| 1392 | val = qemu_opt_get(opts, "type"); |
| 1393 | if (val) { |
| 1394 | unsigned long type = strtoul(val, NULL, 0); |
| 1395 | |
| 1396 | if (type > SMBIOS_MAX_TYPE) { |
| 1397 | error_setg(errp, "out of range!"); |
| 1398 | return; |
| 1399 | } |
| 1400 | |
| 1401 | if (test_bit(type, smbios_have_binfile_bitmap)) { |
| 1402 | error_setg(errp, "can't add fields, binary file already loaded!"); |
| 1403 | return; |
| 1404 | } |
| 1405 | set_bit(type, smbios_have_fields_bitmap); |
| 1406 | |
| 1407 | switch (type) { |
| 1408 | case 0: |
| 1409 | if (!qemu_opts_validate(opts, qemu_smbios_type0_opts, errp)) { |
| 1410 | return; |
| 1411 | } |
| 1412 | save_opt(&smbios_type0.vendor, opts, "vendor"); |
| 1413 | save_opt(&smbios_type0.version, opts, "version"); |
| 1414 | save_opt(&smbios_type0.date, opts, "date"); |
| 1415 | smbios_type0.uefi = qemu_opt_get_bool(opts, "uefi", false); |
| 1416 | smbios_type0.vm = qemu_opt_get_bool(opts, "vm", true); |
| 1417 | |
| 1418 | val = qemu_opt_get(opts, "release"); |
| 1419 | if (val) { |
| 1420 | if (sscanf(val, "%hhu.%hhu", &smbios_type0.major, |
| 1421 | &smbios_type0.minor) != 2) { |
| 1422 | error_setg(errp, "Invalid release"); |
| 1423 | return; |
| 1424 | } |
| 1425 | smbios_type0.have_major_minor = true; |
| 1426 | } |
| 1427 | return; |
| 1428 | case 1: |
| 1429 | if (!qemu_opts_validate(opts, qemu_smbios_type1_opts, errp)) { |
| 1430 | return; |
| 1431 | } |
| 1432 | save_opt(&smbios_type1.manufacturer, opts, "manufacturer"); |
| 1433 | save_opt(&smbios_type1.product, opts, "product"); |
| 1434 | save_opt(&smbios_type1.version, opts, "version"); |
| 1435 | save_opt(&smbios_type1.serial, opts, "serial"); |
| 1436 | save_opt(&smbios_type1.sku, opts, "sku"); |
| 1437 | save_opt(&smbios_type1.family, opts, "family"); |
| 1438 | |
| 1439 | val = qemu_opt_get(opts, "uuid"); |
| 1440 | if (val) { |
| 1441 | if (qemu_uuid_parse(val, &qemu_uuid) != 0) { |
| 1442 | error_setg(errp, "Invalid UUID"); |
| 1443 | return; |
| 1444 | } |
| 1445 | qemu_uuid_set = true; |
| 1446 | } |
| 1447 | return; |
| 1448 | case 2: |
| 1449 | if (!qemu_opts_validate(opts, qemu_smbios_type2_opts, errp)) { |
| 1450 | return; |
| 1451 | } |
| 1452 | save_opt(&type2.manufacturer, opts, "manufacturer"); |
| 1453 | save_opt(&type2.product, opts, "product"); |
| 1454 | save_opt(&type2.version, opts, "version"); |
| 1455 | save_opt(&type2.serial, opts, "serial"); |
| 1456 | save_opt(&type2.asset, opts, "asset"); |
| 1457 | save_opt(&type2.location, opts, "location"); |
| 1458 | return; |
| 1459 | case 3: |
| 1460 | if (!qemu_opts_validate(opts, qemu_smbios_type3_opts, errp)) { |
| 1461 | return; |
| 1462 | } |
| 1463 | save_opt(&type3.manufacturer, opts, "manufacturer"); |
| 1464 | save_opt(&type3.version, opts, "version"); |
| 1465 | save_opt(&type3.serial, opts, "serial"); |
| 1466 | save_opt(&type3.asset, opts, "asset"); |
| 1467 | save_opt(&type3.sku, opts, "sku"); |
| 1468 | return; |
| 1469 | case 4: |
| 1470 | if (!qemu_opts_validate(opts, qemu_smbios_type4_opts, errp)) { |
| 1471 | return; |
| 1472 | } |
| 1473 | save_opt(&type4.sock_pfx, opts, "sock_pfx"); |
| 1474 | type4.processor_family = qemu_opt_get_number(opts, |
| 1475 | "processor-family", |
| 1476 | 0x01 /* Other */); |
| 1477 | save_opt(&type4.manufacturer, opts, "manufacturer"); |
| 1478 | save_opt(&type4.version, opts, "version"); |
| 1479 | save_opt(&type4.serial, opts, "serial"); |
| 1480 | save_opt(&type4.asset, opts, "asset"); |
| 1481 | save_opt(&type4.part, opts, "part"); |
| 1482 | /* If the value is 0, it will take the value from the CPU model. */ |
| 1483 | type4.processor_id = qemu_opt_get_number(opts, "processor-id", 0); |
| 1484 | type4.max_speed = qemu_opt_get_number(opts, "max-speed", |
| 1485 | DEFAULT_CPU_SPEED); |
| 1486 | type4.current_speed = qemu_opt_get_number(opts, "current-speed", |
| 1487 | DEFAULT_CPU_SPEED); |
| 1488 | if (type4.max_speed > UINT16_MAX || |
| 1489 | type4.current_speed > UINT16_MAX) { |
| 1490 | error_setg(errp, "SMBIOS CPU speed is too large (> %d)", |
| 1491 | UINT16_MAX); |
| 1492 | } |
| 1493 | return; |
| 1494 | case 8: |
| 1495 | if (!qemu_opts_validate(opts, qemu_smbios_type8_opts, errp)) { |
| 1496 | return; |
| 1497 | } |
| 1498 | struct type8_instance *t8_i; |
| 1499 | t8_i = g_new0(struct type8_instance, 1); |
| 1500 | save_opt(&t8_i->internal_reference, opts, "internal_reference"); |
| 1501 | save_opt(&t8_i->external_reference, opts, "external_reference"); |
| 1502 | t8_i->connector_type = qemu_opt_get_number(opts, |
| 1503 | "connector_type", 0); |
| 1504 | t8_i->port_type = qemu_opt_get_number(opts, "port_type", 0); |
| 1505 | QTAILQ_INSERT_TAIL(&type8, t8_i, next); |
| 1506 | return; |
| 1507 | case 9: { |
| 1508 | if (!qemu_opts_validate(opts, qemu_smbios_type9_opts, errp)) { |
| 1509 | return; |
| 1510 | } |
| 1511 | struct type9_instance *t; |
| 1512 | t = g_new0(struct type9_instance, 1); |
| 1513 | save_opt(&t->slot_designation, opts, "slot_designation"); |
| 1514 | t->slot_type = qemu_opt_get_number(opts, "slot_type", 0); |
| 1515 | t->slot_data_bus_width = |
| 1516 | qemu_opt_get_number(opts, "slot_data_bus_width", 0); |
| 1517 | t->current_usage = qemu_opt_get_number(opts, "current_usage", 0); |
| 1518 | t->slot_length = qemu_opt_get_number(opts, "slot_length", 0); |
| 1519 | t->slot_id = qemu_opt_get_number(opts, "slot_id", 0); |
| 1520 | t->slot_characteristics1 = |
| 1521 | qemu_opt_get_number(opts, "slot_characteristics1", 0); |
| 1522 | t->slot_characteristics2 = |
| 1523 | qemu_opt_get_number(opts, "slot_characteristics2", 0); |
| 1524 | save_opt(&t->pcidev, opts, "pcidev"); |
| 1525 | QTAILQ_INSERT_TAIL(&type9, t, next); |
| 1526 | return; |
| 1527 | } |
| 1528 | case 11: |
| 1529 | if (!qemu_opts_validate(opts, qemu_smbios_type11_opts, errp)) { |
| 1530 | return; |
| 1531 | } |
| 1532 | if (!save_opt_list(&type11.nvalues, &type11.values, opts, errp)) { |
| 1533 | return; |
| 1534 | } |
| 1535 | return; |
| 1536 | case 17: |
| 1537 | if (!qemu_opts_validate(opts, qemu_smbios_type17_opts, errp)) { |
| 1538 | return; |
| 1539 | } |
| 1540 | save_opt(&type17.loc_pfx, opts, "loc_pfx"); |
| 1541 | save_opt(&type17.bank, opts, "bank"); |
| 1542 | save_opt(&type17.manufacturer, opts, "manufacturer"); |
| 1543 | save_opt(&type17.serial, opts, "serial"); |
| 1544 | save_opt(&type17.asset, opts, "asset"); |
| 1545 | save_opt(&type17.part, opts, "part"); |
| 1546 | type17.speed = qemu_opt_get_number(opts, "speed", 0); |
| 1547 | return; |
| 1548 | case 41: { |
| 1549 | struct type41_instance *t41_i; |
| 1550 | Error *local_err = NULL; |
| 1551 | |
| 1552 | if (!qemu_opts_validate(opts, qemu_smbios_type41_opts, errp)) { |
| 1553 | return; |
| 1554 | } |
| 1555 | t41_i = g_new0(struct type41_instance, 1); |
| 1556 | save_opt(&t41_i->designation, opts, "designation"); |
| 1557 | t41_i->kind = qapi_enum_parse(&type41_kind_lookup, |
| 1558 | qemu_opt_get(opts, "kind"), |
| 1559 | 0, &local_err) + 1; |
| 1560 | t41_i->kind |= 0x80; /* enabled */ |
| 1561 | if (local_err != NULL) { |
| 1562 | error_propagate(errp, local_err); |
| 1563 | g_free(t41_i); |
| 1564 | return; |
| 1565 | } |
| 1566 | t41_i->instance = qemu_opt_get_number(opts, "instance", 1); |
| 1567 | save_opt(&t41_i->pcidev, opts, "pcidev"); |
| 1568 | |
| 1569 | QTAILQ_INSERT_TAIL(&type41, t41_i, next); |
| 1570 | return; |
| 1571 | } |
| 1572 | default: |
| 1573 | error_setg(errp, |
| 1574 | "Don't know how to build fields for SMBIOS type %ld", |
| 1575 | type); |
| 1576 | return; |
| 1577 | } |
| 1578 | } |
| 1579 | |
| 1580 | error_setg(errp, "Must specify type= or file="); |
| 1581 | } |