From 26d320220778973310de0afa96974a2e9e987caf Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Tue, 7 Feb 2017 18:29:58 +0000 Subject: [PATCH 01/13] integratorcp: adding vmstate for save/restore VMState added by this patch preserves correct loading of the integratorcp device state. Signed-off-by: Pavel Dovgalyuk Message-id: 20170131114310.6768.79416.stgit@PASHA-ISP [PMM: removed unnecessary minimum_version_id_old lines] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/integratorcp.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 039812a3fd..f81634d903 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -53,6 +53,26 @@ static uint8_t integrator_spd[128] = { 0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40 }; +static const VMStateDescription vmstate_integratorcm = { + .name = "integratorcm", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cm_osc, IntegratorCMState), + VMSTATE_UINT32(cm_ctrl, IntegratorCMState), + VMSTATE_UINT32(cm_lock, IntegratorCMState), + VMSTATE_UINT32(cm_auxosc, IntegratorCMState), + VMSTATE_UINT32(cm_sdram, IntegratorCMState), + VMSTATE_UINT32(cm_init, IntegratorCMState), + VMSTATE_UINT32(cm_flags, IntegratorCMState), + VMSTATE_UINT32(cm_nvflags, IntegratorCMState), + VMSTATE_UINT32(int_level, IntegratorCMState), + VMSTATE_UINT32(irq_enabled, IntegratorCMState), + VMSTATE_UINT32(fiq_enabled, IntegratorCMState), + VMSTATE_END_OF_LIST() + } +}; + static uint64_t integratorcm_read(void *opaque, hwaddr offset, unsigned size) { @@ -309,6 +329,18 @@ typedef struct icp_pic_state { qemu_irq parent_fiq; } icp_pic_state; +static const VMStateDescription vmstate_icp_pic = { + .name = "icp_pic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, icp_pic_state), + VMSTATE_UINT32(irq_enabled, icp_pic_state), + VMSTATE_UINT32(fiq_enabled, icp_pic_state), + VMSTATE_END_OF_LIST() + } +}; + static void icp_pic_update(icp_pic_state *s) { uint32_t flags; @@ -438,6 +470,16 @@ typedef struct ICPCtrlRegsState { #define ICP_INTREG_WPROT (1 << 0) #define ICP_INTREG_CARDIN (1 << 3) +static const VMStateDescription vmstate_icp_control = { + .name = "icp_control", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(intreg_state, ICPCtrlRegsState), + VMSTATE_END_OF_LIST() + } +}; + static uint64_t icp_control_read(void *opaque, hwaddr offset, unsigned size) { @@ -640,6 +682,21 @@ static void core_class_init(ObjectClass *klass, void *data) dc->props = core_properties; dc->realize = integratorcm_realize; + dc->vmsd = &vmstate_integratorcm; +} + +static void icp_pic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_icp_pic; +} + +static void icp_control_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_icp_control; } static const TypeInfo core_info = { @@ -655,6 +712,7 @@ static const TypeInfo icp_pic_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pic_state), .instance_init = icp_pic_init, + .class_init = icp_pic_class_init, }; static const TypeInfo icp_ctrl_regs_info = { @@ -662,6 +720,7 @@ static const TypeInfo icp_ctrl_regs_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ICPCtrlRegsState), .instance_init = icp_control_init, + .class_init = icp_control_class_init, }; static void integratorcp_register_types(void) From 854123bf8d4b8f4dcedcb03d0201e4305da45fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Feb 2017 18:29:59 +0000 Subject: [PATCH 02/13] wdt: Add Aspeed watchdog device model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Aspeed SoC includes a set of watchdog timers using 32-bit decrement counters, which can be based either on the APB clock or a 1 MHz clock. The watchdog timer is designed to prevent system deadlock and, in general, it should be restarted before timeout. When a timeout occurs, different types of signals can be generated, ARM reset, SOC reset, System reset, CPU Interrupt, external signal or boot from alternate block. The current model only performs the system reset function as this is used by U-Boot and Linux. Signed-off-by: Joel Stanley Message-id: 1485452251-1593-2-git-send-email-clg@kaod.org [clg: - fixed compile breakage - fixed io region size - added watchdog_perform_action() on timer expiry - wrote a commit log - merged fixes from Andrew Jeffery to scale the reload value ] Signed-off-by: Cédric Le Goater Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/watchdog/Makefile.objs | 1 + hw/watchdog/wdt_aspeed.c | 225 +++++++++++++++++++++++++++++++ include/hw/watchdog/wdt_aspeed.h | 32 +++++ 3 files changed, 258 insertions(+) create mode 100644 hw/watchdog/wdt_aspeed.c create mode 100644 include/hw/watchdog/wdt_aspeed.h diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs index 72e3ffd93c..9589bed63a 100644 --- a/hw/watchdog/Makefile.objs +++ b/hw/watchdog/Makefile.objs @@ -2,3 +2,4 @@ common-obj-y += watchdog.o common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o +common-obj-$(CONFIG_ASPEED_SOC) += wdt_aspeed.o diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c new file mode 100644 index 0000000000..8bbe579b6b --- /dev/null +++ b/hw/watchdog/wdt_aspeed.c @@ -0,0 +1,225 @@ +/* + * ASPEED Watchdog Controller + * + * Copyright (C) 2016-2017 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "sysemu/watchdog.h" +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/watchdog/wdt_aspeed.h" + +#define WDT_STATUS (0x00 / 4) +#define WDT_RELOAD_VALUE (0x04 / 4) +#define WDT_RESTART (0x08 / 4) +#define WDT_CTRL (0x0C / 4) +#define WDT_CTRL_RESET_MODE_SOC (0x00 << 5) +#define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5) +#define WDT_CTRL_1MHZ_CLK BIT(4) +#define WDT_CTRL_WDT_EXT BIT(3) +#define WDT_CTRL_WDT_INTR BIT(2) +#define WDT_CTRL_RESET_SYSTEM BIT(1) +#define WDT_CTRL_ENABLE BIT(0) + +#define WDT_TIMEOUT_STATUS (0x10 / 4) +#define WDT_TIMEOUT_CLEAR (0x14 / 4) +#define WDT_RESET_WDITH (0x18 / 4) + +#define WDT_RESTART_MAGIC 0x4755 + +static bool aspeed_wdt_is_enabled(const AspeedWDTState *s) +{ + return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE; +} + +static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedWDTState *s = ASPEED_WDT(opaque); + + offset >>= 2; + + switch (offset) { + case WDT_STATUS: + return s->regs[WDT_STATUS]; + case WDT_RELOAD_VALUE: + return s->regs[WDT_RELOAD_VALUE]; + case WDT_RESTART: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from write-only reg at offset 0x%" + HWADDR_PRIx "\n", __func__, offset); + return 0; + case WDT_CTRL: + return s->regs[WDT_CTRL]; + case WDT_TIMEOUT_STATUS: + case WDT_TIMEOUT_CLEAR: + case WDT_RESET_WDITH: + qemu_log_mask(LOG_UNIMP, + "%s: uninmplemented read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + +} + +static void aspeed_wdt_reload(AspeedWDTState *s, bool pclk) +{ + uint32_t reload; + + if (pclk) { + reload = muldiv64(s->regs[WDT_RELOAD_VALUE], NANOSECONDS_PER_SECOND, + s->pclk_freq); + } else { + reload = s->regs[WDT_RELOAD_VALUE] * 1000; + } + + if (aspeed_wdt_is_enabled(s)) { + timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload); + } +} + +static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedWDTState *s = ASPEED_WDT(opaque); + bool enable = data & WDT_CTRL_ENABLE; + + offset >>= 2; + + switch (offset) { + case WDT_STATUS: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read-only reg at offset 0x%" + HWADDR_PRIx "\n", __func__, offset); + break; + case WDT_RELOAD_VALUE: + s->regs[WDT_RELOAD_VALUE] = data; + break; + case WDT_RESTART: + if ((data & 0xFFFF) == WDT_RESTART_MAGIC) { + s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE]; + aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); + } + break; + case WDT_CTRL: + if (enable && !aspeed_wdt_is_enabled(s)) { + s->regs[WDT_CTRL] = data; + aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); + } else if (!enable && aspeed_wdt_is_enabled(s)) { + s->regs[WDT_CTRL] = data; + timer_del(s->timer); + } + break; + case WDT_TIMEOUT_STATUS: + case WDT_TIMEOUT_CLEAR: + case WDT_RESET_WDITH: + qemu_log_mask(LOG_UNIMP, + "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + return; +} + +static WatchdogTimerModel model = { + .wdt_name = TYPE_ASPEED_WDT, + .wdt_description = "Aspeed watchdog device", +}; + +static const VMStateDescription vmstate_aspeed_wdt = { + .name = "vmstate_aspeed_wdt", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(timer, AspeedWDTState), + VMSTATE_UINT32_ARRAY(regs, AspeedWDTState, ASPEED_WDT_REGS_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static const MemoryRegionOps aspeed_wdt_ops = { + .read = aspeed_wdt_read, + .write = aspeed_wdt_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void aspeed_wdt_reset(DeviceState *dev) +{ + AspeedWDTState *s = ASPEED_WDT(dev); + + s->regs[WDT_STATUS] = 0x3EF1480; + s->regs[WDT_RELOAD_VALUE] = 0x03EF1480; + s->regs[WDT_RESTART] = 0; + s->regs[WDT_CTRL] = 0; + + timer_del(s->timer); +} + +static void aspeed_wdt_timer_expired(void *dev) +{ + AspeedWDTState *s = ASPEED_WDT(dev); + + qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n"); + watchdog_perform_action(); + timer_del(s->timer); +} + +#define PCLK_HZ 24000000 + +static void aspeed_wdt_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedWDTState *s = ASPEED_WDT(dev); + + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); + + /* FIXME: This setting should be derived from the SCU hw strapping + * register SCU70 + */ + s->pclk_freq = PCLK_HZ; + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s, + TYPE_ASPEED_WDT, ASPEED_WDT_REGS_MAX * 4); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_wdt_realize; + dc->reset = aspeed_wdt_reset; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->vmsd = &vmstate_aspeed_wdt; +} + +static const TypeInfo aspeed_wdt_info = { + .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_ASPEED_WDT, + .instance_size = sizeof(AspeedWDTState), + .class_init = aspeed_wdt_class_init, +}; + +static void wdt_aspeed_register_types(void) +{ + watchdog_add_model(&model); + type_register_static(&aspeed_wdt_info); +} + +type_init(wdt_aspeed_register_types) diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h new file mode 100644 index 0000000000..080c223122 --- /dev/null +++ b/include/hw/watchdog/wdt_aspeed.h @@ -0,0 +1,32 @@ +/* + * ASPEED Watchdog Controller + * + * Copyright (C) 2016-2017 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#ifndef ASPEED_WDT_H +#define ASPEED_WDT_H + +#include "hw/sysbus.h" + +#define TYPE_ASPEED_WDT "aspeed.wdt" +#define ASPEED_WDT(obj) \ + OBJECT_CHECK(AspeedWDTState, (obj), TYPE_ASPEED_WDT) + +#define ASPEED_WDT_REGS_MAX (0x20 / 4) + +typedef struct AspeedWDTState { + /*< private >*/ + SysBusDevice parent_obj; + QEMUTimer *timer; + + /*< public >*/ + MemoryRegion iomem; + uint32_t regs[ASPEED_WDT_REGS_MAX]; + + uint32_t pclk_freq; +} AspeedWDTState; + +#endif /* ASPEED_WDT_H */ From 013befe1ca055f555062c8ead083b3c214be97b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 7 Feb 2017 18:29:59 +0000 Subject: [PATCH 03/13] aspeed: add a watchdog controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This enables reboot of a guest from U-Boot and Linux. Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Message-id: 1485452251-1593-3-git-send-email-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed_soc.c | 13 +++++++++++++ include/hw/arm/aspeed_soc.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index b3e7f07b61..571e4f097b 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -31,6 +31,7 @@ #define ASPEED_SOC_SCU_BASE 0x1E6E2000 #define ASPEED_SOC_SRAM_BASE 0x1E720000 #define ASPEED_SOC_TIMER_BASE 0x1E782000 +#define ASPEED_SOC_WDT_BASE 0x1E785000 #define ASPEED_SOC_I2C_BASE 0x1E78A000 static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; @@ -170,6 +171,10 @@ static void aspeed_soc_init(Object *obj) sc->info->silicon_rev); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size", &error_abort); + + object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ASPEED_WDT); + object_property_add_child(obj, "wdt", OBJECT(&s->wdt), NULL); + qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default()); } static void aspeed_soc_realize(DeviceState *dev, Error **errp) @@ -286,6 +291,14 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, ASPEED_SOC_SDMC_BASE); + + /* Watch dog */ + object_property_set_bool(OBJECT(&s->wdt), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, ASPEED_SOC_WDT_BASE); } static void aspeed_soc_class_init(ObjectClass *oc, void *data) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 1ab5deaa08..dbec0c1598 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -19,6 +19,7 @@ #include "hw/timer/aspeed_timer.h" #include "hw/i2c/aspeed_i2c.h" #include "hw/ssi/aspeed_smc.h" +#include "hw/watchdog/wdt_aspeed.h" #define ASPEED_SPIS_NUM 2 @@ -37,6 +38,7 @@ typedef struct AspeedSoCState { AspeedSMCState fmc; AspeedSMCState spi[ASPEED_SPIS_NUM]; AspeedSDMCState sdmc; + AspeedWDTState wdt; } AspeedSoCState; #define TYPE_ASPEED_SOC "aspeed-soc" From 42922105beb14c2fc58185ea022b9f72fb5465e9 Mon Sep 17 00:00:00 2001 From: Prasad J Pandit Date: Tue, 7 Feb 2017 18:29:59 +0000 Subject: [PATCH 04/13] sd: sdhci: check data length during dma_memory_read While doing multi block SDMA transfer in routine 'sdhci_sdma_transfer_multi_blocks', the 's->fifo_buffer' starting index 'begin' and data length 's->data_count' could end up to be same. This could lead to an OOB access issue. Correct transfer data length to avoid it. Cc: qemu-stable@nongnu.org Reported-by: Jiang Xin Signed-off-by: Prasad J Pandit Reviewed-by: Peter Maydell Message-id: 20170130064736.9236-1-ppandit@redhat.com Signed-off-by: Peter Maydell --- hw/sd/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 01fbf228be..5bd5ab6319 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -536,7 +536,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) boundary_count -= block_size - begin; } dma_memory_read(&address_space_memory, s->sdmasysad, - &s->fifo_buffer[begin], s->data_count); + &s->fifo_buffer[begin], s->data_count - begin); s->sdmasysad += s->data_count - begin; if (s->data_count == block_size) { for (n = 0; n < block_size; n++) { From 00909b585861ee3dca15f3d7624aac9be0f9080f Mon Sep 17 00:00:00 2001 From: Julian Brown Date: Tue, 7 Feb 2017 18:29:59 +0000 Subject: [PATCH 05/13] hw/arm/integratorcp: Support specifying features via -cpu Since the integratorcp board creates the CPU object directly rather than via cpu_arm_init(), we have to call the CPU class parse_features() method ourselves if we want to support the user passing features via the -cpu command line argument as well as just the cpu name. Do so. Signed-off-by: Julian Brown [PMM: split out into its own patch] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/integratorcp.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index f81634d903..5610ffc9ce 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -577,27 +577,42 @@ static void integratorcp_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; + char **cpustr; ObjectClass *cpu_oc; + CPUClass *cc; Object *cpuobj; ARMCPU *cpu; + const char *typename; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *ram_alias = g_new(MemoryRegion, 1); qemu_irq pic[32]; DeviceState *dev, *sic, *icp; int i; + Error *err = NULL; if (!cpu_model) { cpu_model = "arm926"; } - cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + cpustr = g_strsplit(cpu_model, ",", 2); + + cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); if (!cpu_oc) { fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } + typename = object_class_get_name(cpu_oc); - cpuobj = object_new(object_class_get_name(cpu_oc)); + cc = CPU_CLASS(cpu_oc); + cc->parse_features(typename, cpustr[1], &err); + g_strfreev(cpustr); + if (err) { + error_report_err(err); + exit(1); + } + + cpuobj = object_new(typename); /* By default ARM1176 CPUs have EL3 enabled. This board does not * currently support EL3 so the CPU EL3 property is disabled before From 3a062d5730266b2386eeda68b1a1c6e96451db31 Mon Sep 17 00:00:00 2001 From: Julian Brown Date: Tue, 7 Feb 2017 18:29:59 +0000 Subject: [PATCH 06/13] target/arm: Add cfgend parameter for ARM CPU selection. Add a new "cfgend" property which selects whether the CPU resets into big-endian mode or not. This setting affects whether we reset with SCTLR_B (ARMv6 and earlier) or SCTLR_EE (ARMv7 and later) set. Signed-off-by: Julian Brown Message-id: 11420d1c49636c1790e60578ee996e51f0f0b835.1484929304.git.julian@codesourcery.com [PMM: use error_report_err() rather than error_report(); move the integratorcp changes to their own patch; drop an unnecessary extra #include; rephrase commit message accordingly; move setting of reset_sctlr above registration of cpregs so it actually has an effect] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu.c | 13 +++++++++++++ target/arm/cpu.h | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e9f10f7747..a8cfd9d0f5 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -541,6 +541,9 @@ static Property arm_cpu_has_el2_property = static Property arm_cpu_has_el3_property = DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); +static Property arm_cpu_cfgend_property = + DEFINE_PROP_BOOL("cfgend", ARMCPU, cfgend, false); + /* use property name "pmu" to match other archs and virt tools */ static Property arm_cpu_has_pmu_property = DEFINE_PROP_BOOL("pmu", ARMCPU, has_pmu, true); @@ -608,6 +611,8 @@ static void arm_cpu_post_init(Object *obj) } } + qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property, + &error_abort); } static void arm_cpu_finalizefn(Object *obj) @@ -728,6 +733,14 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->reset_sctlr |= (1 << 13); } + if (cpu->cfgend) { + if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { + cpu->reset_sctlr |= SCTLR_EE; + } else { + cpu->reset_sctlr |= SCTLR_B; + } + } + if (!cpu->has_el3) { /* If the has_el3 CPU property is disabled then we need to disable the * feature. diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 39bff86daf..c0b3832d74 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -676,6 +676,13 @@ struct ARMCPU { int gic_vpribits; /* number of virtual priority bits */ int gic_vprebits; /* number of virtual preemption bits */ + /* Whether the cfgend input is high (i.e. this CPU should reset into + * big-endian mode). This setting isn't used directly: instead it modifies + * the reset_sctlr value to have SCTLR_B or SCTLR_EE set, depending on the + * architecture version. + */ + bool cfgend; + ARMELChangeHook *el_change_hook; void *el_change_hook_opaque; }; From f7478a92dd9ee2276bfaa5b7317140d3f9d6a53b Mon Sep 17 00:00:00 2001 From: Julian Brown Date: Tue, 7 Feb 2017 18:29:59 +0000 Subject: [PATCH 07/13] Fix Thumb-1 BE32 execution and disassembly. Thumb-1 code has some issues in BE32 mode (as currently implemented). In short, since bytes are swapped within words at load time for BE32 executables, this also swaps pairs of adjacent Thumb-1 instructions. This patch un-swaps those pairs of instructions again, both for execution, and for disassembly. (The previous version of the patch always read four bytes in arm_read_memory_func and then extracted the proper two bytes, in a probably misguided attempt to match the behaviour of actual hardware as described by e.g. the ARM9TDMI TRM, section 3.3 "Endian effects for instruction fetches". It's less complicated to just read the correct two bytes though.) Signed-off-by: Julian Brown Message-id: ca20462a044848000370318a8bd41dd0a4ed273f.1484929304.git.julian@codesourcery.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- disas.c | 1 + include/disas/bfd.h | 7 +++++++ target/arm/arm_ldst.h | 10 +++++++++- target/arm/cpu.c | 23 +++++++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/disas.c b/disas.c index 05a7a1260a..d335c55bbf 100644 --- a/disas.c +++ b/disas.c @@ -190,6 +190,7 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, s.cpu = cpu; s.info.read_memory_func = target_read_memory; + s.info.read_memory_inner_func = NULL; s.info.buffer_vma = code; s.info.buffer_length = size; s.info.print_address_func = generic_print_address; diff --git a/include/disas/bfd.h b/include/disas/bfd.h index 0435b8c9f9..b01e002b4c 100644 --- a/include/disas/bfd.h +++ b/include/disas/bfd.h @@ -295,6 +295,7 @@ typedef struct disassemble_info { The bottom 16 bits are for the internal use of the disassembler. */ unsigned long flags; #define INSN_HAS_RELOC 0x80000000 +#define INSN_ARM_BE32 0x00010000 PTR private_data; /* Function used to get bytes to disassemble. MEMADDR is the @@ -306,6 +307,12 @@ typedef struct disassemble_info { (bfd_vma memaddr, bfd_byte *myaddr, int length, struct disassemble_info *info); + /* A place to stash the real read_memory_func if read_memory_func wants to + do some funky address arithmetic or similar (e.g. for ARM BE32 mode). */ + int (*read_memory_inner_func) + (bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info); + /* Function which should be called if we get an error that we can't recover from. STATUS is the errno value from read_memory_func and MEMADDR is the address that we were trying to read. INFO is a diff --git a/target/arm/arm_ldst.h b/target/arm/arm_ldst.h index a76d89f62c..01587b3ebb 100644 --- a/target/arm/arm_ldst.h +++ b/target/arm/arm_ldst.h @@ -39,7 +39,15 @@ static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr, static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr, bool sctlr_b) { - uint16_t insn = cpu_lduw_code(env, addr); + uint16_t insn; +#ifndef CONFIG_USER_ONLY + /* In big-endian (BE32) mode, adjacent Thumb instructions have been swapped + within each word. Undo that now. */ + if (sctlr_b) { + addr ^= 2; + } +#endif + insn = cpu_lduw_code(env, addr); if (bswap_code(sctlr_b)) { return bswap16(insn); } diff --git a/target/arm/cpu.c b/target/arm/cpu.c index a8cfd9d0f5..81448caf18 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -446,6 +446,21 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info) return print_insn_arm(pc | 1, info); } +static int arm_read_memory_func(bfd_vma memaddr, bfd_byte *b, + int length, struct disassemble_info *info) +{ + assert(info->read_memory_inner_func); + assert((info->flags & INSN_ARM_BE32) == 0 || length == 2 || length == 4); + + if ((info->flags & INSN_ARM_BE32) != 0 && length == 2) { + assert(info->endian == BFD_ENDIAN_LITTLE); + return info->read_memory_inner_func(memaddr ^ 2, (bfd_byte *)b, 2, + info); + } else { + return info->read_memory_inner_func(memaddr, b, length, info); + } +} + static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) { ARMCPU *ac = ARM_CPU(cpu); @@ -471,6 +486,14 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) info->endian = BFD_ENDIAN_BIG; #endif } + if (info->read_memory_inner_func == NULL) { + info->read_memory_inner_func = info->read_memory_func; + info->read_memory_func = arm_read_memory_func; + } + info->flags &= ~INSN_ARM_BE32; + if (arm_sctlr_b(env)) { + info->flags |= INSN_ARM_BE32; + } } static void arm_cpu_initfn(Object *obj) From 40612000599e52e792d23c998377a0fa429c4036 Mon Sep 17 00:00:00 2001 From: Julian Brown Date: Tue, 7 Feb 2017 18:29:59 +0000 Subject: [PATCH 08/13] arm: Correctly handle watchpoints for BE32 CPUs In BE32 mode, sub-word size watchpoints can fail to trigger because the address of the access is adjusted in the opcode helpers before being compared with the watchpoint registers. This patch reverses the address adjustment before performing the comparison with the help of a new CPUClass hook. This version of the patch augments and tidies up comments a little. Signed-off-by: Julian Brown Message-id: caaf64ffc72f6ae183015337b7afdbd4b8989cb6.1484929304.git.julian@codesourcery.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- exec.c | 1 + include/qom/cpu.h | 3 +++ qom/cpu.c | 6 ++++++ target/arm/cpu.c | 3 +++ target/arm/internals.h | 5 +++++ target/arm/op_helper.c | 22 ++++++++++++++++++++++ 6 files changed, 40 insertions(+) diff --git a/exec.c b/exec.c index 8b9ed73b15..6fa337b8d8 100644 --- a/exec.c +++ b/exec.c @@ -2115,6 +2115,7 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags) return; } vaddr = (cpu->mem_io_vaddr & TARGET_PAGE_MASK) + offset; + vaddr = cc->adjust_watchpoint_address(cpu, vaddr, len); QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { if (cpu_watchpoint_address_matches(wp, vaddr, len) && (wp->flags & flags)) { diff --git a/include/qom/cpu.h b/include/qom/cpu.h index ca4d0fb1b4..45bcf21a21 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -132,6 +132,8 @@ struct TranslationBlock; * @cpu_exec_exit: Callback for cpu_exec cleanup. * @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec. * @disas_set_info: Setup architecture specific components of disassembly info + * @adjust_watchpoint_address: Perform a target-specific adjustment to an + * address before attempting to match it against watchpoints. * * Represents a CPU family or model. */ @@ -195,6 +197,7 @@ typedef struct CPUClass { bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); void (*disas_set_info)(CPUState *cpu, disassemble_info *info); + vaddr (*adjust_watchpoint_address)(CPUState *cpu, vaddr addr, int len); } CPUClass; #ifdef HOST_WORDS_BIGENDIAN diff --git a/qom/cpu.c b/qom/cpu.c index d57faf3ddc..0e19b1aa21 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -391,6 +391,11 @@ static int64_t cpu_common_get_arch_id(CPUState *cpu) return cpu->cpu_index; } +static vaddr cpu_adjust_watchpoint_address(CPUState *cpu, vaddr addr, int len) +{ + return addr; +} + static void cpu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -415,6 +420,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) k->cpu_exec_enter = cpu_common_noop; k->cpu_exec_exit = cpu_common_noop; k->cpu_exec_interrupt = cpu_common_exec_interrupt; + k->adjust_watchpoint_address = cpu_adjust_watchpoint_address; set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 81448caf18..4ee250cec6 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1675,6 +1675,9 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_stop_before_watchpoint = true; cc->debug_excp_handler = arm_debug_excp_handler; cc->debug_check_watchpoint = arm_debug_check_watchpoint; +#if !defined(CONFIG_USER_ONLY) + cc->adjust_watchpoint_address = arm_adjust_watchpoint_address; +#endif cc->disas_set_info = arm_disas_set_info; } diff --git a/target/arm/internals.h b/target/arm/internals.h index 2e65bc12fa..f742a419ff 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -444,6 +444,11 @@ void hw_breakpoint_update_all(ARMCPU *cpu); /* Callback function for checking if a watchpoint should trigger. */ bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); +/* Adjust addresses (in BE32 mode) before testing against watchpoint + * addresses. + */ +vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len); + /* Callback function for when a watchpoint or breakpoint triggers. */ void arm_debug_excp_handler(CPUState *cs); diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index ba796d898e..fb366fdc35 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -1225,6 +1225,28 @@ bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) return check_watchpoints(cpu); } +vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + /* In BE32 system mode, target memory is stored byteswapped (on a + * little-endian host system), and by the time we reach here (via an + * opcode helper) the addresses of subword accesses have been adjusted + * to account for that, which means that watchpoints will not match. + * Undo the adjustment here. + */ + if (arm_sctlr_b(env)) { + if (len == 1) { + addr ^= 3; + } else if (len == 2) { + addr ^= 2; + } + } + + return addr; +} + void arm_debug_excp_handler(CPUState *cs) { /* Called by core code when a watchpoint or breakpoint fires; From 63f26fcfda8e19f94ce23336726d14805250a5b6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Feb 2017 18:29:59 +0000 Subject: [PATCH 09/13] target/arm: Abstract out pbit/wbit tests in ARM ldr/str decode In the ARM ldr/str decode path, rather than directly testing "insn & (1 << 21)" and "insn & (1 << 24)", abstract these bits out into wbit and pbit local flags. (We will want to do more tests against them to determine whether we need to provide syndrome information.) Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias --- target/arm/translate.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index 493c627bcf..175b4c1cb7 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -8782,6 +8782,8 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } else { int address_offset; bool load = insn & (1 << 20); + bool wbit = insn & (1 << 21); + bool pbit = insn & (1 << 24); bool doubleword = false; /* Misc load/store */ rn = (insn >> 16) & 0xf; @@ -8799,8 +8801,9 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } addr = load_reg(s, rn); - if (insn & (1 << 24)) + if (pbit) { gen_add_datah_offset(s, insn, 0, addr); + } address_offset = 0; if (doubleword) { @@ -8849,10 +8852,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) ensure correct behavior with overlapping index registers. ldrd with base writeback is undefined if the destination and index registers overlap. */ - if (!(insn & (1 << 24))) { + if (!pbit) { gen_add_datah_offset(s, insn, address_offset, addr); store_reg(s, rn, addr); - } else if (insn & (1 << 21)) { + } else if (wbit) { if (address_offset) tcg_gen_addi_i32(addr, addr, address_offset); store_reg(s, rn, addr); From 9bb6558a218bf7e466e5ac1100639517d8a30d33 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Feb 2017 18:30:00 +0000 Subject: [PATCH 10/13] target/arm: A32, T32: Create Instruction Syndromes for Data Aborts Add support for generating the ISS (Instruction Specific Syndrome) for Data Abort exceptions taken from AArch32. These syndromes are used by hypervisors for example to trap and emulate memory accesses. This is the equivalent for AArch32 guests of the work done for AArch64 guests in commit aaa1f954d4cab243. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias --- target/arm/translate-a64.c | 14 --- target/arm/translate.c | 184 +++++++++++++++++++++++++++---------- target/arm/translate.h | 14 +++ 3 files changed, 149 insertions(+), 63 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index d0352e2045..e61bbd6b3b 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -379,20 +379,6 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) } } -static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) -{ - /* We don't need to save all of the syndrome so we mask and shift - * out uneeded bits to help the sleb128 encoder do a better job. - */ - syn &= ARM_INSN_START_WORD2_MASK; - syn >>= ARM_INSN_START_WORD2_SHIFT; - - /* We check and clear insn_start_idx to catch multiple updates. */ - assert(s->insn_start_idx != 0); - tcg_set_insn_param(s->insn_start_idx, 2, syn); - s->insn_start_idx = 0; -} - static void unallocated_encoding(DisasContext *s) { /* Unallocated and reserved encodings are uncategorized */ diff --git a/target/arm/translate.c b/target/arm/translate.c index 175b4c1cb7..4436d8f3a2 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -102,6 +102,49 @@ void arm_translate_init(void) a64_translate_init(); } +/* Flags for the disas_set_da_iss info argument: + * lower bits hold the Rt register number, higher bits are flags. + */ +typedef enum ISSInfo { + ISSNone = 0, + ISSRegMask = 0x1f, + ISSInvalid = (1 << 5), + ISSIsAcqRel = (1 << 6), + ISSIsWrite = (1 << 7), + ISSIs16Bit = (1 << 8), +} ISSInfo; + +/* Save the syndrome information for a Data Abort */ +static void disas_set_da_iss(DisasContext *s, TCGMemOp memop, ISSInfo issinfo) +{ + uint32_t syn; + int sas = memop & MO_SIZE; + bool sse = memop & MO_SIGN; + bool is_acqrel = issinfo & ISSIsAcqRel; + bool is_write = issinfo & ISSIsWrite; + bool is_16bit = issinfo & ISSIs16Bit; + int srt = issinfo & ISSRegMask; + + if (issinfo & ISSInvalid) { + /* Some callsites want to conditionally provide ISS info, + * eg "only if this was not a writeback" + */ + return; + } + + if (srt == 15) { + /* For AArch32, insns where the src/dest is R15 never generate + * ISS information. Catching that here saves checking at all + * the call sites. + */ + return; + } + + syn = syn_data_abort_with_iss(0, sas, sse, srt, 0, is_acqrel, + 0, 0, 0, is_write, 0, is_16bit); + disas_set_insn_syndrome(s, syn); +} + static inline ARMMMUIdx get_a32_user_mem_index(DisasContext *s) { /* Return the mmu_idx to use for A32/T32 "unprivileged load/store" @@ -933,6 +976,14 @@ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 a32, int index) \ { \ gen_aa32_ld_i32(s, val, a32, index, OPC | s->be_data); \ +} \ +static inline void gen_aa32_ld##SUFF##_iss(DisasContext *s, \ + TCGv_i32 val, \ + TCGv_i32 a32, int index, \ + ISSInfo issinfo) \ +{ \ + gen_aa32_ld##SUFF(s, val, a32, index); \ + disas_set_da_iss(s, OPC, issinfo); \ } #define DO_GEN_ST(SUFF, OPC) \ @@ -940,6 +991,14 @@ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 a32, int index) \ { \ gen_aa32_st_i32(s, val, a32, index, OPC | s->be_data); \ +} \ +static inline void gen_aa32_st##SUFF##_iss(DisasContext *s, \ + TCGv_i32 val, \ + TCGv_i32 a32, int index, \ + ISSInfo issinfo) \ +{ \ + gen_aa32_st##SUFF(s, val, a32, index); \ + disas_set_da_iss(s, OPC, issinfo | ISSIsWrite); \ } static inline void gen_aa32_frob64(DisasContext *s, TCGv_i64 val) @@ -8682,16 +8741,19 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); switch (op1) { case 0: /* lda */ - gen_aa32_ld32u(s, tmp, addr, - get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, + get_mem_index(s), + rd | ISSIsAcqRel); break; case 2: /* ldab */ - gen_aa32_ld8u(s, tmp, addr, - get_mem_index(s)); + gen_aa32_ld8u_iss(s, tmp, addr, + get_mem_index(s), + rd | ISSIsAcqRel); break; case 3: /* ldah */ - gen_aa32_ld16u(s, tmp, addr, - get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, + get_mem_index(s), + rd | ISSIsAcqRel); break; default: abort(); @@ -8702,16 +8764,19 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = load_reg(s, rm); switch (op1) { case 0: /* stl */ - gen_aa32_st32(s, tmp, addr, - get_mem_index(s)); + gen_aa32_st32_iss(s, tmp, addr, + get_mem_index(s), + rm | ISSIsAcqRel); break; case 2: /* stlb */ - gen_aa32_st8(s, tmp, addr, - get_mem_index(s)); + gen_aa32_st8_iss(s, tmp, addr, + get_mem_index(s), + rm | ISSIsAcqRel); break; case 3: /* stlh */ - gen_aa32_st16(s, tmp, addr, - get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, + get_mem_index(s), + rm | ISSIsAcqRel); break; default: abort(); @@ -8785,10 +8850,15 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) bool wbit = insn & (1 << 21); bool pbit = insn & (1 << 24); bool doubleword = false; + ISSInfo issinfo; + /* Misc load/store */ rn = (insn >> 16) & 0xf; rd = (insn >> 12) & 0xf; + /* ISS not valid if writeback */ + issinfo = (pbit & !wbit) ? rd : ISSInvalid; + if (!load && (sh & 2)) { /* doubleword */ ARCH(5TE); @@ -8832,20 +8902,23 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); switch (sh) { case 1: - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), + issinfo); break; case 2: - gen_aa32_ld8s(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8s_iss(s, tmp, addr, get_mem_index(s), + issinfo); break; default: case 3: - gen_aa32_ld16s(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16s_iss(s, tmp, addr, get_mem_index(s), + issinfo); break; } } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), issinfo); tcg_temp_free_i32(tmp); } /* Perform base writeback before the loaded value to @@ -9198,17 +9271,17 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) /* load */ tmp = tcg_temp_new_i32(); if (insn & (1 << 22)) { - gen_aa32_ld8u(s, tmp, tmp2, i); + gen_aa32_ld8u_iss(s, tmp, tmp2, i, rd); } else { - gen_aa32_ld32u(s, tmp, tmp2, i); + gen_aa32_ld32u_iss(s, tmp, tmp2, i, rd); } } else { /* store */ tmp = load_reg(s, rd); if (insn & (1 << 22)) { - gen_aa32_st8(s, tmp, tmp2, i); + gen_aa32_st8_iss(s, tmp, tmp2, i, rd); } else { - gen_aa32_st32(s, tmp, tmp2, i); + gen_aa32_st32_iss(s, tmp, tmp2, i, rd); } tcg_temp_free_i32(tmp); } @@ -9669,13 +9742,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); switch (op) { case 0: /* ldab */ - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; case 1: /* ldah */ - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; case 2: /* lda */ - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; default: abort(); @@ -9685,13 +9761,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: /* stlb */ - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); + gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; case 1: /* stlh */ - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; case 2: /* stl */ - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), + rs | ISSIsAcqRel); break; default: abort(); @@ -10637,6 +10716,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw int postinc = 0; int writeback = 0; int memidx; + ISSInfo issinfo; + if ((insn & 0x01100000) == 0x01000000) { if (disas_neon_ls_insn(s, insn)) { goto illegal_op; @@ -10740,24 +10821,27 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } } } + + issinfo = writeback ? ISSInvalid : rs; + if (insn & (1 << 20)) { /* Load. */ tmp = tcg_temp_new_i32(); switch (op) { case 0: - gen_aa32_ld8u(s, tmp, addr, memidx); + gen_aa32_ld8u_iss(s, tmp, addr, memidx, issinfo); break; case 4: - gen_aa32_ld8s(s, tmp, addr, memidx); + gen_aa32_ld8s_iss(s, tmp, addr, memidx, issinfo); break; case 1: - gen_aa32_ld16u(s, tmp, addr, memidx); + gen_aa32_ld16u_iss(s, tmp, addr, memidx, issinfo); break; case 5: - gen_aa32_ld16s(s, tmp, addr, memidx); + gen_aa32_ld16s_iss(s, tmp, addr, memidx, issinfo); break; case 2: - gen_aa32_ld32u(s, tmp, addr, memidx); + gen_aa32_ld32u_iss(s, tmp, addr, memidx, issinfo); break; default: tcg_temp_free_i32(tmp); @@ -10774,13 +10858,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: - gen_aa32_st8(s, tmp, addr, memidx); + gen_aa32_st8_iss(s, tmp, addr, memidx, issinfo); break; case 1: - gen_aa32_st16(s, tmp, addr, memidx); + gen_aa32_st16_iss(s, tmp, addr, memidx, issinfo); break; case 2: - gen_aa32_st32(s, tmp, addr, memidx); + gen_aa32_st32_iss(s, tmp, addr, memidx, issinfo); break; default: tcg_temp_free_i32(tmp); @@ -10917,7 +11001,8 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, val); tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), + rd | ISSIs16Bit); tcg_temp_free_i32(addr); store_reg(s, rd, tmp); break; @@ -11120,28 +11205,28 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) switch (op) { case 0: /* str */ - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 1: /* strh */ - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 2: /* strb */ - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); + gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 3: /* ldrsb */ - gen_aa32_ld8s(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8s_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 4: /* ldr */ - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 5: /* ldrh */ - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 6: /* ldrb */ - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 7: /* ldrsh */ - gen_aa32_ld16s(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16s_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; } if (op >= 3) { /* load */ @@ -11185,12 +11270,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); + gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -11207,12 +11292,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -11228,12 +11313,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -11715,6 +11800,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) store_cpu_field(tmp, condexec_bits); } do { + dc->insn_start_idx = tcg_op_buf_count(); tcg_gen_insn_start(dc->pc, (dc->condexec_cond << 4) | (dc->condexec_mask >> 1), 0); diff --git a/target/arm/translate.h b/target/arm/translate.h index 285e96f087..abb0760158 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -104,6 +104,20 @@ static inline int default_exception_el(DisasContext *s) ? 3 : MAX(1, s->current_el); } +static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) +{ + /* We don't need to save all of the syndrome so we mask and shift + * out unneeded bits to help the sleb128 encoder do a better job. + */ + syn &= ARM_INSN_START_WORD2_MASK; + syn >>= ARM_INSN_START_WORD2_SHIFT; + + /* We check and clear insn_start_idx to catch multiple updates. */ + assert(s->insn_start_idx != 0); + tcg_set_insn_param(s->insn_start_idx, 2, syn); + s->insn_start_idx = 0; +} + /* target-specific extra values for is_jmp */ /* These instructions trap after executing, so the A32/T32 decoder must * defer them until after the conditional execution state has been updated. From 394c8bbfb7aeaa788c2bc99ca972d359847a6459 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Feb 2017 18:30:00 +0000 Subject: [PATCH 11/13] stellaris: Document memory map and which SoC devices are unimplemented Add a comment documenting the memory map of the SoC devices and which are not implemented. Signed-off-by: Peter Maydell Message-id: 1484247815-15279-2-git-send-email-peter.maydell@linaro.org --- hw/arm/stellaris.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 794a3ada71..5c4b707998 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1220,6 +1220,40 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, 0x40024000, 0x40025000, 0x40026000}; static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; + /* Memory map of SoC devices, from + * Stellaris LM3S6965 Microcontroller Data Sheet (rev I) + * http://www.ti.com/lit/ds/symlink/lm3s6965.pdf + * + * 40000000 wdtimer (unimplemented) + * 40002000 i2c (unimplemented) + * 40004000 GPIO + * 40005000 GPIO + * 40006000 GPIO + * 40007000 GPIO + * 40008000 SSI + * 4000c000 UART + * 4000d000 UART + * 4000e000 UART + * 40020000 i2c + * 40021000 i2c (unimplemented) + * 40024000 GPIO + * 40025000 GPIO + * 40026000 GPIO + * 40028000 PWM (unimplemented) + * 4002c000 QEI (unimplemented) + * 4002d000 QEI (unimplemented) + * 40030000 gptimer + * 40031000 gptimer + * 40032000 gptimer + * 40033000 gptimer + * 40038000 ADC + * 4003c000 analogue comparator (unimplemented) + * 40048000 ethernet + * 400fc000 hibernation module (unimplemented) + * 400fd000 flash memory control (unimplemented) + * 400fe000 system control + */ + DeviceState *gpio_dev[7], *nvic; qemu_irq gpio_in[7][8]; qemu_irq gpio_out[7][8]; From f5095aa3805f8cf91c4df6e7e1dab0d914bc40bc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Feb 2017 18:30:00 +0000 Subject: [PATCH 12/13] hw/misc: New "unimplemented" sysbus device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a new "unimplemented" sysbus device, which simply accepts all read and write accesses, and implements them as read-as-zero, write-ignored, with logging of the access as LOG_UNIMP. This is useful for stubbing out bits of an SoC or board model which haven't been written yet. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1484247815-15279-3-git-send-email-peter.maydell@linaro.org --- hw/misc/Makefile.objs | 2 + hw/misc/unimp.c | 107 ++++++++++++++++++++++++++++++++++++++++ include/hw/misc/unimp.h | 39 +++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 hw/misc/unimp.c create mode 100644 include/hw/misc/unimp.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 1a89615a62..898e4ccfb1 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -6,6 +6,8 @@ common-obj-$(CONFIG_SGA) += sga.o common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o +common-obj-y += unimp.o + obj-$(CONFIG_VMPORT) += vmport.o # ARM devices diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c new file mode 100644 index 0000000000..bcbb585888 --- /dev/null +++ b/hw/misc/unimp.c @@ -0,0 +1,107 @@ +/* "Unimplemented" device + * + * This is a dummy device which accepts and logs all accesses. + * It's useful for stubbing out regions of an SoC or board + * map which correspond to devices that have not yet been + * implemented. This is often sufficient to placate initial + * guest device driver probing such that the system will + * come up. + * + * Copyright Linaro Limited, 2017 + * Written by Peter Maydell + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/misc/unimp.h" +#include "qemu/log.h" +#include "qapi/error.h" + +#define UNIMPLEMENTED_DEVICE(obj) \ + OBJECT_CHECK(UnimplementedDeviceState, (obj), TYPE_UNIMPLEMENTED_DEVICE) + +typedef struct { + SysBusDevice parent_obj; + MemoryRegion iomem; + char *name; + uint64_t size; +} UnimplementedDeviceState; + +static uint64_t unimp_read(void *opaque, hwaddr offset, unsigned size) +{ + UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(opaque); + + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + s->name, size, offset); + return 0; +} + +static void unimp_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(opaque); + + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + s->name, size, value, offset); +} + +static const MemoryRegionOps unimp_ops = { + .read = unimp_read, + .write = unimp_write, + .impl.min_access_size = 1, + .impl.max_access_size = 8, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void unimp_realize(DeviceState *dev, Error **errp) +{ + UnimplementedDeviceState *s = UNIMPLEMENTED_DEVICE(dev); + + if (s->size == 0) { + error_setg(errp, "property 'size' not specified or zero"); + return; + } + + if (s->name == NULL) { + error_setg(errp, "property 'name' not specified"); + return; + } + + memory_region_init_io(&s->iomem, OBJECT(s), &unimp_ops, s, + s->name, s->size); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static Property unimp_properties[] = { + DEFINE_PROP_UINT64("size", UnimplementedDeviceState, size, 0), + DEFINE_PROP_STRING("name", UnimplementedDeviceState, name), + DEFINE_PROP_END_OF_LIST(), +}; + +static void unimp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = unimp_realize; + dc->props = unimp_properties; +} + +static const TypeInfo unimp_info = { + .name = TYPE_UNIMPLEMENTED_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(UnimplementedDeviceState), + .class_init = unimp_class_init, +}; + +static void unimp_register_types(void) +{ + type_register_static(&unimp_info); +} + +type_init(unimp_register_types) diff --git a/include/hw/misc/unimp.h b/include/hw/misc/unimp.h new file mode 100644 index 0000000000..3462d85836 --- /dev/null +++ b/include/hw/misc/unimp.h @@ -0,0 +1,39 @@ +/* + * "Unimplemented" device + * + * Copyright Linaro Limited, 2017 + * Written by Peter Maydell + */ + +#ifndef HW_MISC_UNIMP_H +#define HW_MISC_UNIMP_H + +#define TYPE_UNIMPLEMENTED_DEVICE "unimplemented-device" + +/** + * create_unimplemented_device: create and map a dummy device + * @name: name of the device for debug logging + * @base: base address of the device's MMIO region + * @size: size of the device's MMIO region + * + * This utility function creates and maps an instance of unimplemented-device, + * which is a dummy device which simply logs all guest accesses to + * it via the qemu_log LOG_UNIMP debug log. + * The device is mapped at priority -1000, which means that you can + * use it to cover a large region and then map other devices on top of it + * if necessary. + */ +static inline void create_unimplemented_device(const char *name, + hwaddr base, + hwaddr size) +{ + DeviceState *dev = qdev_create(NULL, TYPE_UNIMPLEMENTED_DEVICE); + + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint64(dev, "size", size); + qdev_init_nofail(dev); + + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, base, -1000); +} + +#endif From aecfbbc97a2e52bbee34a53c32f961a182046a95 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Feb 2017 18:30:00 +0000 Subject: [PATCH 13/13] stellaris: Use the 'unimplemented' device for parts we don't implement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the 'unimplemented' dummy device to cover regions of the SoC device memory map which we don't have proper device implementations for yet. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 1484247815-15279-4-git-send-email-peter.maydell@linaro.org --- hw/arm/stellaris.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 5c4b707998..9edcd49740 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -21,6 +21,7 @@ #include "exec/address-spaces.h" #include "sysemu/sysemu.h" #include "hw/char/pl011.h" +#include "hw/misc/unimp.h" #define GPIO_A 0 #define GPIO_B 1 @@ -1404,6 +1405,19 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, } } } + + /* Add dummy regions for the devices we don't implement yet, + * so guest accesses don't cause unlogged crashes. + */ + create_unimplemented_device("wdtimer", 0x40000000, 0x1000); + create_unimplemented_device("i2c-0", 0x40002000, 0x1000); + create_unimplemented_device("i2c-2", 0x40021000, 0x1000); + create_unimplemented_device("PWM", 0x40028000, 0x1000); + create_unimplemented_device("QEI-0", 0x4002c000, 0x1000); + create_unimplemented_device("QEI-1", 0x4002d000, 0x1000); + create_unimplemented_device("analogue-comparator", 0x4003c000, 0x1000); + create_unimplemented_device("hibernation", 0x400fc000, 0x1000); + create_unimplemented_device("flash-control", 0x400fd000, 0x1000); } /* FIXME: Figure out how to generate these from stellaris_boards. */