From patchwork Mon Apr 14 20:55:03 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: 339056 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 A326C1400DA for ; Tue, 15 Apr 2014 06:58:23 +1000 (EST) Received: from localhost ([::1]:45619 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnxN-0004wo-D4 for incoming@patchwork.ozlabs.org; Mon, 14 Apr 2014 16:58:21 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56220) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnuc-0008J7-F0 for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:36 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WZnuW-0003uE-G2 for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:30 -0400 Received: from mail-qc0-x22f.google.com ([2607:f8b0:400d:c01::22f]:55683) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WZnuW-0003tj-5A for qemu-devel@nongnu.org; Mon, 14 Apr 2014 16:55:24 -0400 Received: by mail-qc0-f175.google.com with SMTP id e16so9488936qcx.34 for ; Mon, 14 Apr 2014 13:55:23 -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=Z1268F87yVYXje7zCDYi6fAg594Cn53L28wZ18tRZKM=; b=ZjW6Xcqh6Q2LMkxGjsGdLXM1vTq4m3jZ/gygZgdmZ+agQpr10NWiQ/MOdeAN1ZdlOZ xtYCvFLHF5t/8nmQjBJXoheQvYHy5AuLksv24FGuhkDztV1W5zHEpAARvd95Xd4BVUQG PlM41D6D6PggIsitTdH0ZcVPYB+qC8uIE+q6jybvhFqARUdwxKGfQnzWxb8xCB5uIDHc yA8Hwn81ol6hdm8ut1+R0AnEragBOk2kFwRAqfmh4aHhqGzGUefdpoG+dc49LxNKnaWS AnGBs06cULoH9rydmoDpEj0PvODn3hW+D4ZdzqejISdBvUYND08Z5Y80VrY+TvYDPTq4 qtuw== X-Received: by 10.140.47.138 with SMTP id m10mr51238914qga.21.1397508923659; Mon, 14 Apr 2014 13:55:23 -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.22 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 14 Apr 2014 13:55:23 -0700 (PDT) From: "Gabriel L. Somlo" To: qemu-devel@nongnu.org Date: Mon, 14 Apr 2014 16:55:03 -0400 Message-Id: <1397508911-18691-10-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:c01::22f 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 09/17] SMBIOS: Build full smbios memory tables (type 16, 17, 19, and 20) 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 full smbios tables representing the system RAM: - type 16 (physical memory array): represents the entire system RAM; - type 17 (memory device) tables: one per virtual DIMM; - type 19 (memory array mapped address): represent major RAM areas (currently one for below-4G memory, and, if applicable, one for above-4G memory); - type 20 (memory device mapped address): mappings between type 19 areas and type 17 DIMMs; These tables will be made available to the bios via fw_cfg. This patch also thoroughly documents the current memory table layout. Signed-off-by: Gabriel Somlo --- hw/i386/pc_piix.c | 3 +- hw/i386/pc_q35.c | 3 +- hw/i386/smbios.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++- include/hw/i386/smbios.h | 13 ++- 4 files changed, 264 insertions(+), 11 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8513de0..db075eb 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -146,7 +146,8 @@ static void pc_init1(QEMUMachineInitArgs *args, if (smbios_defaults) { /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", - args->machine->name); + args->machine->name, + below_4g_mem_size, above_4g_mem_size); } /* allocate ram and load rom/bios */ diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index eacec53..3aaac7a 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -133,7 +133,8 @@ static void pc_q35_init(QEMUMachineInitArgs *args) if (smbios_defaults) { /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - args->machine->name); + args->machine->name, + below_4g_mem_size, above_4g_mem_size); } /* allocate ram and load rom/bios */ diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 5b80021..6510ff3 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -43,6 +43,7 @@ static int smbios_type4_count = 0; static bool smbios_immutable; static bool smbios_have_defaults; static uint32_t smbios_cpuid_version, smbios_cpuid_features; /* for type 4 */ +static ram_addr_t smbios_below_4g_ram, smbios_above_4g_ram; /* for type 19 */ static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); @@ -70,6 +71,10 @@ static struct { const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; } type4; +static struct { + const char *loc_pfx, *bank, *manufacturer, *serial, *asset, *part; +} type17; + static QemuOptsList qemu_smbios_opts = { .name = "smbios", .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), @@ -244,6 +249,39 @@ static const QemuOptDesc qemu_smbios_type4_opts[] = { { /* end of list */ } }; +static const QemuOptDesc qemu_smbios_type17_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + },{ + .name = "loc_pfx", + .type = QEMU_OPT_STRING, + .help = "device locator string prefix", + },{ + .name = "bank", + .type = QEMU_OPT_STRING, + .help = "bank locator string", + },{ + .name = "manufacturer", + .type = QEMU_OPT_STRING, + .help = "manufacturer name", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "serial number", + },{ + .name = "asset", + .type = QEMU_OPT_STRING, + .help = "asset tag number", + },{ + .name = "part", + .type = QEMU_OPT_STRING, + .help = "part number", + }, + { /* end of list */ } +}; + static void smbios_register_config(void) { qemu_add_opts(&qemu_smbios_opts); @@ -466,6 +504,117 @@ static void smbios_build_type_4_table(unsigned instance) smbios_type4_count++; } +#define ONE_KB ((ram_addr_t)1 << 10) +#define ONE_MB ((ram_addr_t)1 << 20) +#define ONE_GB ((ram_addr_t)1 << 30) + +static void smbios_build_type_16_table(unsigned dimm_cnt) +{ + ram_addr_t ram_size_kb = ram_size >> 10; + + SMBIOS_BUILD_TABLE_PRE(16, 0x1000, true); /* required */ + + t->location = 0x01; /* Other */ + t->use = 0x03; /* System memory */ + t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ + /* if ram_size < 2T, use value in Kilobytes; 0x80000000 == 2T and over */ + t->maximum_capacity = (ram_size_kb < 0x80000000) ? ram_size_kb : 0x80000000; + if (t->maximum_capacity == 0x80000000) { + /* TODO: support smbios v2.7 extended capacity */ + fprintf(stderr, "qemu: warning: SMBIOS v2.7+ required for " + "ram_size >= 2T (%ld)\n", ram_size); + } + t->memory_error_information_handle = 0xFFFE; /* Not provided */ + t->number_of_memory_devices = dimm_cnt; + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_17_table(unsigned instance, ram_addr_t size) +{ + char loc_str[128]; + ram_addr_t size_mb; + + SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */ + + t->physical_memory_array_handle = 0x1000; /* Type 16 (Phys. Mem. Array) */ + t->memory_error_information_handle = 0; /* SeaBIOS, should be 0xFFFE(N/A) */ + t->total_width = 64; /* hardcoded in SeaBIOS */ + t->data_width = 64; /* hardcoded in SeaBIOS */ + size_mb = QEMU_ALIGN_UP(size, ONE_MB) / ONE_MB; + if (size_mb < 0x7FFF) { + t->size = size_mb; + } else { + t->size = 0x7FFF; + /* TODO: support smbios v2.7 extended capacity */ + fprintf(stderr, "qemu: warning: SMBIOS v2.7+ required for " + "DIMM size >= 0x7FFF Mbytes (0x%lx)\n", size_mb); + } + t->form_factor = 0x09; /* DIMM */ + t->device_set = 0; /* Not in a set */ + snprintf(loc_str, sizeof(loc_str), "%s %d", type17.loc_pfx, instance); + SMBIOS_TABLE_SET_STR(17, device_locator_str, loc_str); + SMBIOS_TABLE_SET_STR(17, bank_locator_str, type17.bank); + t->memory_type = 0x07; /* RAM */ + t->type_detail = 0; /* hardcoded in SeaBIOS */ + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_19_table(unsigned instance, + ram_addr_t start, ram_addr_t size) +{ + ram_addr_t end, start_kb, end_kb; + + SMBIOS_BUILD_TABLE_PRE(19, 0x1300 + instance, true); /* required */ + + end = start + size - 1; + assert(end > start); + start_kb = start / ONE_KB; + end_kb = end / ONE_KB; + if (start_kb >= UINT32_MAX || end_kb >= UINT32_MAX) { + t->starting_address = t->ending_address = UINT32_MAX; + fprintf(stderr, "qemu: warning: SMBIOS v2.7+ required for " + "type19(start=%lx, size=%lx)\n", start, size); + } else { + t->starting_address = start_kb; + t->ending_address = end_kb; + } + t->memory_array_handle = 0x1000; /* Type 16 (Phys. Mem. Array) */ + t->partition_width = 1; /* One device per row */ + + SMBIOS_BUILD_TABLE_POST; +} + +static void smbios_build_type_20_table(unsigned instance, + unsigned dev_hndl, unsigned array_hndl, + ram_addr_t start, ram_addr_t size) +{ + ram_addr_t end, start_kb, end_kb; + + SMBIOS_BUILD_TABLE_PRE(20, 0x1400 + instance, true); /* required */ + + end = start + size - 1; + assert(end > start); + start_kb = start / ONE_KB; + end_kb = end / ONE_KB; + if (start_kb >= UINT32_MAX || end_kb >= UINT32_MAX) { + t->starting_address = t->ending_address = UINT32_MAX; + fprintf(stderr, "qemu: warning: SMBIOS v2.7+ required for " + "type20(start=%lx, size=%lx)\n", start, size); + } else { + t->starting_address = start_kb; + t->ending_address = end_kb; + } + t->memory_device_handle = 0x1100 + dev_hndl; /* Type 17 (Memory Device) */ + t->memory_array_mapped_address_handle = 0x1300 + array_hndl; /* Type 19 */ + t->partition_row_position = 1; /* One device per row, always first pos. */ + t->interleave_position = 0; /* Not interleaved */ + t->interleaved_data_depth = 0; /* Not interleaved */ + + SMBIOS_BUILD_TABLE_POST; +} + #define SMBIOS_SET_DEFAULT(field, value) \ if (!field) { \ field = value; \ @@ -478,10 +627,17 @@ void smbios_set_cpuid(uint32_t version, uint32_t features) } void smbios_set_defaults(const char *manufacturer, - const char *product, const char *version) + const char *product, const char *version, + ram_addr_t below_4g_mem_size, + ram_addr_t above_4g_mem_size) { const char *manufacturer_compat = "Bochs"; /* SeaBIOS compatibility */ smbios_have_defaults = true; + + assert(ram_size == below_4g_mem_size + above_4g_mem_size); + smbios_below_4g_ram = below_4g_mem_size; + smbios_above_4g_ram = above_4g_mem_size; + SMBIOS_SET_DEFAULT(type0.vendor, manufacturer); SMBIOS_SET_DEFAULT(type0.version, version); SMBIOS_SET_DEFAULT(type0.date, "01/01/2014"); @@ -500,11 +656,12 @@ void smbios_set_defaults(const char *manufacturer, /* not set in SeaBIOS SMBIOS_SET_DEFAULT(type4.version, version); */ + SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); } uint8_t *smbios_get_table(size_t *length) { - unsigned i; + unsigned i, dimm_cnt; if (!smbios_immutable) { smbios_build_type_0_table(); @@ -515,6 +672,88 @@ uint8_t *smbios_get_table(size_t *length) /* count CPUs starting with 1, to minimize diff vs. SeaBIOS */ smbios_build_type_4_table(i + 1); } + + /* SeaBIOS expects tables compliant to smbios v2.4; + * As such, we currently support ram_size up to 2T + * (relevant to type 16), and DIMM sizes up to 16G + * (for type 17). + * + * One type 16 (physical memory array) table is created + * to represent the entire given ram_size, which is then + * split into type 17 (memory device) DMIMMs of 16G, with + * the last DIMM covering the sub-16G remainder + * (ram_size % 16G). + * + * Up to two type 19 (memory array mapped address) tables + * are created: the first one covers below-4G memory, and + * the second, if applicable, covers above-4g memory. + * + * Tables of type 20 (memory device mapped address) are + * created as necessary, to connect type 17 DIMMs to + * type 19 memory areas. + * + * The following figure illustrates how many instances of + * each type are generated: + * + * ------- ------- + * | T17 | | T17 | + * | <=16G | | <=16G | ... + * | 1100h |<----+ | 1101h | + * ------- | ------- + * ^ | ^ + * | | | + * ---+--- ---+--- ---+--- + * | T20 | | T20 | | T20 | + * | <4G | | 4G+ | | <=16G | ... + * | 1400h | | 1401h | | 1402h | + * ---+--- ---+--- ---+--- + * | | | + * v v v + * ------- -------------------...-- + * | T19 | | T19 | + * | <4G | | 4G and up | + * | 1300h | | 1301h | + * ------- -------------------...-- + * + * With under 4G of memory, a single DIMM and a single + * below-4G memory area are linked together by a single + * type 20 device mapped address. + * + * With over 4G (but less than 16G) of memory, we still + * require only one DIMM, but create two memory areas, + * one representing the below_4g_ram, and the other one + * for above_4g_ram. Two type 20 device mapped address + * tables link our DIMM to the below_4g and above_4g + * areas, respectively. + * + * With over 16G of memory, we create additional DIMMs, and + * additional type 20 device mapped address tables to link + * each such additional DIMM to the above_4g_ram area. + */ + +#define MAX_DIMM_SZ (16 * ONE_GB) +#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ : ram_size % MAX_DIMM_SZ) + + dimm_cnt = QEMU_ALIGN_UP(ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; + smbios_build_type_16_table(dimm_cnt); + for (i = 0; i < dimm_cnt; i++) { + smbios_build_type_17_table(i, GET_DIMM_SZ); + } + smbios_build_type_19_table(0, 0, smbios_below_4g_ram); + smbios_build_type_20_table(0, 0, 0, 0, smbios_below_4g_ram); + if (smbios_above_4g_ram) { + ram_addr_t start = 4 * ONE_GB, size; + smbios_build_type_19_table(1, start, smbios_above_4g_ram); + for (i = 0; i < dimm_cnt; i++) { + size = GET_DIMM_SZ; + if (i == 0) { /* below-4G portion of DIMM 0 already mapped */ + size -= smbios_below_4g_ram; + } + smbios_build_type_20_table(i + 1, i, 1, start, size); + start += size; + } + } + smbios_validate_table(); smbios_immutable = true; } @@ -685,6 +924,19 @@ void smbios_entry_add(QemuOpts *opts) save_opt(&type4.asset, opts, "asset"); save_opt(&type4.part, opts, "part"); return; + case 17: + qemu_opts_validate(opts, qemu_smbios_type17_opts, &local_err); + if (local_err) { + error_report("%s", error_get_pretty(local_err)); + exit(1); + } + save_opt(&type17.loc_pfx, opts, "loc_pfx"); + save_opt(&type17.bank, opts, "bank"); + save_opt(&type17.manufacturer, opts, "manufacturer"); + save_opt(&type17.serial, opts, "serial"); + save_opt(&type17.asset, opts, "asset"); + save_opt(&type17.part, opts, "part"); + 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 af5ee01..2a0d384 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -20,7 +20,9 @@ void smbios_entry_add(QemuOpts *opts); void smbios_set_cpuid(uint32_t version, uint32_t features); void smbios_set_defaults(const char *manufacturer, - const char *product, const char *version); + 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); /* @@ -118,9 +120,7 @@ struct smbios_type_4 { uint16_t l3_cache_handle; } QEMU_PACKED; -/* SMBIOS type 16 - Physical Memory Array - * Associated with one type 17 (Memory Device). - */ +/* SMBIOS type 16 - Physical Memory Array */ struct smbios_type_16 { struct smbios_structure_header header; uint8_t location; @@ -130,9 +130,8 @@ struct smbios_type_16 { uint16_t memory_error_information_handle; uint16_t number_of_memory_devices; } QEMU_PACKED; -/* SMBIOS type 17 - Memory Device - * Associated with one type 19 - */ + +/* SMBIOS type 17 - Memory Device */ struct smbios_type_17 { struct smbios_structure_header header; uint16_t physical_memory_array_handle;