From patchwork Mon Apr 14 20:54:58 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: 339052 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 A316D1400DA for ; Tue, 15 Apr 2014 06:56:15 +1000 (EST) Received: from localhost ([::1]:45602 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnvJ-000153-HC for incoming@patchwork.ozlabs.org; Mon, 14 Apr 2014 16:56:13 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56141) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnuY-0008In-KT for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:32 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WZnuS-0003rT-Rt for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:26 -0400 Received: from mail-qa0-x234.google.com ([2607:f8b0:400d:c00::234]:58606) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnuS-0003rF-Li for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:20 -0400 Received: by mail-qa0-f52.google.com with SMTP id s7so6522333qap.39 for ; Mon, 14 Apr 2014 13:55:20 -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=+1cnO5oNZzh5nY34fQ/EjIN8DSUQpwrDNlcdzMwh0BQ=; b=mZbSP75PXIxcNCuTufFsWRYtFlgRYtVpQcmeJ+t+2Rncunpyq167tqaYxp9jK3fw1i gT7U0dbOKU6/2qgAYbH6liYRF5E2xZNQLW33jTHyvxhsB0Y5oCUG5c7s/KTqBcB7NDU0 ynsK4T4NqlQaqgT/tla4hb4WWGs7hAPl1j+XGsTVEVq8wPjCa/xw5lhYvvAPXtMx8rra 4ApnJtKJ8dR3tOA7qo+FrCubtNcY5aSe77mkRw0ik2//B9ppgLLTBroRT9ngTjvYvv9v 2uR90633J4RYpq0TVS7Yvf9inlUoRumpuWk+J/YO9SqKJTJE44OyKZdL/5Bbd0liFDYm qB3Q== X-Received: by 10.140.102.85 with SMTP id v79mr50994436qge.8.1397508920146; Mon, 14 Apr 2014 13:55:20 -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.19 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 14 Apr 2014 13:55:19 -0700 (PDT) From: "Gabriel L. Somlo" To: qemu-devel@nongnu.org Date: Mon, 14 Apr 2014 16:54:58 -0400 Message-Id: <1397508911-18691-5-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:c00::234 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 04/17] SMBIOS: Add code to build full smbios tables; build type 2 table 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 This patch adds a set of macros which build full smbios tables of a given type, including the logic to decide whether a given table type should be built or not. To illustrate this new functionality, we introduce and optionally build a table of type 2 (base board), which is required by some versions of OS X (10.7 and 10.8). Signed-off-by: Gabriel Somlo --- hw/i386/smbios.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++ include/hw/i386/smbios.h | 18 +++++- 2 files changed, 175 insertions(+), 1 deletion(-) diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 6889332..06f572d 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -48,6 +48,7 @@ static uint8_t *smbios_entries; static size_t smbios_entries_len; static int smbios_type4_count = 0; static bool smbios_immutable; +static bool smbios_have_defaults; static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); @@ -63,6 +64,10 @@ static struct { /* uuid is in qemu_uuid[] */ } type1; +static struct { + const char *manufacturer, *product, *version, *serial, *asset, *location; +} type2; + static QemuOptsList qemu_smbios_opts = { .name = "smbios", .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), @@ -146,6 +151,39 @@ static const QemuOptDesc qemu_smbios_type1_opts[] = { { /* end of list */ } }; +static const QemuOptDesc qemu_smbios_type2_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 = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "location", + .type = QEMU_OPT_STRING, + .help = "location in chassis", + }, + { /* end of list */ } +}; + static void smbios_register_config(void) { qemu_add_opts(&qemu_smbios_opts); @@ -161,6 +199,90 @@ static void smbios_validate_table(void) } } +static bool smbios_skip_table(uint8_t type, bool required_table) +{ + if (test_bit(type, have_binfile_bitmap)) { + return true; /* user provided their own binary blob(s) */ + } + if (test_bit(type, have_fields_bitmap)) { + return false; /* user provided fields via command line */ + } + if (smbios_have_defaults && required_table) { + return false; /* we're building tables, and this one's required */ + } + return true; +} + +#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 */ \ + int str_index = 0; \ + do { \ + /* should we skip building this table ? */ \ + if (smbios_skip_table(tbl_type, tbl_required)) { \ + 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); \ + 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; \ + } while (0) + +#define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \ + do { \ + int len = (value != NULL) ? strlen(value) + 1 : 0; \ + if (len > 1) { \ + smbios_entries = g_realloc(smbios_entries, \ + 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); \ + t = (struct smbios_type_##tbl_type *)(smbios_entries + t_off);\ + t->field = ++str_index; \ + w->header.length += len; \ + } else { \ + t->field = 0; \ + } \ + } while (0) + +#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; \ + } \ + \ + /* update fw_cfg smbios element count */ \ + *(uint16_t *)smbios_entries += 1; \ + } while (0) + static void smbios_add_field(int type, int offset, const void *data, size_t len) { struct smbios_field *field; @@ -230,6 +352,24 @@ static void smbios_build_type_1_fields(void) } } +static void smbios_build_type_2_table(void) +{ + SMBIOS_BUILD_TABLE_PRE(2, 0x200, false); /* optional */ + + SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer); + SMBIOS_TABLE_SET_STR(2, product_str, type2.product); + SMBIOS_TABLE_SET_STR(2, version_str, type2.version); + SMBIOS_TABLE_SET_STR(2, serial_number_str, type2.serial); + SMBIOS_TABLE_SET_STR(2, asset_tag_number_str, type2.asset); + t->feature_flags = 0x01; /* Motherboard */ + SMBIOS_TABLE_SET_STR(2, location_str, type2.location); + t->chassis_handle = 0x300; /* Type 3 (System enclosure) */ + t->board_type = 0x0A; /* Motherboard */ + t->contained_element_count = 0; + + SMBIOS_BUILD_TABLE_POST; +} + #define SMBIOS_SET_DEFAULT(field, value) \ if (!field) { \ field = value; \ @@ -238,14 +378,19 @@ static void smbios_build_type_1_fields(void) void smbios_set_defaults(const char *manufacturer, const char *product, const char *version) { + smbios_have_defaults = true; SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); SMBIOS_SET_DEFAULT(type1.product, product); SMBIOS_SET_DEFAULT(type1.version, version); + SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(type2.product, product); + SMBIOS_SET_DEFAULT(type2.version, version); } uint8_t *smbios_get_table(size_t *length) { if (!smbios_immutable) { + smbios_build_type_2_table(); smbios_build_type_0_fields(); smbios_build_type_1_fields(); smbios_validate_table(); @@ -381,6 +526,19 @@ void smbios_entry_add(QemuOpts *opts) qemu_uuid_set = true; } return; + case 2: + qemu_opts_validate(opts, qemu_smbios_type2_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type2.manufacturer, opts, "manufacturer"); + save_opt(&type2.product, opts, "product"); + save_opt(&type2.version, opts, "version"); + save_opt(&type2.serial, opts, "serial"); + save_opt(&type2.asset, opts, "asset"); + save_opt(&type2.location, opts, "location"); + return; default: error_report("Don't know how to build fields for SMBIOS type %ld", type); diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 3425d40..2642e1a 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -62,6 +62,22 @@ struct smbios_type_1 { uint8_t family_str; } QEMU_PACKED; +/* SMBIOS type 2 - Base Board */ +struct smbios_type_2 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t product_str; + uint8_t version_str; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t feature_flags; + uint8_t location_str; + uint16_t chassis_handle; + uint8_t board_type; + uint8_t contained_element_count; + /* contained elements follow */ +} QEMU_PACKED; + /* SMBIOS type 3 - System Enclosure (v2.3) */ struct smbios_type_3 { struct smbios_structure_header header; @@ -78,7 +94,7 @@ struct smbios_type_3 { uint8_t height; uint8_t number_of_power_cords; uint8_t contained_element_count; - // contained elements follow + /* contained elements follow */ } QEMU_PACKED; /* SMBIOS type 4 - Processor Information (v2.0) */