From patchwork Fri Jun 17 08:14:14 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chao Peng X-Patchwork-Id: 9183049 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 9BEFB60776 for ; Fri, 17 Jun 2016 08:31:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8AE9628390 for ; Fri, 17 Jun 2016 08:31:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7E2AF283A2; Fri, 17 Jun 2016 08:31:55 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 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 mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3554428390 for ; Fri, 17 Jun 2016 08:31:54 +0000 (UTC) Received: from localhost ([::1]:54772 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bDpBx-0004e1-AF for patchwork-qemu-devel@patchwork.kernel.org; Fri, 17 Jun 2016 04:31:53 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45033) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bDp17-0002CP-Fs for qemu-devel@nongnu.org; Fri, 17 Jun 2016 04:20:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bDp10-0007Co-W9 for qemu-devel@nongnu.org; Fri, 17 Jun 2016 04:20:40 -0400 Received: from mga02.intel.com ([134.134.136.20]:55279) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bDp10-0007Ag-BS for qemu-devel@nongnu.org; Fri, 17 Jun 2016 04:20:34 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga101.jf.intel.com with ESMTP; 17 Jun 2016 01:20:33 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.26,482,1459839600"; d="scan'208";a="989448693" Received: from vmm-docker1.bj.intel.com ([10.240.193.52]) by fmsmga001.fm.intel.com with ESMTP; 17 Jun 2016 01:20:32 -0700 From: Chao Peng To: qemu-devel@nongnu.org Date: Fri, 17 Jun 2016 04:14:14 -0400 Message-Id: <1466151257-96318-7-git-send-email-chao.p.peng@linux.intel.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1466151257-96318-1-git-send-email-chao.p.peng@linux.intel.com> References: <1466151257-96318-1-git-send-email-chao.p.peng@linux.intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 134.134.136.20 Subject: [Qemu-devel] [RFC 6/9] acpi: patch guest ACPI for pc-lite X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Haozhong Zhang , Xiao Guangrong , Eduardo Habkost , "Michael S. Tsirkin" , Paolo Bonzini , gor Mammedov , Richard Henderson Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Haozhong Zhang Traditionally, guest firmware is responsible to patch ACPI tables generated by QEMU. However, no firmware is used with pc-lite and patching ACPI should be done in QEMU for pc-lite. Signed-off-by: Haozhong Zhang --- hw/acpi/nvdimm.c | 6 +- hw/i386/Makefile.objs | 2 +- hw/i386/acpi-build.c | 72 +++++----- hw/i386/pc_lite_acpi.c | 299 +++++++++++++++++++++++++++++++++++++++++ include/hw/i386/pc_lite_acpi.h | 10 ++ 5 files changed, 355 insertions(+), 34 deletions(-) create mode 100644 hw/i386/pc_lite_acpi.c create mode 100644 include/hw/i386/pc_lite_acpi.h diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index b4c2262..523c744 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -478,8 +478,10 @@ void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, state->dsm_mem = g_array_new(false, true /* clear */, 1); acpi_data_push(state->dsm_mem, sizeof(NvdimmDsmIn)); - fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data, - state->dsm_mem->len); + if (fw_cfg) { + fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data, + state->dsm_mem->len); + } } #define NVDIMM_COMMON_DSM "NCAL" diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index b52d5b8..7d29ec0 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -1,6 +1,6 @@ obj-$(CONFIG_KVM) += kvm/ obj-y += multiboot.o -obj-y += pc.o pc_piix.o pc_q35.o +obj-y += pc.o pc_piix.o pc_q35.o pc_lite_acpi.o obj-y += pc_sysfw.o obj-y += intel_iommu.o obj-$(CONFIG_XEN) += ../xenpv/ xen/ diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 4b5ed96..6532de7 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -59,6 +59,8 @@ #include "qapi/qmp/qint.h" #include "qom/qom-qobject.h" +#include "hw/i386/pc_lite_acpi.h" + /* These are used to size the ACPI tables for -M pc-i440fx-1.7 and * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows * a little bit, there should be plenty of free space since the DSDT @@ -2148,8 +2150,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(scope, aml_name_decl("_S5", pkg)); aml_append(dsdt, scope); - /* create fw_cfg node, unconditionally */ - { + /* create fw_cfg node for non pc-lite platforms */ + if (misc->pm_type != PMTYPE_LITE) { /* when using port i/o, the 8-bit data register *always* overlaps * with half of the 16-bit control register. Hence, the total size * of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the @@ -2775,8 +2777,9 @@ void acpi_setup(void) PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); AcpiBuildTables tables; AcpiBuildState *build_state; + bool is_pc_lite = !!pm_lite_find(); - if (!pcms->fw_cfg) { + if (!pcms->fw_cfg && !is_pc_lite) { ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); return; } @@ -2799,41 +2802,48 @@ void acpi_setup(void) acpi_build(&tables, MACHINE(pcms)); /* Now expose it all to Guest */ - build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data, - ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_MAX_SIZE); - assert(build_state->table_mr != NULL); - - build_state->linker_mr = - acpi_add_rom_blob(build_state, tables.linker->cmd_blob, - "etc/table-loader", 0); - - fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, - tables.tcpalog->data, acpi_data_len(tables.tcpalog)); - - if (!pcmc->rsdp_in_ram) { - /* - * Keep for compatibility with old machine types. - * Though RSDP is small, its contents isn't immutable, so - * we'll update it along with the rest of tables on guest access. - */ - uint32_t rsdp_size = acpi_data_len(tables.rsdp); + if (!is_pc_lite) { + build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data, + ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TABLE_MAX_SIZE); + assert(build_state->table_mr != NULL); - build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); - fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE, - acpi_build_update, build_state, - build_state->rsdp, rsdp_size); - build_state->rsdp_mr = NULL; - } else { - build_state->rsdp = NULL; - build_state->rsdp_mr = acpi_add_rom_blob(build_state, tables.rsdp, - ACPI_BUILD_RSDP_FILE, 0); + build_state->linker_mr = + acpi_add_rom_blob(build_state, tables.linker->cmd_blob, + "etc/table-loader", 0); + + fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, + tables.tcpalog->data, acpi_data_len(tables.tcpalog)); + + if (!pcmc->rsdp_in_ram) { + /* + * Keep for compatibility with old machine types. + * Though RSDP is small, its contents isn't immutable, so + * we'll update it along with the rest of tables on guest access. + */ + uint32_t rsdp_size = acpi_data_len(tables.rsdp); + + build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); + fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE, + acpi_build_update, build_state, + build_state->rsdp, rsdp_size); + build_state->rsdp_mr = NULL; + } else { + build_state->rsdp = NULL; + build_state->rsdp_mr = acpi_add_rom_blob(build_state, tables.rsdp, + ACPI_BUILD_RSDP_FILE, 0); + } } qemu_register_reset(acpi_build_reset, build_state); acpi_build_reset(build_state); vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); + if (is_pc_lite) { + pc_lite_acpi_build(pcms, tables.linker, &error_abort); + build_state->patched = 1; + } + /* Cleanup tables but don't free the memory: we track it * in build_state. */ diff --git a/hw/i386/pc_lite_acpi.c b/hw/i386/pc_lite_acpi.c new file mode 100644 index 0000000..01f4394 --- /dev/null +++ b/hw/i386/pc_lite_acpi.c @@ -0,0 +1,299 @@ +#include "qemu/osdep.h" +#include +#include "qemu-common.h" +#include "qemu/mmap-alloc.h" +#include "hw/i386/pc.h" +#include "hw/i386/pc_lite_acpi.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/bios-linker-loader.h" +#include "exec/memory.h" +#include "qapi/error.h" + +/* #define DEBUG_PC_LITE_ACPI */ +#ifdef DEBUG_PC_LITE_ACPI +#define pc_lite_acpi_dprintf(fmt, ...) \ + do { \ + printf("PC_LITE_ACPI: "fmt, ##__VA_ARGS__); \ + } while (0) +#else +#define pc_lite_acpi_dprintf(fmt, ...) +#endif + + +typedef +struct PCLiteAcpiZone { + MemoryRegion *mr; + hwaddr start; + hwaddr offset; +} PCLiteAcpiZone; +static PCLiteAcpiZone pc_lite_acpi_himem_zone; +static PCLiteAcpiZone pc_lite_acpi_fseg_zone; + +#define PC_LITE_ACPI_HIMEM_SIZE (256 * 1024) +#define PC_LITE_ACPI_FSEG_SIZE (0x100000 - 0xe0000) + +static PCLiteAcpiZone *pc_lite_acpi_get_zone(uint8_t zone) +{ + if (zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { + return &pc_lite_acpi_himem_zone; + } else if (zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) { + return &pc_lite_acpi_fseg_zone; + } else { + return NULL; + } +} + +static int pc_lite_acpi_zone_init(PCLiteAcpiZone *zone, const char *name, + hwaddr start, uint64_t size) +{ + void *buf; + MemoryRegion *mr; + + buf = qemu_ram_mmap(-1, size, 0x1000, true); + if (buf == MAP_FAILED) { + return -1; + } + + mr = g_malloc(sizeof(*mr)); + memory_region_init_ram_ptr(mr, NULL, name, size, buf); + memory_region_add_subregion_overlap(get_system_memory(), start, mr, 0); + e820_add_entry(start, size, E820_RESERVED); + + zone->mr = mr; + zone->start = start; + zone->offset = 0; + + return 0; +} + +static void pc_lite_acpi_zones_init(PCMachineState *pcms) +{ + uint64_t start; + + assert(pcms->below_4g_mem_size >= PC_LITE_ACPI_HIMEM_SIZE); + start = pcms->below_4g_mem_size - PC_LITE_ACPI_HIMEM_SIZE; + pc_lite_acpi_zone_init(&pc_lite_acpi_himem_zone, "acpi_himem", + start, PC_LITE_ACPI_HIMEM_SIZE); + pc_lite_acpi_zone_init(&pc_lite_acpi_fseg_zone, "acpi_fseg", + 0xe0000, PC_LITE_ACPI_FSEG_SIZE); +} + +/* return the offset within the corresponding zone, or ~0 for failure */ +static hwaddr pc_lite_acpi_zone_alloc(PCLiteAcpiZone *zone, + uint64_t size, uint64_t align, + Error **errp) +{ + hwaddr start = zone->start; + hwaddr offset = zone->offset; + uint64_t max_size = memory_region_size(zone->mr); + uint64_t addr; + Error *local_err = NULL; + + addr = ROUND_UP(start + offset, align); + offset = addr - start; + if (size > max_size || max_size - size < offset) { + error_setg(&local_err, "Not enough space"); + goto out; + } + zone->offset = offset + size; + + out: + error_propagate(errp, local_err); + return offset; +} + + +typedef +struct PCLiteAcpiFileEntry { + char *name; + MemoryRegion *mr; + hwaddr offset; +} PCLiteAcpiFileEntry; + +typedef +struct PCLiteAcpiFiles { + GArray *file_list; +} PCLiteAcpiFiles; + +static PCLiteAcpiFiles *pc_lite_acpi_files; + +static void pc_lite_acpi_files_init(void) +{ + pc_lite_acpi_files = g_new(PCLiteAcpiFiles, 1); + pc_lite_acpi_files->file_list = g_array_new(false, true /* clear */, + sizeof(PCLiteAcpiFileEntry)); +} + +static PCLiteAcpiFileEntry *pc_lite_acpi_file_search(const char *name) +{ + int i; + GArray *file_list = pc_lite_acpi_files->file_list; + PCLiteAcpiFileEntry *file; + + for (i = 0; i < file_list->len; i++) { + file = &g_array_index(file_list, PCLiteAcpiFileEntry, i); + if (!strcmp(file->name, name)) { + return file; + } + } + return NULL; +} + +static void pc_lite_acpi_file_add(const char *name, + MemoryRegion *mr, hwaddr offset) +{ + PCLiteAcpiFileEntry file = { g_strdup(name), mr, offset }; + assert(!pc_lite_acpi_file_search(name)); + g_array_append_val(pc_lite_acpi_files->file_list, file); +} + +static void *pc_lite_acpi_file_get_ptr(PCLiteAcpiFileEntry *file) +{ + void *ptr = memory_region_get_ram_ptr(file->mr); + return ptr + file->offset; +} + +static hwaddr pc_lite_acpi_file_get_addr(PCLiteAcpiFileEntry *file) +{ + return file->mr->addr + file->offset; +} + +static void pc_lite_acpi_patch_allocate(const BiosLinkerLoaderEntry *cmd, + const BiosLinkerFileEntry *file, + Error **errp) +{ + PCLiteAcpiZone *zone = pc_lite_acpi_get_zone(cmd->alloc.zone); + MemoryRegion *zone_mr = zone->mr; + GArray *data = file->blob; + unsigned size = acpi_data_len(data); + hwaddr offset; + void *dest; + Error *local_err = NULL; + + assert(!strncmp(cmd->alloc.file, file->name, BIOS_LINKER_LOADER_FILESZ)); + + if (!zone) { + error_setg(&local_err, "Unknown zone type %d of file %s", + cmd->alloc.zone, cmd->alloc.file); + goto out; + } + + offset = pc_lite_acpi_zone_alloc(zone, size, cmd->alloc.align, &local_err); + if (local_err) { + goto out; + } + + dest = memory_region_get_ram_ptr(zone_mr); + memcpy(dest + offset, data->data, size); + memory_region_set_dirty(zone_mr, offset, size); + + pc_lite_acpi_file_add(cmd->alloc.file, zone_mr, offset); + + out: + error_propagate(errp, local_err); +} + +static void pc_lite_acpi_patch_add_pointer(const BiosLinkerLoaderEntry *cmd, + Error **errp) +{ + PCLiteAcpiFileEntry *dest_file, *src_file; + void *dest; + uint64_t pointer = 0; + uint32_t offset = cmd->pointer.offset; + uint32_t size = cmd->pointer.size; + Error *local_err = NULL; + + dest_file = pc_lite_acpi_file_search(cmd->pointer.dest_file); + if (!dest_file) { + error_setg(&local_err, "Not found dest_file %s", + cmd->pointer.dest_file); + goto out; + } + src_file = pc_lite_acpi_file_search(cmd->pointer.src_file); + if (!src_file) { + error_setg(&local_err, "Not found src_file %s", + cmd->pointer.src_file); + goto out; + } + + dest = pc_lite_acpi_file_get_ptr(dest_file); + memcpy(&pointer, dest + offset, size); + pointer += pc_lite_acpi_file_get_addr(src_file); + memcpy(dest + offset, &pointer, size); + memory_region_set_dirty(dest_file->mr, dest_file->offset + offset, size); + + out: + error_propagate(errp, local_err); +} + +static void pc_lite_acpi_patch_add_checksum(const BiosLinkerLoaderEntry *cmd, + Error **errp) +{ + PCLiteAcpiFileEntry *file = pc_lite_acpi_file_search(cmd->cksum.file); + uint32_t offset = cmd->cksum.offset; + uint8_t *dest, *cksum; + Error *local_err = NULL; + + if (!file) { + error_setg(&local_err, "Not found file %s", cmd->cksum.file); + goto out; + } + + dest = pc_lite_acpi_file_get_ptr(file); + cksum = dest + offset; + *cksum = acpi_checksum(dest + cmd->cksum.start, cmd->cksum.length); + memory_region_set_dirty(file->mr, file->offset + offset, sizeof(*cksum)); + + out: + error_propagate(errp, local_err); +} + +/** + * Patch guest ACPI which is usually done by guest BIOS. However, no + * BIOS is used with pc-lite, so it has to be done in QEMU. + */ +static void pc_lite_acpi_patch(BIOSLinker *linker, Error **errp) +{ + void *cmd_blob_data = linker->cmd_blob->data; + unsigned cmd_blob_len = linker->cmd_blob->len; + uint64_t offset; + const BiosLinkerLoaderEntry *cmd; + const BiosLinkerFileEntry *file; + Error *local_err = NULL; + + for (offset = 0; offset < cmd_blob_len; offset += sizeof(*cmd)) { + cmd = cmd_blob_data + offset; + + switch (cmd->command) { + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: + file = bios_linker_find_file(linker, cmd->alloc.file); + pc_lite_acpi_patch_allocate(cmd, file, &local_err); + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + pc_lite_acpi_patch_add_pointer(cmd, &local_err); + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + pc_lite_acpi_patch_add_checksum(cmd, &local_err); + break; + default: + pc_lite_acpi_dprintf("Ignore unknown command 0x%x\n", cmd->command); + continue; + } + + if (local_err) { + goto out; + } + } + + out: + error_propagate(errp, local_err); +} + + +void pc_lite_acpi_build(PCMachineState *pcms, BIOSLinker *linker, Error **errp) +{ + pc_lite_acpi_zones_init(pcms); + pc_lite_acpi_files_init(); + pc_lite_acpi_patch(linker, errp); +} diff --git a/include/hw/i386/pc_lite_acpi.h b/include/hw/i386/pc_lite_acpi.h new file mode 100644 index 0000000..aa08415 --- /dev/null +++ b/include/hw/i386/pc_lite_acpi.h @@ -0,0 +1,10 @@ +#ifndef HW_I386_PC_LITE_ACPI_H +#define HW_I386_PC_LITE_ACPI_H + +#include "hw/i386/pc.h" +#include "hw/acpi/bios-linker-loader.h" +#include "qapi/error.h" + +void pc_lite_acpi_build(PCMachineState *pcms, BIOSLinker *linker, Error **errp); + +#endif