From patchwork Mon Apr 14 20:55:05 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Gabriel L. Somlo" X-Patchwork-Id: 339055 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 088A41400DA for ; Tue, 15 Apr 2014 06:58:09 +1000 (EST) Received: from localhost ([::1]:45618 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnx8-0004Ht-Uz for incoming@patchwork.ozlabs.org; Mon, 14 Apr 2014 16:58:06 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56250) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnue-0008JO-3c for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:38 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WZnuX-0003v7-Ow for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:31 -0400 Received: from mail-qg0-x229.google.com ([2607:f8b0:400d:c04::229]:64747) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnuX-0003uw-Jj for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:25 -0400 Received: by mail-qg0-f41.google.com with SMTP id z60so323908qgd.0 for ; Mon, 14 Apr 2014 13:55:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YfB1C2Saoj8R7Zncixz2hQ8gL/6rNxHsOq11BqrLwL8=; b=Fwk4IjukO48sFIZD0nwa/tF1zVEzb2OErrO40wIwoOMEReUxa8o8j89DnWYbVjRqQ9 RnzKN1rQiXrVIiSTVJnliasRWYPHziac0M3iFX+aFq+LoxQra+sj4EFRYneBhBwOt3Wb SRA7C1HYhD1gnyqRWPjJsOouq/yGj8n8EHhzEt5cWvG5M2e1JJwxSwMCdX/NDDq+EmT0 t+N+ZOEjATGLGx7dqQg5R8RvM8QIXbmN326L4VtvYulg94U2e94a3CHjXUWkzdWx/r05 vrueonSpfBXP+v370dmxE5Me/5hLny7Qaisyke/dDhJnr/3uOOl4P44D41m0s3FkXff+ TIyQ== X-Received: by 10.140.18.175 with SMTP id 44mr5895069qgf.105.1397508925127; Mon, 14 Apr 2014 13:55:25 -0700 (PDT) Received: from ERROL.ini.cmu.edu (ERROL.INI.CMU.EDU. [128.2.16.43]) by mx.google.com with ESMTPSA id 21sm21884140qgh.23.2014.04.14.13.55.24 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 14 Apr 2014 13:55:24 -0700 (PDT) From: "Gabriel L. Somlo" To: qemu-devel@nongnu.org Date: Mon, 14 Apr 2014 16:55:05 -0400 Message-Id: <1397508911-18691-12-git-send-email-somlo@cmu.edu> X-Mailer: git-send-email 1.9.0 In-Reply-To: <1397508911-18691-1-git-send-email-somlo@cmu.edu> References: <1397508911-18691-1-git-send-email-somlo@cmu.edu> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:400d:c04::229 Cc: seabios@seabios.org, agraf@suse.de, armbru@redhat.com, kevin@koconnor.net, kraxel@redhat.com, lersek@redhat.com Subject: [Qemu-devel] [QEMU v6 PATCH 11/17] SMBIOS: Generate aggregate smbios tables, including entry point X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Build a complete set of smbios tables as a monolithic blob; Also, build an entry point structure, and insert both the set of tables and the entry point into distinct fw_cfg files. This patch expects a SeaBIOS version equal or later than commit XXXXXXXXX. An earlier version will work, but without the ability to retrieve any smbios data passed from QEMU, effectively resulting in any command-line smbios options being ignored. Signed-off-by: Gabriel Somlo --- hw/i386/pc.c | 17 +++--- hw/i386/smbios.c | 135 ++++++++++++++++++++++++----------------------- include/hw/i386/smbios.h | 23 +++++++- 3 files changed, 100 insertions(+), 75 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 6e3962e..b3c7cc2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -73,7 +73,7 @@ #define ACPI_DATA_SIZE 0x10000 #define BIOS_CFG_IOPORT 0x510 #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) -#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) +#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) /* deprecated */ #define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) #define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3) #define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4) @@ -627,8 +627,8 @@ static unsigned int pc_apic_id_limit(unsigned int max_cpus) static FWCfgState *bochs_bios_init(void) { FWCfgState *fw_cfg; - uint8_t *smbios_table; - size_t smbios_len; + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; uint64_t *numa_fw_cfg; int i, j; unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); @@ -655,10 +655,13 @@ static FWCfgState *bochs_bios_init(void) acpi_tables, acpi_tables_len); fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override()); - smbios_table = smbios_get_table(&smbios_len); - if (smbios_table) - fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, - smbios_table, smbios_len); + smbios_get_tables(&smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len); + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); + fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, &e820_reserve, sizeof(e820_reserve)); fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index b1f1d46..12cd06a 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -21,24 +21,12 @@ #include "hw/i386/smbios.h" #include "hw/loader.h" -/* - * Structures shared with the BIOS - */ -struct smbios_header { - uint16_t length; - uint8_t type; -} QEMU_PACKED; - -struct smbios_table { - struct smbios_header header; - uint8_t data[]; -} QEMU_PACKED; - -#define SMBIOS_FIELD_ENTRY 0 -#define SMBIOS_TABLE_ENTRY 1 - static uint8_t *smbios_entries; static size_t smbios_entries_len; +static unsigned smbios_table_max; +static unsigned smbios_table_cnt; +static struct smbios_entry_point ep; + static int smbios_type4_count = 0; static bool smbios_immutable; static bool smbios_have_defaults; @@ -312,9 +300,8 @@ static bool smbios_skip_table(uint8_t type, bool required_table) } #define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \ - struct smbios_table *w; \ struct smbios_type_##tbl_type *t; \ - size_t w_off, t_off; /* wrapper, table offsets into smbios_entries */ \ + size_t t_off; /* table offset into smbios_entries */ \ int str_index = 0; \ do { \ /* should we skip building this table ? */ \ @@ -322,24 +309,13 @@ static bool smbios_skip_table(uint8_t type, bool required_table) return; \ } \ \ - /* initialize fw_cfg smbios element count */ \ - if (!smbios_entries) { \ - smbios_entries_len = sizeof(uint16_t); \ - smbios_entries = g_malloc0(smbios_entries_len); \ - } \ - \ - /* use offsets of wrapper w and table t within smbios_entries */ \ - /* (pointers must be updated after each realloc) */ \ - w_off = smbios_entries_len; \ - t_off = w_off + sizeof(*w); \ - smbios_entries_len = t_off + sizeof(*t); \ + /* use offset of table t within smbios_entries */ \ + /* (pointer must be updated after each realloc) */ \ + t_off = smbios_entries_len; \ + smbios_entries_len += sizeof(*t); \ smbios_entries = g_realloc(smbios_entries, smbios_entries_len); \ - w = (struct smbios_table *)(smbios_entries + w_off); \ t = (struct smbios_type_##tbl_type *)(smbios_entries + t_off); \ \ - w->header.type = SMBIOS_TABLE_ENTRY; \ - w->header.length = sizeof(*w) + sizeof(*t); \ - \ t->header.type = tbl_type; \ t->header.length = sizeof(*t); \ t->header.handle = tbl_handle; \ @@ -353,11 +329,9 @@ static bool smbios_skip_table(uint8_t type, bool required_table) smbios_entries_len + len); \ memcpy(smbios_entries + smbios_entries_len, value, len); \ smbios_entries_len += len; \ - /* update pointer(s) post-realloc */ \ - w = (struct smbios_table *)(smbios_entries + w_off); \ + /* update pointer post-realloc */ \ t = (struct smbios_type_##tbl_type *)(smbios_entries + t_off);\ t->field = ++str_index; \ - w->header.length += len; \ } else { \ t->field = 0; \ } \ @@ -365,20 +339,23 @@ static bool smbios_skip_table(uint8_t type, bool required_table) #define SMBIOS_BUILD_TABLE_POST \ do { \ - /* add empty string if no strings defined in table */ \ - /* (NOTE: terminating \0 currently handled by fw_cfg/seabios) */ \ - if (str_index == 0) { \ - smbios_entries = g_realloc(smbios_entries, \ - smbios_entries_len + 1); \ - *(smbios_entries + smbios_entries_len) = 0; \ - smbios_entries_len += 1; \ - /* update pointer(s) post-realloc */ \ - w = (struct smbios_table *)(smbios_entries + w_off); \ - w->header.length += 1; \ + size_t term_cnt, t_size; \ + \ + /* add '\0' terminator (add two if no strings defined) */ \ + term_cnt = (str_index == 0) ? 2 : 1; \ + smbios_entries = g_realloc(smbios_entries, \ + smbios_entries_len + term_cnt); \ + memset(smbios_entries + smbios_entries_len, 0, term_cnt); \ + smbios_entries_len += term_cnt; \ + \ + /* update smbios max. element size */ \ + t_size = smbios_entries_len - t_off; \ + if (t_size > smbios_table_max) { \ + smbios_table_max = t_size; \ } \ \ - /* update fw_cfg smbios element count */ \ - *(uint16_t *)smbios_entries += 1; \ + /* update smbios element count */ \ + smbios_table_cnt++; \ } while (0) static void smbios_build_type_0_table(void) @@ -675,7 +652,32 @@ void smbios_set_defaults(const char *manufacturer, SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); } -uint8_t *smbios_get_table(size_t *length) +static void smbios_entry_point_setup(void) +{ + memcpy(ep.anchor_string, "_SM_", 4); + memcpy(ep.intermediate_anchor_string, "_DMI_", 5); + ep.length = sizeof(struct smbios_entry_point); + ep.entry_point_revision = 0; /* formatted_area reserved, per spec v2.1+ */ + memset(ep.formatted_area, 0, 5); + + /* compliant with smbios spec v2.8 */ + ep.smbios_major_version = 2; + ep.smbios_minor_version = 8; + ep.smbios_bcd_revision = 0x28; + + /* set during table construction, but BIOS may override: */ + ep.structure_table_length = smbios_entries_len; + ep.max_structure_size = smbios_table_max; + ep.number_of_structures = smbios_table_cnt; + + /* BIOS must recalculate: */ + ep.checksum = 0; + ep.intermediate_checksum = 0; + ep.structure_table_address = 0; /* where BIOS has copied smbios_entries */ +} + +void smbios_get_tables(uint8_t **tables, size_t *tables_len, + uint8_t **anchor, size_t *anchor_len) { unsigned i, dimm_cnt; @@ -774,10 +776,15 @@ uint8_t *smbios_get_table(size_t *length) smbios_build_type_127_table(); smbios_validate_table(); + smbios_entry_point_setup(); smbios_immutable = true; } - *length = smbios_entries_len; - return smbios_entries; + + /* return tables blob and entry point (anchor), and their sizes */ + *tables = smbios_entries; + *tables_len = smbios_entries_len; + *anchor = (uint8_t *)&ep; + *anchor_len = sizeof(struct smbios_entry_point); } static void save_opt(const char **dest, QemuOpts *opts, const char *name) @@ -798,7 +805,6 @@ void smbios_entry_add(QemuOpts *opts) val = qemu_opt_get(opts, "file"); if (val) { struct smbios_structure_header *header; - struct smbios_table *table; int size; qemu_opts_validate(opts, qemu_smbios_file_opts, &local_err); @@ -813,24 +819,16 @@ void smbios_entry_add(QemuOpts *opts) exit(1); } - if (!smbios_entries) { - smbios_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); - } + smbios_entries = g_realloc(smbios_entries, smbios_entries_len + size); - smbios_entries = g_realloc(smbios_entries, smbios_entries_len + - sizeof(*table) + size); - table = (struct smbios_table *)(smbios_entries + smbios_entries_len); - table->header.type = SMBIOS_TABLE_ENTRY; - table->header.length = cpu_to_le16(sizeof(*table) + size); + header = (struct smbios_structure_header *) + (smbios_entries + smbios_entries_len); - if (load_image(val, table->data) != size) { + if (load_image(val, (uint8_t *)header) != size) { error_report("Failed to load SMBIOS file %s", val); exit(1); } - header = (struct smbios_structure_header *)(table->data); - if (test_bit(header->type, have_fields_bitmap)) { error_report("Can't add binary type %d table! " "(fields already specified)", header->type); @@ -842,9 +840,12 @@ void smbios_entry_add(QemuOpts *opts) smbios_type4_count++; } - smbios_entries_len += sizeof(*table) + size; - (*(uint16_t *)smbios_entries) = - cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); + smbios_entries_len += size; + if (size > smbios_table_max) { + smbios_table_max = size; + } + smbios_table_cnt++; + return; } diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 2a0d384..8a4e3b1 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -23,12 +23,33 @@ void smbios_set_defaults(const char *manufacturer, const char *product, const char *version, ram_addr_t below_4g_mem_size, ram_addr_t above_4g_mem_size); -uint8_t *smbios_get_table(size_t *length); +void smbios_get_tables(uint8_t **tables, size_t *tables_len, + uint8_t **anchor, size_t *anchor_len); /* * SMBIOS spec defined tables */ +/* SMBIOS entry point (anchor). + * BIOS must place this at a 16-bit-aligned address between 0xf0000 and 0xfffff. + */ +struct smbios_entry_point { + uint8_t anchor_string[4]; + uint8_t checksum; + uint8_t length; + uint8_t smbios_major_version; + uint8_t smbios_minor_version; + uint16_t max_structure_size; + uint8_t entry_point_revision; + uint8_t formatted_area[5]; + uint8_t intermediate_anchor_string[5]; + uint8_t intermediate_checksum; + uint16_t structure_table_length; + uint32_t structure_table_address; + uint16_t number_of_structures; + uint8_t smbios_bcd_revision; +} QEMU_PACKED; + /* This goes at the beginning of every SMBIOS structure. */ struct smbios_structure_header { uint8_t type;