From deb2db996cbb9470b39ae1e383791ef34c4eb3c2 Mon Sep 17 00:00:00 2001 From: Alvise Rigo Date: Fri, 15 Jan 2016 11:37:42 +0100 Subject: [PATCH 01/11] target-arm: Use the right MMU index in arm_regime_using_lpae_format arm_regime_using_lpae_format checks whether the LPAE extension is used for stage 1 translation regimes. MMU indexes not exclusively of a stage 1 regime won't work with this method. In case of ARMMMUIdx_S12NSE0 or ARMMMUIdx_S12NSE1, offset these values by ARMMMUIdx_S1NSE0 to get the right index indicating a stage 1 translation regime. Rename also the function to arm_s1_regime_using_lpae_format and update the comments to reflect the change. Signed-off-by: Alvise Rigo Message-id: 1452854262-19550-1-git-send-email-a.rigo@virtualopensystems.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/helper.c | 12 ++++++++---- target-arm/internals.h | 5 +++-- target-arm/op_helper.c | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 59d5a41b58..faeaaa806a 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -5996,11 +5996,15 @@ static inline bool regime_using_lpae_format(CPUARMState *env, return false; } -/* Returns true if the translation regime is using LPAE format page tables. - * Used when raising alignment exceptions, whose FSR changes depending on - * whether the long or short descriptor format is in use. */ -bool arm_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) +/* Returns true if the stage 1 translation regime is using LPAE format page + * tables. Used when raising alignment exceptions, whose FSR changes depending + * on whether the long or short descriptor format is in use. */ +bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) { + if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { + mmu_idx += ARMMMUIdx_S1NSE0; + } + return regime_using_lpae_format(env, mmu_idx); } diff --git a/target-arm/internals.h b/target-arm/internals.h index b925aaaa45..d226bbe857 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -441,8 +441,9 @@ struct ARMMMUFaultInfo { bool arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx, uint32_t *fsr, ARMMMUFaultInfo *fi); -/* Return true if the translation regime is using LPAE format page tables */ -bool arm_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx); +/* Return true if the stage 1 translation regime is using LPAE format page + * tables */ +bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx); /* Raise a data fault alignment exception for the specified virtual address */ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write, diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index e42d287d9c..951fc5ae51 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -149,7 +149,7 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write, /* the DFSR for an alignment fault depends on whether we're using * the LPAE long descriptor format, or the short descriptor format */ - if (arm_regime_using_lpae_format(env, cpu_mmu_index(env, false))) { + if (arm_s1_regime_using_lpae_format(env, cpu_mmu_index(env, false))) { env->exception.fsr = 0x21; } else { env->exception.fsr = 0x1; From dc3b89ef87dc0ecd5ba435c155ad8511b848d909 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 12 Jan 2016 14:39:18 -0800 Subject: [PATCH 02/11] xlnx-zynqmp: Add support for high DDR memory regions The Xilinx ZynqMP SoC and EP108 board supports three memory regions: - A 2GB region starting at 0 - A 32GB region starting at 32GB - A 256GB region starting at 768GB This patch adds support for the first two memory regions, which is automatically created based on the size specified by the QEMU memory command line argument. On hardware the physical memory region is one continuous region, it is then mapped into the three different regions by the DDRC. As we don't model the DDRC this is done at startup by QEMU. The board creates the memory region and then passes that memory region to the SoC. The SoC then maps the memory regions. Signed-off-by: Alistair Francis Reviewed-by: Peter Crosthwaite Message-id: a1e47db941d65733724a300fcd98b74fbeeaaf22.1452637205.git.alistair.francis@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-ep108.c | 40 +++++++++++++++++++----------------- hw/arm/xlnx-zynqmp.c | 37 +++++++++++++++++++++++++++++++++ include/hw/arm/xlnx-zynqmp.h | 12 +++++++++++ 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c index 73e60876e8..9099025a79 100644 --- a/hw/arm/xlnx-ep108.c +++ b/hw/arm/xlnx-ep108.c @@ -25,42 +25,44 @@ typedef struct XlnxEP108 { MemoryRegion ddr_ram; } XlnxEP108; -/* Max 2GB RAM */ -#define EP108_MAX_RAM_SIZE 0x80000000ull - static struct arm_boot_info xlnx_ep108_binfo; static void xlnx_ep108_init(MachineState *machine) { XlnxEP108 *s = g_new0(XlnxEP108, 1); Error *err = NULL; + uint64_t ram_size = machine->ram_size; + + /* Create the memory region to pass to the SoC */ + if (ram_size > XLNX_ZYNQMP_MAX_RAM_SIZE) { + error_report("ERROR: RAM size 0x%" PRIx64 " above max supported of " + "0x%llx", ram_size, + XLNX_ZYNQMP_MAX_RAM_SIZE); + exit(1); + } + + if (ram_size < 0x08000000) { + qemu_log("WARNING: RAM size 0x%" PRIx64 " is small for EP108", + ram_size); + } + + memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram", + ram_size); object_initialize(&s->soc, sizeof(s->soc), TYPE_XLNX_ZYNQMP); object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), &error_abort); + object_property_set_link(OBJECT(&s->soc), OBJECT(&s->ddr_ram), + "ddr-ram", &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", &err); if (err) { error_report_err(err); exit(1); } - if (machine->ram_size > EP108_MAX_RAM_SIZE) { - error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, " - "reduced to %llx", machine->ram_size, EP108_MAX_RAM_SIZE); - machine->ram_size = EP108_MAX_RAM_SIZE; - } - - if (machine->ram_size < 0x08000000) { - qemu_log("WARNING: RAM size " RAM_ADDR_FMT " is small for EP108", - machine->ram_size); - } - - memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram", - machine->ram_size); - memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram); - - xlnx_ep108_binfo.ram_size = machine->ram_size; + xlnx_ep108_binfo.ram_size = ram_size; xlnx_ep108_binfo.kernel_filename = machine->kernel_filename; xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline; xlnx_ep108_binfo.initrd_filename = machine->initrd_filename; diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 20a3b2b093..f26efd802a 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -90,6 +90,11 @@ static void xlnx_zynqmp_init(Object *obj) &error_abort); } + object_property_add_link(obj, "ddr-ram", TYPE_MEMORY_REGION, + (Object **)&s->ddr_ram, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); @@ -119,10 +124,42 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) XlnxZynqMPState *s = XLNX_ZYNQMP(dev); MemoryRegion *system_memory = get_system_memory(); uint8_t i; + uint64_t ram_size; const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]"; + ram_addr_t ddr_low_size, ddr_high_size; qemu_irq gic_spi[GIC_NUM_SPI_INTR]; Error *err = NULL; + ram_size = memory_region_size(s->ddr_ram); + + /* Create the DDR Memory Regions. User friendly checks should happen at + * the board level + */ + if (ram_size > XLNX_ZYNQMP_MAX_LOW_RAM_SIZE) { + /* The RAM size is above the maximum available for the low DDR. + * Create the high DDR memory region as well. + */ + assert(ram_size <= XLNX_ZYNQMP_MAX_RAM_SIZE); + ddr_low_size = XLNX_ZYNQMP_MAX_LOW_RAM_SIZE; + ddr_high_size = ram_size - XLNX_ZYNQMP_MAX_LOW_RAM_SIZE; + + memory_region_init_alias(&s->ddr_ram_high, NULL, + "ddr-ram-high", s->ddr_ram, + ddr_low_size, ddr_high_size); + memory_region_add_subregion(get_system_memory(), + XLNX_ZYNQMP_HIGH_RAM_START, + &s->ddr_ram_high); + } else { + /* RAM must be non-zero */ + assert(ram_size); + ddr_low_size = ram_size; + } + + memory_region_init_alias(&s->ddr_ram_low, NULL, + "ddr-ram-low", s->ddr_ram, + 0, ddr_low_size); + memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram_low); + /* Create the four OCM banks */ for (i = 0; i < XLNX_ZYNQMP_NUM_OCM_BANKS; i++) { char *ocm_name = g_strdup_printf("zynqmp.ocm_ram_bank_%d", i); diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index d1160920cc..1eba937964 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -51,6 +51,14 @@ #define XLNX_ZYNQMP_GIC_REGION_SIZE 0x1000 #define XLNX_ZYNQMP_GIC_ALIASES (0x10000 / XLNX_ZYNQMP_GIC_REGION_SIZE - 1) +#define XLNX_ZYNQMP_MAX_LOW_RAM_SIZE 0x80000000ull + +#define XLNX_ZYNQMP_MAX_HIGH_RAM_SIZE 0x800000000ull +#define XLNX_ZYNQMP_HIGH_RAM_START 0x800000000ull + +#define XLNX_ZYNQMP_MAX_RAM_SIZE (XLNX_ZYNQMP_MAX_LOW_RAM_SIZE + \ + XLNX_ZYNQMP_MAX_HIGH_RAM_SIZE) + typedef struct XlnxZynqMPState { /*< private >*/ DeviceState parent_obj; @@ -60,8 +68,12 @@ typedef struct XlnxZynqMPState { ARMCPU rpu_cpu[XLNX_ZYNQMP_NUM_RPU_CPUS]; GICState gic; MemoryRegion gic_mr[XLNX_ZYNQMP_GIC_REGIONS][XLNX_ZYNQMP_GIC_ALIASES]; + MemoryRegion ocm_ram[XLNX_ZYNQMP_NUM_OCM_BANKS]; + MemoryRegion *ddr_ram; + MemoryRegion ddr_ram_low, ddr_ram_high; + CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS]; CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS]; SysbusAHCIState sata; From f1cd483004da30a905ee6cf5d3268181f6c13e1a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Jan 2016 20:56:18 +0100 Subject: [PATCH 03/11] qapi-schema: dump-guest-memory: Improve text dump-guest-memory is supported by more than just x86, however the paging option is not. (No functional change.) Signed-off-by: Andrew Jones Reviewed-by: Eric Blake Message-id: 1452542185-10914-2-git-send-email-drjones@redhat.com Signed-off-by: Peter Maydell --- qapi-schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qapi-schema.json b/qapi-schema.json index 2e31733b21..0c75465031 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2169,8 +2169,7 @@ # @dump-guest-memory # # Dump guest's memory to vmcore. It is a synchronous operation that can take -# very long depending on the amount of guest memory. This command is only -# supported on i386 and x86_64. +# very long depending on the amount of guest memory. # # @paging: if true, do paging to get guest's memory mapping. This allows # using gdb to process the core file. @@ -2186,6 +2185,7 @@ # 2. The guest can be in real-mode even if paging is enabled. For # example, the guest uses ACPI to sleep, and ACPI sleep state # goes in real-mode +# 3. Currently only supported on i386 and x86_64. # # @protocol: the filename or file descriptor of the vmcore. The supported # protocols are: From b09afd58e4cb61a854c34040a12962e5b6f05ca5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Jan 2016 20:56:19 +0100 Subject: [PATCH 04/11] dump: qemunotes aren't commonly needed Only one of three architectures implementing qmp-dump-guest-memory write qemu notes. And, another architecture (arm/aarch64) is coming, which won't use them either. Make the common implementation truly common. (No functional change.) Signed-off-by: Andrew Jones Reviewed-by: Peter Maydell Message-id: 1452542185-10914-3-git-send-email-drjones@redhat.com Signed-off-by: Peter Maydell --- qom/cpu.c | 4 ++-- target-ppc/arch_dump.c | 6 ------ target-ppc/cpu-qom.h | 2 -- target-ppc/translate_init.c | 1 - target-s390x/arch_dump.c | 6 ------ target-s390x/cpu-qom.h | 2 -- target-s390x/cpu.c | 1 - 7 files changed, 2 insertions(+), 20 deletions(-) diff --git a/qom/cpu.c b/qom/cpu.c index fb80d13a3f..8f537a470f 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -130,7 +130,7 @@ int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, static int cpu_common_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, void *opaque) { - return -1; + return 0; } int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, @@ -159,7 +159,7 @@ int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, static int cpu_common_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, void *opaque) { - return -1; + return 0; } int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, diff --git a/target-ppc/arch_dump.c b/target-ppc/arch_dump.c index 5acafc68a4..816dbc2134 100644 --- a/target-ppc/arch_dump.c +++ b/target-ppc/arch_dump.c @@ -278,9 +278,3 @@ int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, PowerPCCPU *cpu = POWERPC_CPU(cs); return ppc64_write_all_elf64_notes("CORE", f, cpu, cpuid, opaque); } - -int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, - CPUState *cpu, void *opaque) -{ - return 0; -} diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index bc20504b3d..7d5e2b36a9 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -125,8 +125,6 @@ int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_read_register_apple(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg); -int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, - CPUState *cpu, void *opaque); int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); #ifndef CONFIG_USER_ONLY diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index e88dc7fc7a..4ab2d927b0 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -9712,7 +9712,6 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) cc->vmsd = &vmstate_ppc_cpu; #if defined(TARGET_PPC64) cc->write_elf64_note = ppc64_cpu_write_elf64_note; - cc->write_elf64_qemunote = ppc64_cpu_write_elf64_qemunote; #endif #endif cc->cpu_exec_enter = ppc_cpu_exec_enter; diff --git a/target-s390x/arch_dump.c b/target-s390x/arch_dump.c index dab63eb44f..549a99b357 100644 --- a/target-s390x/arch_dump.c +++ b/target-s390x/arch_dump.c @@ -246,9 +246,3 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) return (elf_note_size) * nr_cpus; } - -int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, - CPUState *cpu, void *opaque) -{ - return 0; -} diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h index 491c1b8769..029a44af24 100644 --- a/target-s390x/cpu-qom.h +++ b/target-s390x/cpu-qom.h @@ -91,8 +91,6 @@ void s390_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); -int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, - CPUState *cpu, void *opaque); hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr); int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index 189a2afc0f..e5a3f65029 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -353,7 +353,6 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_debug = s390_cpu_get_phys_page_debug; cc->vmsd = &vmstate_s390_cpu; cc->write_elf64_note = s390_cpu_write_elf64_note; - cc->write_elf64_qemunote = s390_cpu_write_elf64_qemunote; cc->cpu_exec_interrupt = s390_cpu_exec_interrupt; cc->debug_excp_handler = s390x_cpu_debug_excp_handler; #endif From 8161befdd15ddc5a8bb9e807ff1ac5907c594688 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Jan 2016 20:56:20 +0100 Subject: [PATCH 05/11] dump: allow target to set the page size This is necessary for targets that don't have TARGET_PAGE_SIZE == real-target-page-size. The target should set the page size to the correct one, if known, or, if not known, to the maximum page size it supports. (No functional change.) Signed-off-by: Andrew Jones Reviewed-by: Peter Maydell Message-id: 1452542185-10914-4-git-send-email-drjones@redhat.com Signed-off-by: Peter Maydell --- dump.c | 127 +++++++++++++++++++++++-------------- include/sysemu/dump-arch.h | 8 ++- include/sysemu/dump.h | 10 +-- 3 files changed, 85 insertions(+), 60 deletions(-) diff --git a/dump.c b/dump.c index 78b7d843ce..e1d9bae9e8 100644 --- a/dump.c +++ b/dump.c @@ -347,18 +347,18 @@ static void write_memory(DumpState *s, GuestPhysBlock *block, ram_addr_t start, int64_t i; Error *local_err = NULL; - for (i = 0; i < size / TARGET_PAGE_SIZE; i++) { - write_data(s, block->host_addr + start + i * TARGET_PAGE_SIZE, - TARGET_PAGE_SIZE, &local_err); + for (i = 0; i < size / s->dump_info.page_size; i++) { + write_data(s, block->host_addr + start + i * s->dump_info.page_size, + s->dump_info.page_size, &local_err); if (local_err) { error_propagate(errp, local_err); return; } } - if ((size % TARGET_PAGE_SIZE) != 0) { - write_data(s, block->host_addr + start + i * TARGET_PAGE_SIZE, - size % TARGET_PAGE_SIZE, &local_err); + if ((size % s->dump_info.page_size) != 0) { + write_data(s, block->host_addr + start + i * s->dump_info.page_size, + size % s->dump_info.page_size, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -737,7 +737,7 @@ static void create_header32(DumpState *s, Error **errp) strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE)); dh->header_version = cpu_to_dump32(s, 6); - block_size = TARGET_PAGE_SIZE; + block_size = s->dump_info.page_size; dh->block_size = cpu_to_dump32(s, block_size); sub_hdr_size = sizeof(struct KdumpSubHeader32) + s->note_size; sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size); @@ -837,7 +837,7 @@ static void create_header64(DumpState *s, Error **errp) strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE)); dh->header_version = cpu_to_dump32(s, 6); - block_size = TARGET_PAGE_SIZE; + block_size = s->dump_info.page_size; dh->block_size = cpu_to_dump32(s, block_size); sub_hdr_size = sizeof(struct KdumpSubHeader64) + s->note_size; sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size); @@ -933,6 +933,11 @@ static void write_dump_header(DumpState *s, Error **errp) } } +static size_t dump_bitmap_get_bufsize(DumpState *s) +{ + return s->dump_info.page_size; +} + /* * set dump_bitmap sequencely. the bit before last_pfn is not allowed to be * rewritten, so if need to set the first bit, set last_pfn and pfn to 0. @@ -946,6 +951,8 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, off_t old_offset, new_offset; off_t offset_bitmap1, offset_bitmap2; uint32_t byte, bit; + size_t bitmap_bufsize = dump_bitmap_get_bufsize(s); + size_t bits_per_buf = bitmap_bufsize * CHAR_BIT; /* should not set the previous place */ assert(last_pfn <= pfn); @@ -956,14 +963,14 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, * making new_offset be bigger than old_offset can also sync remained data * into vmcore. */ - old_offset = BUFSIZE_BITMAP * (last_pfn / PFN_BUFBITMAP); - new_offset = BUFSIZE_BITMAP * (pfn / PFN_BUFBITMAP); + old_offset = bitmap_bufsize * (last_pfn / bits_per_buf); + new_offset = bitmap_bufsize * (pfn / bits_per_buf); while (old_offset < new_offset) { /* calculate the offset and write dump_bitmap */ offset_bitmap1 = s->offset_dump_bitmap + old_offset; if (write_buffer(s->fd, offset_bitmap1, buf, - BUFSIZE_BITMAP) < 0) { + bitmap_bufsize) < 0) { return -1; } @@ -971,17 +978,17 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap + old_offset; if (write_buffer(s->fd, offset_bitmap2, buf, - BUFSIZE_BITMAP) < 0) { + bitmap_bufsize) < 0) { return -1; } - memset(buf, 0, BUFSIZE_BITMAP); - old_offset += BUFSIZE_BITMAP; + memset(buf, 0, bitmap_bufsize); + old_offset += bitmap_bufsize; } /* get the exact place of the bit in the buf, and set it */ - byte = (pfn % PFN_BUFBITMAP) / CHAR_BIT; - bit = (pfn % PFN_BUFBITMAP) % CHAR_BIT; + byte = (pfn % bits_per_buf) / CHAR_BIT; + bit = (pfn % bits_per_buf) % CHAR_BIT; if (value) { buf[byte] |= 1u << bit; } else { @@ -991,6 +998,20 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, return 0; } +static uint64_t dump_paddr_to_pfn(DumpState *s, uint64_t addr) +{ + int target_page_shift = ctz32(s->dump_info.page_size); + + return (addr >> target_page_shift) - ARCH_PFN_OFFSET; +} + +static uint64_t dump_pfn_to_paddr(DumpState *s, uint64_t pfn) +{ + int target_page_shift = ctz32(s->dump_info.page_size); + + return (pfn + ARCH_PFN_OFFSET) << target_page_shift; +} + /* * exam every page and return the page frame number and the address of the page. * bufptr can be NULL. note: the blocks here is supposed to reflect guest-phys @@ -1001,16 +1022,16 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, uint8_t **bufptr, DumpState *s) { GuestPhysBlock *block = *blockptr; - hwaddr addr; + hwaddr addr, target_page_mask = ~((hwaddr)s->dump_info.page_size - 1); uint8_t *buf; /* block == NULL means the start of the iteration */ if (!block) { block = QTAILQ_FIRST(&s->guest_phys_blocks.head); *blockptr = block; - assert((block->target_start & ~TARGET_PAGE_MASK) == 0); - assert((block->target_end & ~TARGET_PAGE_MASK) == 0); - *pfnptr = paddr_to_pfn(block->target_start); + assert((block->target_start & ~target_page_mask) == 0); + assert((block->target_end & ~target_page_mask) == 0); + *pfnptr = dump_paddr_to_pfn(s, block->target_start); if (bufptr) { *bufptr = block->host_addr; } @@ -1018,10 +1039,10 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, } *pfnptr = *pfnptr + 1; - addr = pfn_to_paddr(*pfnptr); + addr = dump_pfn_to_paddr(s, *pfnptr); if ((addr >= block->target_start) && - (addr + TARGET_PAGE_SIZE <= block->target_end)) { + (addr + s->dump_info.page_size <= block->target_end)) { buf = block->host_addr + (addr - block->target_start); } else { /* the next page is in the next block */ @@ -1030,9 +1051,9 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, if (!block) { return false; } - assert((block->target_start & ~TARGET_PAGE_MASK) == 0); - assert((block->target_end & ~TARGET_PAGE_MASK) == 0); - *pfnptr = paddr_to_pfn(block->target_start); + assert((block->target_start & ~target_page_mask) == 0); + assert((block->target_end & ~target_page_mask) == 0); + *pfnptr = dump_paddr_to_pfn(s, block->target_start); buf = block->host_addr; } @@ -1050,9 +1071,11 @@ static void write_dump_bitmap(DumpState *s, Error **errp) void *dump_bitmap_buf; size_t num_dumpable; GuestPhysBlock *block_iter = NULL; + size_t bitmap_bufsize = dump_bitmap_get_bufsize(s); + size_t bits_per_buf = bitmap_bufsize * CHAR_BIT; /* dump_bitmap_buf is used to store dump_bitmap temporarily */ - dump_bitmap_buf = g_malloc0(BUFSIZE_BITMAP); + dump_bitmap_buf = g_malloc0(bitmap_bufsize); num_dumpable = 0; last_pfn = 0; @@ -1074,11 +1097,11 @@ static void write_dump_bitmap(DumpState *s, Error **errp) /* * set_dump_bitmap will always leave the recently set bit un-sync. Here we - * set last_pfn + PFN_BUFBITMAP to 0 and those set but un-sync bit will be - * synchronized into vmcore. + * set the remaining bits from last_pfn to the end of the bitmap buffer to + * 0. With those set, the un-sync bit will be synchronized into the vmcore. */ if (num_dumpable > 0) { - ret = set_dump_bitmap(last_pfn, last_pfn + PFN_BUFBITMAP, false, + ret = set_dump_bitmap(last_pfn, last_pfn + bits_per_buf, false, dump_bitmap_buf, s); if (ret < 0) { dump_error(s, "dump: failed to sync dump_bitmap", errp); @@ -1098,8 +1121,8 @@ static void prepare_data_cache(DataCache *data_cache, DumpState *s, { data_cache->fd = s->fd; data_cache->data_size = 0; - data_cache->buf_size = BUFSIZE_DATA_CACHE; - data_cache->buf = g_malloc0(BUFSIZE_DATA_CACHE); + data_cache->buf_size = 4 * dump_bitmap_get_bufsize(s); + data_cache->buf = g_malloc0(data_cache->buf_size); data_cache->offset = offset; } @@ -1193,7 +1216,7 @@ static void write_dump_pages(DumpState *s, Error **errp) prepare_data_cache(&page_data, s, offset_data); /* prepare buffer to store compressed data */ - len_buf_out = get_len_buf_out(TARGET_PAGE_SIZE, s->flag_compress); + len_buf_out = get_len_buf_out(s->dump_info.page_size, s->flag_compress); assert(len_buf_out != 0); #ifdef CONFIG_LZO @@ -1206,19 +1229,19 @@ static void write_dump_pages(DumpState *s, Error **errp) * init zero page's page_desc and page_data, because every zero page * uses the same page_data */ - pd_zero.size = cpu_to_dump32(s, TARGET_PAGE_SIZE); + pd_zero.size = cpu_to_dump32(s, s->dump_info.page_size); pd_zero.flags = cpu_to_dump32(s, 0); pd_zero.offset = cpu_to_dump64(s, offset_data); pd_zero.page_flags = cpu_to_dump64(s, 0); - buf = g_malloc0(TARGET_PAGE_SIZE); - ret = write_cache(&page_data, buf, TARGET_PAGE_SIZE, false); + buf = g_malloc0(s->dump_info.page_size); + ret = write_cache(&page_data, buf, s->dump_info.page_size, false); g_free(buf); if (ret < 0) { dump_error(s, "dump: failed to write page data (zero page)", errp); goto out; } - offset_data += TARGET_PAGE_SIZE; + offset_data += s->dump_info.page_size; /* * dump memory to vmcore page by page. zero page will all be resided in the @@ -1226,7 +1249,7 @@ static void write_dump_pages(DumpState *s, Error **errp) */ while (get_next_page(&block_iter, &pfn_iter, &buf, s)) { /* check zero page */ - if (is_zero_page(buf, TARGET_PAGE_SIZE)) { + if (is_zero_page(buf, s->dump_info.page_size)) { ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor), false); if (ret < 0) { @@ -1248,8 +1271,8 @@ static void write_dump_pages(DumpState *s, Error **errp) size_out = len_buf_out; if ((s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) && (compress2(buf_out, (uLongf *)&size_out, buf, - TARGET_PAGE_SIZE, Z_BEST_SPEED) == Z_OK) && - (size_out < TARGET_PAGE_SIZE)) { + s->dump_info.page_size, Z_BEST_SPEED) == Z_OK) && + (size_out < s->dump_info.page_size)) { pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_ZLIB); pd.size = cpu_to_dump32(s, size_out); @@ -1260,9 +1283,9 @@ static void write_dump_pages(DumpState *s, Error **errp) } #ifdef CONFIG_LZO } else if ((s->flag_compress & DUMP_DH_COMPRESSED_LZO) && - (lzo1x_1_compress(buf, TARGET_PAGE_SIZE, buf_out, + (lzo1x_1_compress(buf, s->dump_info.page_size, buf_out, (lzo_uint *)&size_out, wrkmem) == LZO_E_OK) && - (size_out < TARGET_PAGE_SIZE)) { + (size_out < s->dump_info.page_size)) { pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_LZO); pd.size = cpu_to_dump32(s, size_out); @@ -1274,9 +1297,9 @@ static void write_dump_pages(DumpState *s, Error **errp) #endif #ifdef CONFIG_SNAPPY } else if ((s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) && - (snappy_compress((char *)buf, TARGET_PAGE_SIZE, + (snappy_compress((char *)buf, s->dump_info.page_size, (char *)buf_out, &size_out) == SNAPPY_OK) && - (size_out < TARGET_PAGE_SIZE)) { + (size_out < s->dump_info.page_size)) { pd.flags = cpu_to_dump32(s, DUMP_DH_COMPRESSED_SNAPPY); pd.size = cpu_to_dump32(s, size_out); @@ -1289,13 +1312,14 @@ static void write_dump_pages(DumpState *s, Error **errp) } else { /* * fall back to save in plaintext, size_out should be - * assigned TARGET_PAGE_SIZE + * assigned the target's page size */ pd.flags = cpu_to_dump32(s, 0); - size_out = TARGET_PAGE_SIZE; + size_out = s->dump_info.page_size; pd.size = cpu_to_dump32(s, size_out); - ret = write_cache(&page_data, buf, TARGET_PAGE_SIZE, false); + ret = write_cache(&page_data, buf, + s->dump_info.page_size, false); if (ret < 0) { dump_error(s, "dump: failed to write page data", errp); goto out; @@ -1430,7 +1454,7 @@ static void get_max_mapnr(DumpState *s) GuestPhysBlock *last_block; last_block = QTAILQ_LAST(&s->guest_phys_blocks.head, GuestPhysBlockHead); - s->max_mapnr = paddr_to_pfn(last_block->target_end); + s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end); } static void dump_init(DumpState *s, int fd, bool has_format, @@ -1489,6 +1513,10 @@ static void dump_init(DumpState *s, int fd, bool has_format, goto cleanup; } + if (!s->dump_info.page_size) { + s->dump_info.page_size = TARGET_PAGE_SIZE; + } + s->note_size = cpu_get_note_size(s->dump_info.d_class, s->dump_info.d_machine, nr_cpus); if (s->note_size < 0) { @@ -1512,8 +1540,9 @@ static void dump_init(DumpState *s, int fd, bool has_format, get_max_mapnr(s); uint64_t tmp; - tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), TARGET_PAGE_SIZE); - s->len_dump_bitmap = tmp * TARGET_PAGE_SIZE; + tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), + s->dump_info.page_size); + s->len_dump_bitmap = tmp * s->dump_info.page_size; /* init for kdump-compressed format */ if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { diff --git a/include/sysemu/dump-arch.h b/include/sysemu/dump-arch.h index 9c95cede3d..43358396ea 100644 --- a/include/sysemu/dump-arch.h +++ b/include/sysemu/dump-arch.h @@ -15,9 +15,11 @@ #define DUMP_ARCH_H typedef struct ArchDumpInfo { - int d_machine; /* Architecture */ - int d_endian; /* ELFDATA2LSB or ELFDATA2MSB */ - int d_class; /* ELFCLASS32 or ELFCLASS64 */ + int d_machine; /* Architecture */ + int d_endian; /* ELFDATA2LSB or ELFDATA2MSB */ + int d_class; /* ELFCLASS32 or ELFCLASS64 */ + uint32_t page_size; /* The target's page size. If it's variable and + * unknown, then this should be the maximum. */ } ArchDumpInfo; struct GuestPhysBlockList; /* memory_mapping.h */ diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index 7e4ec5c7d9..16cbd8d881 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -20,12 +20,9 @@ #define VERSION_FLAT_HEADER (1) /* version of flattened format */ #define END_FLAG_FLAT_HEADER (-1) +#ifndef ARCH_PFN_OFFSET #define ARCH_PFN_OFFSET (0) - -#define paddr_to_pfn(X) \ - (((unsigned long long)(X) >> TARGET_PAGE_BITS) - ARCH_PFN_OFFSET) -#define pfn_to_paddr(X) \ - (((unsigned long long)(X) + ARCH_PFN_OFFSET) << TARGET_PAGE_BITS) +#endif /* * flag for compressed format @@ -39,9 +36,6 @@ #define PHYS_BASE (0) #define DUMP_LEVEL (1) #define DISKDUMP_HEADER_BLOCKS (1) -#define BUFSIZE_BITMAP (TARGET_PAGE_SIZE) -#define PFN_BUFBITMAP (CHAR_BIT * BUFSIZE_BITMAP) -#define BUFSIZE_DATA_CACHE (TARGET_PAGE_SIZE * 4) #include "sysemu/dump-arch.h" #include "sysemu/memory_mapping.h" From b6e05aa473b52e049654fae834453232e6b6e798 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Jan 2016 20:56:21 +0100 Subject: [PATCH 06/11] dump: allow target to set the physical base crash assumes the physical base in the kdump subheader of makedumpfile formatted dumps is correct. Zero is not correct for all architectures, so allow it to be changed. (No functional change.) Signed-off-by: Andrew Jones Reviewed-by: Peter Maydell Message-id: 1452542185-10914-5-git-send-email-drjones@redhat.com Signed-off-by: Peter Maydell --- dump.c | 4 ++-- include/sysemu/dump-arch.h | 1 + include/sysemu/dump.h | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dump.c b/dump.c index e1d9bae9e8..2d4892bec2 100644 --- a/dump.c +++ b/dump.c @@ -775,7 +775,7 @@ static void create_header32(DumpState *s, Error **errp) /* 64bit max_mapnr_64 */ kh->max_mapnr_64 = cpu_to_dump64(s, s->max_mapnr); - kh->phys_base = cpu_to_dump32(s, PHYS_BASE); + kh->phys_base = cpu_to_dump32(s, s->dump_info.phys_base); kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL); offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; @@ -875,7 +875,7 @@ static void create_header64(DumpState *s, Error **errp) /* 64bit max_mapnr_64 */ kh->max_mapnr_64 = cpu_to_dump64(s, s->max_mapnr); - kh->phys_base = cpu_to_dump64(s, PHYS_BASE); + kh->phys_base = cpu_to_dump64(s, s->dump_info.phys_base); kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL); offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; diff --git a/include/sysemu/dump-arch.h b/include/sysemu/dump-arch.h index 43358396ea..e25b02e990 100644 --- a/include/sysemu/dump-arch.h +++ b/include/sysemu/dump-arch.h @@ -20,6 +20,7 @@ typedef struct ArchDumpInfo { int d_class; /* ELFCLASS32 or ELFCLASS64 */ uint32_t page_size; /* The target's page size. If it's variable and * unknown, then this should be the maximum. */ + uint64_t phys_base; /* The target's physmem base. */ } ArchDumpInfo; struct GuestPhysBlockList; /* memory_mapping.h */ diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index 16cbd8d881..2f04b247be 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -33,7 +33,6 @@ #define KDUMP_SIGNATURE "KDUMP " #define SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1) -#define PHYS_BASE (0) #define DUMP_LEVEL (1) #define DISKDUMP_HEADER_BLOCKS (1) From da2b91409ffe33f0a59acff1843ce1758325f2ec Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Jan 2016 20:56:22 +0100 Subject: [PATCH 07/11] target-arm: support QMP dump-guest-memory Add the support needed for creating prstatus elf notes. This allows us to use QMP dump-guest-memory. Signed-off-by: Andrew Jones Message-id: 1452542185-10914-6-git-send-email-drjones@redhat.com Reviewed-by: Peter Maydell [PMM: moved setting of cpu::write_elf64_note inside !CONFIG_USER_ONLY ifdef to avoid compile failure for linux-user build] Signed-off-by: Peter Maydell --- target-arm/Makefile.objs | 3 +- target-arm/arch_dump.c | 230 +++++++++++++++++++++++++++++++++++++++ target-arm/cpu-qom.h | 5 + target-arm/cpu.c | 2 + 4 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 target-arm/arch_dump.c diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index 9460b409a5..a80eb39743 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -1,5 +1,5 @@ obj-y += arm-semi.o -obj-$(CONFIG_SOFTMMU) += machine.o +obj-$(CONFIG_SOFTMMU) += machine.o psci.o arch_dump.o obj-$(CONFIG_KVM) += kvm.o obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o @@ -7,6 +7,5 @@ obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o obj-y += gdbstub.o -obj-$(CONFIG_SOFTMMU) += psci.o obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o obj-y += crypto_helper.o diff --git a/target-arm/arch_dump.c b/target-arm/arch_dump.c new file mode 100644 index 0000000000..f7a4873787 --- /dev/null +++ b/target-arm/arch_dump.c @@ -0,0 +1,230 @@ +/* Support for writing ELF notes for ARM architectures + * + * Copyright (C) 2015 Red Hat Inc. + * + * Author: Andrew Jones + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 . + */ + +#include "cpu.h" +#include "elf.h" +#include "sysemu/dump.h" + +/* struct user_pt_regs from arch/arm64/include/uapi/asm/ptrace.h */ +struct aarch64_user_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct aarch64_user_regs) != 272); + +/* struct elf_prstatus from include/uapi/linux/elfcore.h */ +struct aarch64_elf_prstatus { + char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ + uint32_t pr_pid; + char pad2[76]; /* 76 == offsetof(struct elf_prstatus, pr_reg) - + offsetof(struct elf_prstatus, pr_ppid) */ + struct aarch64_user_regs pr_reg; + uint32_t pr_fpvalid; + char pad3[4]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct aarch64_elf_prstatus) != 392); + +struct aarch64_note { + Elf64_Nhdr hdr; + char name[8]; /* align_up(sizeof("CORE"), 4) */ + struct aarch64_elf_prstatus prstatus; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct aarch64_note) != 412); + +static void aarch64_note_init(struct aarch64_note *note, DumpState *s, + const char *name, Elf64_Word namesz, + Elf64_Word type, Elf64_Word descsz) +{ + memset(note, 0, sizeof(*note)); + + note->hdr.n_namesz = cpu_to_dump32(s, namesz); + note->hdr.n_descsz = cpu_to_dump32(s, descsz); + note->hdr.n_type = cpu_to_dump32(s, type); + + memcpy(note->name, name, namesz); +} + +int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque) +{ + struct aarch64_note note; + CPUARMState *env = &ARM_CPU(cs)->env; + DumpState *s = opaque; + uint64_t pstate, sp; + int ret, i; + + aarch64_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); + + note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); + + if (!is_a64(env)) { + aarch64_sync_32_to_64(env); + pstate = cpsr_read(env); + sp = 0; + } else { + pstate = pstate_read(env); + sp = env->xregs[31]; + } + + for (i = 0; i < 31; ++i) { + note.prstatus.pr_reg.regs[i] = cpu_to_dump64(s, env->xregs[i]); + } + note.prstatus.pr_reg.sp = cpu_to_dump64(s, sp); + note.prstatus.pr_reg.pc = cpu_to_dump64(s, env->pc); + note.prstatus.pr_reg.pstate = cpu_to_dump64(s, pstate); + + ret = f(¬e, sizeof(note), s); + if (ret < 0) { + return -1; + } + + return 0; +} + +/* struct pt_regs from arch/arm/include/asm/ptrace.h */ +struct arm_user_regs { + uint32_t regs[17]; + char pad[4]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct arm_user_regs) != 72); + +/* struct elf_prstatus from include/uapi/linux/elfcore.h */ +struct arm_elf_prstatus { + char pad1[24]; /* 24 == offsetof(struct elf_prstatus, pr_pid) */ + uint32_t pr_pid; + char pad2[44]; /* 44 == offsetof(struct elf_prstatus, pr_reg) - + offsetof(struct elf_prstatus, pr_ppid) */ + struct arm_user_regs pr_reg; + uint32_t pr_fpvalid; +} QEMU_PACKED arm_elf_prstatus; + +QEMU_BUILD_BUG_ON(sizeof(struct arm_elf_prstatus) != 148); + +struct arm_note { + Elf32_Nhdr hdr; + char name[8]; /* align_up(sizeof("CORE"), 4) */ + struct arm_elf_prstatus prstatus; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct arm_note) != 168); + +static void arm_note_init(struct arm_note *note, DumpState *s, + const char *name, Elf32_Word namesz, + Elf32_Word type, Elf32_Word descsz) +{ + memset(note, 0, sizeof(*note)); + + note->hdr.n_namesz = cpu_to_dump32(s, namesz); + note->hdr.n_descsz = cpu_to_dump32(s, descsz); + note->hdr.n_type = cpu_to_dump32(s, type); + + memcpy(note->name, name, namesz); +} + +int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque) +{ + struct arm_note note; + CPUARMState *env = &ARM_CPU(cs)->env; + DumpState *s = opaque; + int ret, i; + + arm_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); + + note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); + + for (i = 0; i < 16; ++i) { + note.prstatus.pr_reg.regs[i] = cpu_to_dump32(s, env->regs[i]); + } + note.prstatus.pr_reg.regs[16] = cpu_to_dump32(s, cpsr_read(env)); + + ret = f(¬e, sizeof(note), s); + if (ret < 0) { + return -1; + } + + return 0; +} + +int cpu_get_dump_info(ArchDumpInfo *info, + const GuestPhysBlockList *guest_phys_blocks) +{ + ARMCPU *cpu = ARM_CPU(first_cpu); + CPUARMState *env = &cpu->env; + GuestPhysBlock *block; + hwaddr lowest_addr = ULLONG_MAX; + + /* Take a best guess at the phys_base. If we get it wrong then crash + * will need '--machdep phys_offset=' added to its command + * line, which isn't any worse than assuming we can use zero, but being + * wrong. This is the same algorithm the crash utility uses when + * attempting to guess as it loads non-dumpfile formatted files. + */ + QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) { + if (block->target_start < lowest_addr) { + lowest_addr = block->target_start; + } + } + + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + info->d_machine = EM_AARCH64; + info->d_class = ELFCLASS64; + info->page_size = (1 << 16); /* aarch64 max pagesize */ + if (lowest_addr != ULLONG_MAX) { + info->phys_base = lowest_addr; + } + } else { + info->d_machine = EM_ARM; + info->d_class = ELFCLASS32; + info->page_size = (1 << 12); + if (lowest_addr < UINT_MAX) { + info->phys_base = lowest_addr; + } + } + + /* We assume the relevant endianness is that of EL1; this is right + * for kernels, but might give the wrong answer if you're trying to + * dump a hypervisor that happens to be running an opposite-endian + * kernel. + */ + info->d_endian = (env->cp15.sctlr_el[1] & SCTLR_EE) != 0 + ? ELFDATA2MSB : ELFDATA2LSB; + + return 0; +} + +ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) +{ + size_t note_size; + + if (class == ELFCLASS64) { + note_size = sizeof(struct aarch64_note); + } else { + note_size = sizeof(struct arm_note); + } + + return note_size * nr_cpus; +} diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 25fb1ce0f3..5bd9b7bb9f 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -221,6 +221,11 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque); +int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque); + /* Callback functions for the generic timer's timers. */ void arm_gt_ptimer_cb(void *opaque); void arm_gt_vtimer_cb(void *opaque); diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 775e0a46bd..450ef78a3d 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -1421,6 +1421,8 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_debug = arm_cpu_get_phys_page_debug; cc->vmsd = &vmstate_arm_cpu; cc->virtio_is_big_endian = arm_cpu_is_big_endian; + cc->write_elf64_note = arm_cpu_write_elf64_note; + cc->write_elf32_note = arm_cpu_write_elf32_note; #endif cc->gdb_num_core_regs = 26; cc->gdb_core_xml_file = "arm-core.xml"; From bada8e4470a4fe57c67688a49bf18a2cee654ef5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Jan 2016 20:56:23 +0100 Subject: [PATCH 08/11] target-arm: dump-guest-memory: add prfpreg notes for aarch64 Signed-off-by: Andrew Jones Reviewed-by: Peter Maydell Message-id: 1452542185-10914-7-git-send-email-drjones@redhat.com Signed-off-by: Peter Maydell --- target-arm/arch_dump.c | 79 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/target-arm/arch_dump.c b/target-arm/arch_dump.c index f7a4873787..d7b63a0d7e 100644 --- a/target-arm/arch_dump.c +++ b/target-arm/arch_dump.c @@ -45,13 +45,36 @@ struct aarch64_elf_prstatus { QEMU_BUILD_BUG_ON(sizeof(struct aarch64_elf_prstatus) != 392); +/* struct user_fpsimd_state from arch/arm64/include/uapi/asm/ptrace.h + * + * While the vregs member of user_fpsimd_state is of type __uint128_t, + * QEMU uses an array of uint64_t, where the high half of the 128-bit + * value is always in the 2n+1'th index. Thus we also break the 128- + * bit values into two halves in this reproduction of user_fpsimd_state. + */ +struct aarch64_user_vfp_state { + uint64_t vregs[64]; + uint32_t fpsr; + uint32_t fpcr; + char pad[8]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct aarch64_user_vfp_state) != 528); + struct aarch64_note { Elf64_Nhdr hdr; char name[8]; /* align_up(sizeof("CORE"), 4) */ - struct aarch64_elf_prstatus prstatus; + union { + struct aarch64_elf_prstatus prstatus; + struct aarch64_user_vfp_state vfp; + }; } QEMU_PACKED; -QEMU_BUILD_BUG_ON(sizeof(struct aarch64_note) != 412); +#define AARCH64_NOTE_HEADER_SIZE offsetof(struct aarch64_note, prstatus) +#define AARCH64_PRSTATUS_NOTE_SIZE \ + (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_elf_prstatus)) +#define AARCH64_PRFPREG_NOTE_SIZE \ + (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_user_vfp_state)) static void aarch64_note_init(struct aarch64_note *note, DumpState *s, const char *name, Elf64_Word namesz, @@ -66,6 +89,42 @@ static void aarch64_note_init(struct aarch64_note *note, DumpState *s, memcpy(note->name, name, namesz); } +static int aarch64_write_elf64_prfpreg(WriteCoreDumpFunction f, + CPUARMState *env, int cpuid, + DumpState *s) +{ + struct aarch64_note note; + int ret, i; + + aarch64_note_init(¬e, s, "CORE", 5, NT_PRFPREG, sizeof(note.vfp)); + + for (i = 0; i < 64; ++i) { + note.vfp.vregs[i] = cpu_to_dump64(s, float64_val(env->vfp.regs[i])); + } + + if (s->dump_info.d_endian == ELFDATA2MSB) { + /* For AArch64 we must always swap the vfp.regs's 2n and 2n+1 + * entries when generating BE notes, because even big endian + * hosts use 2n+1 for the high half. + */ + for (i = 0; i < 32; ++i) { + uint64_t tmp = note.vfp.vregs[2*i]; + note.vfp.vregs[2*i] = note.vfp.vregs[2*i+1]; + note.vfp.vregs[2*i+1] = tmp; + } + } + + note.vfp.fpsr = cpu_to_dump32(s, vfp_get_fpsr(env)); + note.vfp.fpcr = cpu_to_dump32(s, vfp_get_fpcr(env)); + + ret = f(¬e, AARCH64_PRFPREG_NOTE_SIZE, s); + if (ret < 0) { + return -1; + } + + return 0; +} + int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque) { @@ -78,6 +137,7 @@ int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, aarch64_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); + note.prstatus.pr_fpvalid = cpu_to_dump32(s, 1); if (!is_a64(env)) { aarch64_sync_32_to_64(env); @@ -95,12 +155,12 @@ int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, note.prstatus.pr_reg.pc = cpu_to_dump64(s, env->pc); note.prstatus.pr_reg.pstate = cpu_to_dump64(s, pstate); - ret = f(¬e, sizeof(note), s); + ret = f(¬e, AARCH64_PRSTATUS_NOTE_SIZE, s); if (ret < 0) { return -1; } - return 0; + return aarch64_write_elf64_prfpreg(f, env, cpuid, s); } /* struct pt_regs from arch/arm/include/asm/ptrace.h */ @@ -129,7 +189,9 @@ struct arm_note { struct arm_elf_prstatus prstatus; } QEMU_PACKED; -QEMU_BUILD_BUG_ON(sizeof(struct arm_note) != 168); +#define ARM_NOTE_HEADER_SIZE offsetof(struct arm_note, prstatus) +#define ARM_PRSTATUS_NOTE_SIZE \ + (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_elf_prstatus)) static void arm_note_init(struct arm_note *note, DumpState *s, const char *name, Elf32_Word namesz, @@ -161,7 +223,7 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, } note.prstatus.pr_reg.regs[16] = cpu_to_dump32(s, cpsr_read(env)); - ret = f(¬e, sizeof(note), s); + ret = f(¬e, ARM_PRSTATUS_NOTE_SIZE, s); if (ret < 0) { return -1; } @@ -221,9 +283,10 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) size_t note_size; if (class == ELFCLASS64) { - note_size = sizeof(struct aarch64_note); + note_size = AARCH64_PRSTATUS_NOTE_SIZE; + note_size += AARCH64_PRFPREG_NOTE_SIZE; } else { - note_size = sizeof(struct arm_note); + note_size = ARM_PRSTATUS_NOTE_SIZE; } return note_size * nr_cpus; From 7d68e47f1280f911df0ea87051d70a4ee52d701d Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Jan 2016 20:56:24 +0100 Subject: [PATCH 09/11] elf: add arm note types Signed-off-by: Andrew Jones Reviewed-by: Peter Maydell Message-id: 1452542185-10914-8-git-send-email-drjones@redhat.com Signed-off-by: Peter Maydell --- include/elf.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/elf.h b/include/elf.h index 66add810df..1098d217ec 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1471,6 +1471,11 @@ typedef struct elf64_shdr { #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ +#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ /* Note header in a PT_NOTE section */ From ade0d0c0d3a0444c626ec5f46ef75af7395236f6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 11 Jan 2016 20:56:25 +0100 Subject: [PATCH 10/11] target-arm: dump-guest-memory: add vfp notes for arm gdb won't actually dump these with 'info all-registers' since it first tries to confirm that it should by checking the VFP hwcap in the .auxv note. Well, we don't generate an .auxv note. Signed-off-by: Andrew Jones Reviewed-by: Peter Maydell Message-id: 1452542185-10914-9-git-send-email-drjones@redhat.com Signed-off-by: Peter Maydell --- target-arm/arch_dump.c | 49 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/target-arm/arch_dump.c b/target-arm/arch_dump.c index d7b63a0d7e..f45125ffe6 100644 --- a/target-arm/arch_dump.c +++ b/target-arm/arch_dump.c @@ -183,15 +183,28 @@ struct arm_elf_prstatus { QEMU_BUILD_BUG_ON(sizeof(struct arm_elf_prstatus) != 148); +/* struct user_vfp from arch/arm/include/asm/user.h */ +struct arm_user_vfp_state { + uint64_t vregs[32]; + uint32_t fpscr; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct arm_user_vfp_state) != 260); + struct arm_note { Elf32_Nhdr hdr; - char name[8]; /* align_up(sizeof("CORE"), 4) */ - struct arm_elf_prstatus prstatus; + char name[8]; /* align_up(sizeof("LINUX"), 4) */ + union { + struct arm_elf_prstatus prstatus; + struct arm_user_vfp_state vfp; + }; } QEMU_PACKED; #define ARM_NOTE_HEADER_SIZE offsetof(struct arm_note, prstatus) #define ARM_PRSTATUS_NOTE_SIZE \ (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_elf_prstatus)) +#define ARM_VFP_NOTE_SIZE \ + (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_user_vfp_state)) static void arm_note_init(struct arm_note *note, DumpState *s, const char *name, Elf32_Word namesz, @@ -206,17 +219,40 @@ static void arm_note_init(struct arm_note *note, DumpState *s, memcpy(note->name, name, namesz); } +static int arm_write_elf32_vfp(WriteCoreDumpFunction f, CPUARMState *env, + int cpuid, DumpState *s) +{ + struct arm_note note; + int ret, i; + + arm_note_init(¬e, s, "LINUX", 6, NT_ARM_VFP, sizeof(note.vfp)); + + for (i = 0; i < 32; ++i) { + note.vfp.vregs[i] = cpu_to_dump64(s, float64_val(env->vfp.regs[i])); + } + + note.vfp.fpscr = cpu_to_dump32(s, vfp_get_fpscr(env)); + + ret = f(¬e, ARM_VFP_NOTE_SIZE, s); + if (ret < 0) { + return -1; + } + + return 0; +} + int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque) { struct arm_note note; CPUARMState *env = &ARM_CPU(cs)->env; DumpState *s = opaque; - int ret, i; + int ret, i, fpvalid = !!arm_feature(env, ARM_FEATURE_VFP); arm_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); + note.prstatus.pr_fpvalid = cpu_to_dump32(s, fpvalid); for (i = 0; i < 16; ++i) { note.prstatus.pr_reg.regs[i] = cpu_to_dump32(s, env->regs[i]); @@ -226,6 +262,8 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, ret = f(¬e, ARM_PRSTATUS_NOTE_SIZE, s); if (ret < 0) { return -1; + } else if (fpvalid) { + return arm_write_elf32_vfp(f, env, cpuid, s); } return 0; @@ -280,6 +318,8 @@ int cpu_get_dump_info(ArchDumpInfo *info, ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) { + ARMCPU *cpu = ARM_CPU(first_cpu); + CPUARMState *env = &cpu->env; size_t note_size; if (class == ELFCLASS64) { @@ -287,6 +327,9 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) note_size += AARCH64_PRFPREG_NOTE_SIZE; } else { note_size = ARM_PRSTATUS_NOTE_SIZE; + if (arm_feature(env, ARM_FEATURE_VFP)) { + note_size += ARM_VFP_NOTE_SIZE; + } } return note_size * nr_cpus; From 67736a25f86595c8ae174a21d965af5a027884c9 Mon Sep 17 00:00:00 2001 From: Shannon Zhao Date: Fri, 15 Jan 2016 22:11:31 +0800 Subject: [PATCH 11/11] ARM: virt: Don't generate RTC ACPI device when using UEFI When booting the VM with UEFI, UEFI takes ownership of the RTC hardware. While UEFI can use libfdt to disable the RTC device node in the DTB that it passes to the OS, it cannot modify AML. Therefore, we won't generate the RTC ACPI device at all when using UEFI. Signed-off-by: Shannon Zhao Acked-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek Reviewed-by: Andrew Jones Message-id: 1452867091-4023-1-git-send-email-shannon.zhao@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 0caf5ced69..0d5c635cc8 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -94,23 +94,6 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, aml_append(scope, dev); } -static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap, - uint32_t rtc_irq) -{ - Aml *dev = aml_device("RTC0"); - aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); - - Aml *crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(rtc_memmap->base, - rtc_memmap->size, AML_READ_WRITE)); - aml_append(crs, - aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, &rtc_irq, 1)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); -} - static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap) { Aml *dev, *crs; @@ -571,12 +554,15 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) /* Reserve space for header */ acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader)); + /* When booting the VM with UEFI, UEFI takes ownership of the RTC hardware. + * While UEFI can use libfdt to disable the RTC device node in the DTB that + * it passes to the OS, it cannot modify AML. Therefore, we won't generate + * the RTC ACPI device at all when using UEFI. + */ scope = aml_scope("\\_SB"); acpi_dsdt_add_cpus(scope, guest_info->smp_cpus); acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], (irqmap[VIRT_UART] + ARM_SPI_BASE)); - acpi_dsdt_add_rtc(scope, &memmap[VIRT_RTC], - (irqmap[VIRT_RTC] + ARM_SPI_BASE)); acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO], (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);