Introduce a preliminary framework in virt-acpi-build.c with the main ACPI build functions. It exposes the generated ACPI contents to guest over fw_cfg. Some codes borrowed from hw/i386/acpi-build.c.
The minimum required ACPI v5.1 tables for ARM are: - RSDP: Initial table that points to XSDT - XSDT: Points to all other tables (except FACS & DSDT) - FADT: Generic information about the machine - DSDT: Holds all information about system devices/peripherals - FACS: Needs to be pointed from FADT Signed-off-by: Shannon Zhao <zhaoshengl...@huawei.com> --- hw/arm/Makefile.objs | 1 + hw/arm/virt-acpi-build.c | 263 ++++++++++++++++++++++++++++++++++++++ include/hw/arm/virt-acpi-build.h | 71 ++++++++++ 3 files changed, 335 insertions(+), 0 deletions(-) create mode 100644 hw/arm/virt-acpi-build.c create mode 100644 include/hw/arm/virt-acpi-build.h diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 6088e53..8daf825 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -3,6 +3,7 @@ obj-$(CONFIG_DIGIC) += digic_boards.o obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o +obj-$(CONFIG_ACPI) += virt-acpi-build.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c new file mode 100644 index 0000000..4eed0a3 --- /dev/null +++ b/hw/arm/virt-acpi-build.c @@ -0,0 +1,263 @@ +/* Support for generating ACPI tables and passing them to Guests + * + * ARM virt ACPI generation + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * + * Author: Shannon Zhao <zhaoshengl...@huawei.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/arm/virt-acpi-build.h" +#include <stddef.h> +#include <glib.h> +#include "qemu-common.h" +#include "qemu/bitmap.h" +#include "qemu/osdep.h" +#include "qemu/range.h" +#include "qemu/error-report.h" +#include "qom/cpu.h" +#include "target-arm/cpu.h" +#include "hw/acpi/acpi-defs.h" +#include "hw/acpi/acpi.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/acpi/bios-linker-loader.h" +#include "hw/loader.h" + +#include "hw/acpi/acpi-build-utils.h" + +#include "qapi/qmp/qint.h" +#include "qom/qom-qobject.h" +#include "exec/ram_addr.h" + + +#define ACPI_BUILD_TABLE_SIZE 0x20000 + +/* Reserve RAM space for tables: add another order of magnitude. */ +#define ACPI_BUILD_TABLE_MAX_SIZE 0x200000 + +/* #define DEBUG_ACPI_BUILD */ +#ifdef DEBUG_ACPI_BUILD +#define ACPI_BUILD_DPRINTF(fmt, ...) \ + do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0) +#else +#define ACPI_BUILD_DPRINTF(fmt, ...) +#endif + +#define ACPI_BUILD_APPNAME "Bochs" +#define ACPI_BUILD_APPNAME6 "BOCHS " + +#define ACPI_BUILD_RSDP_FILE "etc/acpi/rsdp" +#define ACPI_BUILD_TPMLOG_FILE "etc/tpm/log" + +static inline void *acpi_data_push(GArray *table_data, uint64_t size) +{ + unsigned off = table_data->len; + g_array_set_size(table_data, off + size); + return table_data->data + off; +} + +static unsigned acpi_data_len(GArray *table) +{ +#if GLIB_CHECK_VERSION(2, 22, 0) + assert(g_array_get_element_size(table) == 1); +#endif + return table->len; +} + +static inline void acpi_add_table(GArray *table_offsets, GArray *table_data) +{ + uint32_t offset = cpu_to_le32(table_data->len); + g_array_append_val(table_offsets, offset); +} + +/* RSDP */ +static GArray * +build_rsdp(GArray *rsdp_table, GArray *linker, uint64_t xsdt) +{ + return rsdp_table; +} + +/* XSDT */ +static void +build_xsdt(GArray *table_data, GArray *linker, GArray *table_offsets) +{ +} + +/* MADT */ +static void +build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +{ +} + +/* GTDT */ +static void +build_gtdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +{ +} + +/* FADT */ +static void +build_fadt(GArray *table_data, GArray *linker, uint64_t facs, uint64_t dsdt) +{ +} + +/* FACS */ +static void +build_facs(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +{ +} + +/* DSDT */ +static void +build_dsdt(AcpiAml *table_aml, GArray *linker, VirtGuestInfo *guest_info) +{ +} + +typedef +struct AcpiBuildTables { + AcpiAml table_data; + GArray *rsdp; + GArray *tcpalog; + GArray *linker; +} AcpiBuildTables; + +static inline void acpi_build_tables_init(AcpiBuildTables *tables) +{ + tables->rsdp = g_array_new(false, true /* clear */, 1); + tables->table_data.buf = g_array_new(false, true /* clear */, 1); + tables->tcpalog = g_array_new(false, true /* clear */, 1); + tables->linker = bios_linker_loader_init(); + tables->table_data.linker = tables->linker; +} + +static inline void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) +{ + void *linker_data = bios_linker_loader_cleanup(tables->linker); + g_free(linker_data); + g_array_free(tables->rsdp, mfre); + g_array_free(tables->table_data.buf, true); + g_array_free(tables->tcpalog, mfre); +} + +typedef +struct AcpiBuildState { + /* Copy of table in RAM (for patching). */ + ram_addr_t table_ram; + uint32_t table_size; + /* Is table patched? */ + uint8_t patched; + VirtGuestInfo *guest_info; +} AcpiBuildState; + +static +void acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) +{ + GArray *table_offsets; + unsigned facs, dsdt, xsdt; + + table_offsets = g_array_new(false, true /* clear */, + sizeof(uint32_t)); + ACPI_BUILD_DPRINTF("init ACPI tables\n"); + + bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE, + 64 /* Ensure FACS is aligned */, + false /* high memory */); + + /* + * FACS is pointed to by FADT. + * We place it first since it's the only table that has alignment + * requirements. + */ + facs = tables->table_data.buf->len; + build_facs(tables->table_data.buf, tables->linker, guest_info); + + /* DSDT is pointed to by FADT */ + dsdt = tables->table_data.buf->len; + build_dsdt(&tables->table_data, tables->linker, guest_info); + + /* ACPI tables pointed to by XSDT */ + acpi_add_table(table_offsets, tables->table_data.buf); + build_fadt(tables->table_data.buf, tables->linker, facs, dsdt); + + acpi_add_table(table_offsets, tables->table_data.buf); + build_madt(tables->table_data.buf, tables->linker, guest_info); + + acpi_add_table(table_offsets, tables->table_data.buf); + build_gtdt(tables->table_data.buf, tables->linker, guest_info); + + /* XSDT is pointed to by RSDP */ + xsdt = tables->table_data.buf->len; + build_xsdt(tables->table_data.buf, tables->linker, table_offsets); + + /* RSDP is in FSEG memory, so allocate it separately */ + build_rsdp(tables->rsdp, tables->linker, xsdt); + + /* Cleanup memory that's no longer used. */ + g_array_free(table_offsets, true); +} + +static ram_addr_t acpi_add_rom_blob(AcpiBuildState *build_state, GArray *blob, + const char *name, uint64_t max_size) +{ + return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1, + name, NULL, build_state); +} + +void virt_acpi_setup(VirtGuestInfo *guest_info) +{ + AcpiBuildTables tables; + AcpiBuildState *build_state; + + if (!guest_info->fw_cfg) { + ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); + return; + } + + if (!acpi_enabled) { + ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); + return; + } + + build_state = g_malloc0(sizeof *build_state); + build_state->guest_info = guest_info; + + acpi_build_tables_init(&tables); + acpi_build(build_state->guest_info, &tables); + + /* Now expose it all to Guest */ + build_state->table_ram = acpi_add_rom_blob(build_state, + tables.table_data.buf, + ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TABLE_MAX_SIZE); + assert(build_state->table_ram != RAM_ADDR_MAX); + build_state->table_size = acpi_data_len(tables.table_data.buf); + + acpi_add_rom_blob(NULL, tables.linker, "etc/table-loader", 0); + + fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE, + tables.tcpalog->data, acpi_data_len(tables.tcpalog)); + + /* + * RSDP is small so it's easy to keep it immutable, no need to + * bother with ROM blobs. + */ + fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE, + tables.rsdp->data, acpi_data_len(tables.rsdp)); + + /* Cleanup tables but don't free the memory: we track it + * in build_state. + */ + acpi_build_tables_cleanup(&tables, false); +} diff --git a/include/hw/arm/virt-acpi-build.h b/include/hw/arm/virt-acpi-build.h new file mode 100644 index 0000000..7a09c34 --- /dev/null +++ b/include/hw/arm/virt-acpi-build.h @@ -0,0 +1,71 @@ +/* + * + * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO.,LTD. + * + * Author: Shannon Zhao <zhaoshengl...@huawei.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QEMU_VIRT_ACPI_BUILD_H +#define QEMU_VIRT_ACPI_BUILD_H + +#include "qemu-common.h" +#include "hw/acpi/acpi-defs.h" + + +#define ACPI_VIRT_QEMU_STR_4 "QEMU" +#define ACPI_VIRT_QEMU_STR_6 "QEMU " +#define ACPI_VIRT_MACH_STR_8 "MACHVIRT" + +struct acpi_gtdt_info { + uint32_t timer_virt; + uint32_t timer_s_el1; + uint32_t timer_ns_el1; + uint32_t timer_ns_el2; +}; + +struct acpi_madt_info { + const hwaddr *gic_cpu_base_addr; + const hwaddr *gic_dist_base_addr; +}; + +struct acpi_dsdt_info { + const hwaddr *uart_addr; + const int *uart_irq; + const hwaddr *virtio_mmio_addr; + const int *virtio_mmio_irq; + int virtio_mmio_num; + const hwaddr *rtc_addr; + const int *rtc_irq; + const hwaddr *flash_addr; +}; + +typedef struct VirtGuestInfo { + int nb_cpus; + int max_cpus; + FWCfgState *fw_cfg; + const struct acpi_madt_info *madt_info; + const struct acpi_dsdt_info *dsdt_info; + const struct acpi_gtdt_info *gtdt_info; +} VirtGuestInfo; + + +typedef struct VirtGuestInfoState { + VirtGuestInfo info; + Notifier machine_done; +} VirtGuestInfoState; + +void virt_acpi_setup(VirtGuestInfo *guest_info); + +#endif -- 1.7.1