From cfe25e2bcada943984e27ee63918fd75dc4563ac Mon Sep 17 00:00:00 2001 From: Michael S. Tsirkin Date: Mon, 2 Sep 2013 11:41:37 +0300 Subject: range: add Range to typedefs will help simplify header dependencies. Signed-off-by: Michael S. Tsirkin --- include/qemu/range.h | 2 +- include/qemu/typedefs.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/qemu/range.h b/include/qemu/range.h index b76cc0df09..4a0780d4eb 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -2,6 +2,7 @@ #define QEMU_RANGE_H #include +#include /* * Operations on 64 bit address ranges. @@ -15,7 +16,6 @@ struct Range { uint64_t begin; /* First byte of the range, or 0 if empty. */ uint64_t end; /* 1 + the last byte. 0 if range empty or ends at ~0x0LL. */ }; -typedef struct Range Range; /* Get last byte of a range from offset + length. * Undefined for ranges that wrap around 0. */ diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 3205540059..a4c1b84d69 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -68,5 +68,6 @@ typedef struct QEMUSGList QEMUSGList; typedef struct SHPCDevice SHPCDevice; typedef struct FWCfgState FWCfgState; typedef struct PcGuestInfo PcGuestInfo; +typedef struct Range Range; #endif /* QEMU_TYPEDEFS_H */ -- cgit v1.2.3-55-g7522 From c5a22c4344f17169bb20e122e9d935c62aedc063 Mon Sep 17 00:00:00 2001 From: Michael S. Tsirkin Date: Mon, 2 Sep 2013 11:04:39 +0300 Subject: range: add min/max operations on ranges Signed-off-by: Michael S. Tsirkin --- include/qemu/range.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include') diff --git a/include/qemu/range.h b/include/qemu/range.h index 4a0780d4eb..aae9720161 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -17,6 +17,24 @@ struct Range { uint64_t end; /* 1 + the last byte. 0 if range empty or ends at ~0x0LL. */ }; +static inline void range_extend(Range *range, Range *extend_by) +{ + if (!extend_by->begin && !extend_by->end) { + return; + } + if (!range->begin && !range->end) { + *range = *extend_by; + return; + } + if (range->begin > extend_by->begin) { + range->begin = extend_by->begin; + } + /* Compare last byte in case region ends at ~0x0LL */ + if (range->end - 1 < extend_by->end - 1) { + range->end = extend_by->end; + } +} + /* Get last byte of a range from offset + length. * Undefined for ranges that wrap around 0. */ static inline uint64_t range_get_last(uint64_t offset, uint64_t len) -- cgit v1.2.3-55-g7522 From 438640695723f33be6d0081ac1e690aa40975c39 Mon Sep 17 00:00:00 2001 From: Michael S. Tsirkin Date: Mon, 2 Sep 2013 11:37:02 +0300 Subject: pci: add helper to retrieve the 64-bit range Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/pci/pci.h | 1 + 2 files changed, 51 insertions(+) (limited to 'include') diff --git a/hw/pci/pci.c b/hw/pci/pci.c index ad1c1ca91e..52cbab7dc2 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2257,6 +2257,56 @@ void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque) bus->iommu_opaque = opaque; } +static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) +{ + Range *range = opaque; + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND); + int r; + + if (!(cmd & PCI_COMMAND_MEMORY)) { + return; + } + + if (pc->is_bridge) { + pcibus_t base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + pcibus_t limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + base = MAX(base, 0x1ULL << 32); + + if (limit >= base) { + Range pref_range; + pref_range.begin = base; + pref_range.end = limit + 1; + range_extend(range, &pref_range); + } + } + for (r = 0; r < PCI_NUM_REGIONS; ++r) { + PCIIORegion *region = &dev->io_regions[r]; + Range region_range; + + if (!region->size || + (region->type & PCI_BASE_ADDRESS_SPACE_IO) || + !(region->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { + continue; + } + region_range.begin = pci_get_quad(dev->config + pci_bar(dev, r)); + region_range.end = region_range.begin + region->size; + + region_range.begin = MAX(region_range.begin, 0x1ULL << 32); + + if (region_range.end - 1 >= region_range.begin) { + range_extend(range, ®ion_range); + } + } +} + +void pci_bus_get_w64_range(PCIBus *bus, Range *range) +{ + range->begin = range->end = 0; + pci_for_each_device_under_bus(bus, pci_dev_get_w64, range); +} + static const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 37979aa723..4b90e5d00b 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -397,6 +397,7 @@ const char *pci_root_bus_path(PCIDevice *dev); PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); int pci_qdev_find_device(const char *id, PCIDevice **pdev); PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, const char *devaddr); +void pci_bus_get_w64_range(PCIBus *bus, Range *range); int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned int *slotp, unsigned int *funcp); -- cgit v1.2.3-55-g7522 From 351a6a73ca7a9123f0dfd6c6f85fd01e82fe3741 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 16 Aug 2013 15:18:28 +0200 Subject: smbios: Normalize smbios_entry_add()'s error handling to exit(1) It exits on all error conditions but one, where it returns -1. Normalize, and return void. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Michael S. Tsirkin --- arch_init.c | 4 +--- hw/i386/smbios.c | 10 +++++----- include/hw/i386/smbios.h | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch_init.c b/arch_init.c index e47e1399bb..6ae8eb6340 100644 --- a/arch_init.c +++ b/arch_init.c @@ -1137,9 +1137,7 @@ void do_acpitable_option(const QemuOpts *opts) void do_smbios_option(const char *optarg) { #ifdef TARGET_I386 - if (smbios_entry_add(optarg) < 0) { - exit(1); - } + smbios_entry_add(optarg); #endif } diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index e708cb8919..0608aee3db 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -183,7 +183,7 @@ static void smbios_build_type_1_fields(const char *t) buf, strlen(buf) + 1); } -int smbios_entry_add(const char *t) +void smbios_entry_add(const char *t) { char buf[1024]; @@ -222,7 +222,7 @@ int smbios_entry_add(const char *t) smbios_entries_len += sizeof(*table) + size; (*(uint16_t *)smbios_entries) = cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); - return 0; + return; } if (get_param_value(buf, sizeof(buf), "type", t)) { @@ -230,10 +230,10 @@ int smbios_entry_add(const char *t) switch (type) { case 0: smbios_build_type_0_fields(t); - return 0; + return; case 1: smbios_build_type_1_fields(t); - return 0; + return; default: error_report("Don't know how to build fields for SMBIOS type %ld", type); @@ -242,5 +242,5 @@ int smbios_entry_add(const char *t) } error_report("Must specify type= or file="); - return -1; + exit(1); } diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 9babeaf270..56c6108848 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -13,7 +13,7 @@ * */ -int smbios_entry_add(const char *t); +void smbios_entry_add(const char *t); void smbios_add_field(int type, int offset, const void *data, size_t len); uint8_t *smbios_get_table(size_t *length); -- cgit v1.2.3-55-g7522 From 4f953d2fc806f1ba6fa76f01dfd121fe7d0dc4a7 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 16 Aug 2013 15:18:29 +0200 Subject: smbios: Convert to QemuOpts So that it can be set in config file for -readconfig. This tightens parsing of -smbios, and makes it more consistent with other options: unknown parameters are rejected, numbers with trailing junk are rejected, when a parameter is given multiple times, last rather than first wins, ... MST: drop one chunk to fix build errors Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Michael S. Tsirkin --- arch_init.c | 4 +- hw/i386/smbios.c | 209 ++++++++++++++++++++++++++++++++++++--------- include/hw/i386/smbios.h | 4 +- include/sysemu/arch_init.h | 2 +- vl.c | 3 +- 5 files changed, 179 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/arch_init.c b/arch_init.c index 6ae8eb6340..62f111807d 100644 --- a/arch_init.c +++ b/arch_init.c @@ -1134,10 +1134,10 @@ void do_acpitable_option(const QemuOpts *opts) #endif } -void do_smbios_option(const char *optarg) +void do_smbios_option(QemuOpts *opts) { #ifdef TARGET_I386 - smbios_entry_add(optarg); + smbios_entry_add(opts); #endif } diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 0608aee3db..abfd6f7daa 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -2,9 +2,11 @@ * SMBIOS Support * * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2013 Red Hat, Inc. * * Authors: * Alex Williamson + * Markus Armbruster * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. @@ -13,6 +15,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/config-file.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "hw/i386/smbios.h" @@ -41,11 +44,100 @@ struct smbios_table { #define SMBIOS_FIELD_ENTRY 0 #define SMBIOS_TABLE_ENTRY 1 - static uint8_t *smbios_entries; static size_t smbios_entries_len; static int smbios_type4_count = 0; +static QemuOptsList qemu_smbios_opts = { + .name = "smbios", + .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), + .desc = { + /* + * no elements => accept any params + * validation will happen later + */ + { /* end of list */ } + } +}; + +static const QemuOptDesc qemu_smbios_file_opts[] = { + { + .name = "file", + .type = QEMU_OPT_STRING, + .help = "binary file containing an SMBIOS element", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type0_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "vendor", + .type = QEMU_OPT_STRING, + .help = "vendor name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "date", + .type = QEMU_OPT_STRING, + .help = "release date", + },{ + .name = "release", + .type = QEMU_OPT_STRING, + .help = "revision number", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type1_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "product", + .type = QEMU_OPT_STRING, + .help = "product name", + },{ + .name = "version", + .type = QEMU_OPT_STRING, + .help = "version number", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "uuid", + .type = QEMU_OPT_STRING, + .help = "UUID", + },{ + .name = "sku", + .type = QEMU_OPT_STRING, + .help = "SKU number", + },{ + .name = "family", + .type = QEMU_OPT_STRING, + .help = "family name", + }, + { /* end of list */ } +}; + +static void smbios_register_config(void) +{ + qemu_add_opts(&qemu_smbios_opts); +} + +machine_init(smbios_register_config); + static void smbios_validate_table(void) { if (smbios_type4_count && smbios_type4_count != smp_cpus) { @@ -124,23 +216,30 @@ void smbios_add_field(int type, int offset, const void *data, size_t len) cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); } -static void smbios_build_type_0_fields(const char *t) +static void smbios_build_type_0_fields(QemuOpts *opts) { - char buf[1024]; + const char *val; unsigned char major, minor; - if (get_param_value(buf, sizeof(buf), "vendor", t)) + val = qemu_opt_get(opts, "vendor"); + if (val) { smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "version", t)) + val, strlen(val) + 1); + } + val = qemu_opt_get(opts, "version"); + if (val) { smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "date", t)) + val, strlen(val) + 1); + } + val = qemu_opt_get(opts, "date"); + if (val) { smbios_add_field(0, offsetof(struct smbios_type_0, bios_release_date_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "release", t)) { - if (sscanf(buf, "%hhu.%hhu", &major, &minor) != 2) { + val, strlen(val) + 1); + } + val = qemu_opt_get(opts, "release"); + if (val) { + if (sscanf(val, "%hhu.%hhu", &major, &minor) != 2) { error_report("Invalid release"); exit(1); } @@ -153,47 +252,69 @@ static void smbios_build_type_0_fields(const char *t) } } -static void smbios_build_type_1_fields(const char *t) +static void smbios_build_type_1_fields(QemuOpts *opts) { - char buf[1024]; + const char *val; - if (get_param_value(buf, sizeof(buf), "manufacturer", t)) + val = qemu_opt_get(opts, "manufacturer"); + if (val) { smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "product", t)) + val, strlen(val) + 1); + } + val = qemu_opt_get(opts, "product"); + if (val) { smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "version", t)) + val, strlen(val) + 1); + } + val = qemu_opt_get(opts, "version"); + if (val) { smbios_add_field(1, offsetof(struct smbios_type_1, version_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "serial", t)) + val, strlen(val) + 1); + } + val = qemu_opt_get(opts, "serial"); + if (val) { smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "uuid", t)) { - if (qemu_uuid_parse(buf, qemu_uuid) != 0) { + val, strlen(val) + 1); + } + val = qemu_opt_get(opts, "uuid"); + if (val) { + if (qemu_uuid_parse(val, qemu_uuid) != 0) { error_report("Invalid UUID"); exit(1); } } - if (get_param_value(buf, sizeof(buf), "sku", t)) + val = qemu_opt_get(opts, "sku"); + if (val) { smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str), - buf, strlen(buf) + 1); - if (get_param_value(buf, sizeof(buf), "family", t)) + val, strlen(val) + 1); + } + val = qemu_opt_get(opts, "family"); + if (val) { smbios_add_field(1, offsetof(struct smbios_type_1, family_str), - buf, strlen(buf) + 1); + val, strlen(val) + 1); + } } -void smbios_entry_add(const char *t) +void smbios_entry_add(QemuOpts *opts) { - char buf[1024]; + Error *local_err = NULL; + const char *val; - if (get_param_value(buf, sizeof(buf), "file", t)) { + val = qemu_opt_get(opts, "file"); + if (val) { struct smbios_structure_header *header; struct smbios_table *table; - int size = get_image_size(buf); + int size; + + qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + size = get_image_size(val); if (size == -1 || size < sizeof(struct smbios_structure_header)) { - error_report("Cannot read SMBIOS file %s", buf); + error_report("Cannot read SMBIOS file %s", val); exit(1); } @@ -208,8 +329,8 @@ void smbios_entry_add(const char *t) table->header.type = SMBIOS_TABLE_ENTRY; table->header.length = cpu_to_le16(sizeof(*table) + size); - if (load_image(buf, table->data) != size) { - error_report("Failed to load SMBIOS file %s", buf); + if (load_image(val, table->data) != size) { + error_report("Failed to load SMBIOS file %s", val); exit(1); } @@ -225,14 +346,26 @@ void smbios_entry_add(const char *t) return; } - if (get_param_value(buf, sizeof(buf), "type", t)) { - unsigned long type = strtoul(buf, NULL, 0); + val = qemu_opt_get(opts, "type"); + if (val) { + unsigned long type = strtoul(val, NULL, 0); + switch (type) { case 0: - smbios_build_type_0_fields(t); + qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + smbios_build_type_0_fields(opts); return; case 1: - smbios_build_type_1_fields(t); + qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + smbios_build_type_1_fields(opts); return; default: error_report("Don't know how to build fields for SMBIOS type %ld", diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 56c6108848..d9f43b7c57 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -13,7 +13,9 @@ * */ -void smbios_entry_add(const char *t); +#include "qemu/option.h" + +void smbios_entry_add(QemuOpts *opts); void smbios_add_field(int type, int offset, const void *data, size_t len); uint8_t *smbios_get_table(size_t *length); diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index dece913e7b..be71bcac2d 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -28,7 +28,7 @@ extern const uint32_t arch_type; void select_soundhw(const char *optarg); void do_acpitable_option(const QemuOpts *opts); -void do_smbios_option(const char *optarg); +void do_smbios_option(QemuOpts *opts); void cpudef_init(void); void audio_init(void); int tcg_available(void); diff --git a/vl.c b/vl.c index 4e709d5c1c..503f903727 100644 --- a/vl.c +++ b/vl.c @@ -3491,7 +3491,8 @@ int main(int argc, char **argv, char **envp) do_acpitable_option(opts); break; case QEMU_OPTION_smbios: - do_smbios_option(optarg); + opts = qemu_opts_parse(qemu_find_opts("smbios"), optarg, 0); + do_smbios_option(opts); break; case QEMU_OPTION_enable_kvm: olist = qemu_find_opts("machine"); -- cgit v1.2.3-55-g7522 From fc3b32958a80bca13309e2695de07b43dd788421 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 16 Aug 2013 15:18:31 +0200 Subject: smbios: Make multiple -smbios type= accumulate sanely Currently, -smbios type=T,NAME=VAL,... adds one field (T,NAME) with value VAL to fw_cfg for each unique NAME. If NAME occurs multiple times, the last one's VAL is used (before the QemuOpts conversion, the first one was used). Multiple -smbios can add multiple fields with the same (T, NAME). SeaBIOS reads all of them from fw_cfg, but uses only the first field (T, NAME). The others are ignored. "First one wins, subsequent ones get ignored silently" isn't nice. We commonly let the last option win. Useful, because it lets you -readconfig first, then selectively override with command line options. Clean up -smbios to work the common way. Accumulate the settings, with later ones overwriting earlier ones. Put the result into fw_cfg (no more useless duplicates). Bonus cleanup: qemu_uuid_parse() no longer sets SMBIOS system uuid by side effect. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Michael S. Tsirkin --- arch_init.c | 3 - hw/i386/smbios.c | 152 ++++++++++++++++++++++++++++------------------- include/hw/i386/smbios.h | 1 - include/sysemu/sysemu.h | 1 + vl.c | 2 + 5 files changed, 94 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/arch_init.c b/arch_init.c index 62f111807d..150647b6bb 100644 --- a/arch_init.c +++ b/arch_init.c @@ -1113,9 +1113,6 @@ int qemu_uuid_parse(const char *str, uint8_t *uuid) if (ret != 16) { return -1; } -#ifdef TARGET_I386 - smbios_add_field(1, offsetof(struct smbios_type_1, uuid), uuid, 16); -#endif return 0; } diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 42635513b8..d2dba6c72e 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -47,6 +47,7 @@ struct smbios_table { static uint8_t *smbios_entries; static size_t smbios_entries_len; static int smbios_type4_count = 0; +static bool smbios_immutable; static struct { bool seen; @@ -54,6 +55,17 @@ static struct { Location loc; } first_opt[2]; +static struct { + const char *vendor, *version, *date; + bool have_major_minor; + uint8_t major, minor; +} type0; + +static struct { + const char *manufacturer, *product, *version, *serial, *sku, *family; + /* uuid is in qemu_uuid[] */ +} type1; + static QemuOptsList qemu_smbios_opts = { .name = "smbios", .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), @@ -152,13 +164,6 @@ static void smbios_validate_table(void) } } -uint8_t *smbios_get_table(size_t *length) -{ - smbios_validate_table(); - *length = smbios_entries_len; - return smbios_entries; -} - /* * To avoid unresolvable overlaps in data, don't allow both * tables and fields for the same smbios type. @@ -182,12 +187,10 @@ static void smbios_check_collision(int type, int entry) } } -void smbios_add_field(int type, int offset, const void *data, size_t len) +static void smbios_add_field(int type, int offset, const void *data, size_t len) { struct smbios_field *field; - smbios_check_collision(type, SMBIOS_FIELD_ENTRY); - if (!smbios_entries) { smbios_entries_len = sizeof(uint16_t); smbios_entries = g_malloc0(smbios_entries_len); @@ -207,82 +210,81 @@ void smbios_add_field(int type, int offset, const void *data, size_t len) cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); } -static void smbios_build_type_0_fields(QemuOpts *opts) +static void smbios_build_type_0_fields(void) { - const char *val; - unsigned char major, minor; - - val = qemu_opt_get(opts, "vendor"); - if (val) { + if (type0.vendor) { smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str), - val, strlen(val) + 1); + type0.vendor, strlen(type0.vendor) + 1); } - val = qemu_opt_get(opts, "version"); - if (val) { + if (type0.version) { smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str), - val, strlen(val) + 1); + type0.version, strlen(type0.version) + 1); } - val = qemu_opt_get(opts, "date"); - if (val) { + if (type0.date) { smbios_add_field(0, offsetof(struct smbios_type_0, bios_release_date_str), - val, strlen(val) + 1); + type0.date, strlen(type0.date) + 1); } - val = qemu_opt_get(opts, "release"); - if (val) { - if (sscanf(val, "%hhu.%hhu", &major, &minor) != 2) { - error_report("Invalid release"); - exit(1); - } + if (type0.have_major_minor) { smbios_add_field(0, offsetof(struct smbios_type_0, system_bios_major_release), - &major, 1); + &type0.major, 1); smbios_add_field(0, offsetof(struct smbios_type_0, system_bios_minor_release), - &minor, 1); + &type0.minor, 1); } } -static void smbios_build_type_1_fields(QemuOpts *opts) +static void smbios_build_type_1_fields(void) { - const char *val; - - val = qemu_opt_get(opts, "manufacturer"); - if (val) { + if (type1.manufacturer) { smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str), - val, strlen(val) + 1); + type1.manufacturer, strlen(type1.manufacturer) + 1); } - val = qemu_opt_get(opts, "product"); - if (val) { + if (type1.product) { smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str), - val, strlen(val) + 1); + type1.product, strlen(type1.product) + 1); } - val = qemu_opt_get(opts, "version"); - if (val) { + if (type1.version) { smbios_add_field(1, offsetof(struct smbios_type_1, version_str), - val, strlen(val) + 1); + type1.version, strlen(type1.version) + 1); } - val = qemu_opt_get(opts, "serial"); - if (val) { + if (type1.serial) { smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str), - val, strlen(val) + 1); - } - val = qemu_opt_get(opts, "uuid"); - if (val) { - if (qemu_uuid_parse(val, qemu_uuid) != 0) { - error_report("Invalid UUID"); - exit(1); - } + type1.serial, strlen(type1.serial) + 1); } - val = qemu_opt_get(opts, "sku"); - if (val) { + if (type1.sku) { smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str), - val, strlen(val) + 1); + type1.sku, strlen(type1.sku) + 1); } - val = qemu_opt_get(opts, "family"); - if (val) { + if (type1.family) { smbios_add_field(1, offsetof(struct smbios_type_1, family_str), - val, strlen(val) + 1); + type1.family, strlen(type1.family) + 1); + } + if (qemu_uuid_set) { + smbios_add_field(1, offsetof(struct smbios_type_1, uuid), + qemu_uuid, 16); + } +} + +uint8_t *smbios_get_table(size_t *length) +{ + if (!smbios_immutable) { + smbios_build_type_0_fields(); + smbios_build_type_1_fields(); + smbios_validate_table(); + smbios_immutable = true; + } + *length = smbios_entries_len; + return smbios_entries; +} + +static void save_opt(const char **dest, QemuOpts *opts, const char *name) +{ + const char *val = qemu_opt_get(opts, name); + + if (val) { + *dest = val; } } @@ -291,6 +293,7 @@ void smbios_entry_add(QemuOpts *opts) Error *local_err = NULL; const char *val; + assert(!smbios_immutable); val = qemu_opt_get(opts, "file"); if (val) { struct smbios_structure_header *header; @@ -341,6 +344,8 @@ void smbios_entry_add(QemuOpts *opts) if (val) { unsigned long type = strtoul(val, NULL, 0); + smbios_check_collision(type, SMBIOS_FIELD_ENTRY); + switch (type) { case 0: qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err); @@ -348,7 +353,18 @@ void smbios_entry_add(QemuOpts *opts) error_report("%s", error_get_pretty(local_err)); exit(1); } - smbios_build_type_0_fields(opts); + save_opt(&type0.vendor, opts, "vendor"); + save_opt(&type0.version, opts, "version"); + save_opt(&type0.date, opts, "date"); + + val = qemu_opt_get(opts, "release"); + if (val) { + if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { + error_report("Invalid release"); + exit(1); + } + type0.have_major_minor = true; + } return; case 1: qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err); @@ -356,7 +372,21 @@ void smbios_entry_add(QemuOpts *opts) error_report("%s", error_get_pretty(local_err)); exit(1); } - smbios_build_type_1_fields(opts); + save_opt(&type1.manufacturer, opts, "manufacturer"); + save_opt(&type1.product, opts, "product"); + save_opt(&type1.version, opts, "version"); + save_opt(&type1.serial, opts, "serial"); + save_opt(&type1.sku, opts, "sku"); + save_opt(&type1.family, opts, "family"); + + val = qemu_opt_get(opts, "uuid"); + if (val) { + if (qemu_uuid_parse(val, qemu_uuid) != 0) { + error_report("Invalid UUID"); + exit(1); + } + qemu_uuid_set = true; + } return; default: error_report("Don't know how to build fields for SMBIOS type %ld", diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index d9f43b7c57..b08ec713f2 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -16,7 +16,6 @@ #include "qemu/option.h" void smbios_entry_add(QemuOpts *opts); -void smbios_add_field(int type, int offset, const void *data, size_t len); uint8_t *smbios_get_table(size_t *length); /* diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index b1aa059102..42577363ca 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -16,6 +16,7 @@ extern const char *bios_name; extern const char *qemu_name; extern uint8_t qemu_uuid[]; +extern bool qemu_uuid_set; int qemu_uuid_parse(const char *str, uint8_t *uuid); #define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" diff --git a/vl.c b/vl.c index 503f903727..fb8006e069 100644 --- a/vl.c +++ b/vl.c @@ -254,6 +254,7 @@ uint64_t node_mem[MAX_NODES]; unsigned long *node_cpumask[MAX_NODES]; uint8_t qemu_uuid[16]; +bool qemu_uuid_set; static QEMUBootSetHandler *boot_set_handler; static void *boot_set_opaque; @@ -3588,6 +3589,7 @@ int main(int argc, char **argv, char **envp) " Wrong format.\n"); exit(1); } + qemu_uuid_set = true; break; case QEMU_OPTION_option_rom: if (nb_option_roms >= MAX_OPTION_ROMS) { -- cgit v1.2.3-55-g7522