target-arm:

* new "unimplemented" device for stubbing out devices in a
    system model so accesses can be logged
  * stellaris: document the SoC memory map
  * arm: create instruction syndromes for AArch32 data aborts
  * arm: Correctly handle watchpoints for BE32 CPUs
  * Fix Thumb-1 BE32 execution and disassembly
  * arm: Add cfgend parameter for ARM CPU selection
  * sd: sdhci: check data length during dma_memory_read
  * aspeed: add a watchdog controller
  * integratorcp: adding vmstate for save/restore
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJYmh3zAAoJEDwlJe0UNgzeZF0P/0v1AomIyNLaJ8Xyr6/9Ia9L
 xn8jYsACWyT3AQ4BXdmaGXg24+wPVT4X36wzTNS50s4a3R4VWxaqu+9bxAKVtyfH
 Y1CIXIib5Gz9FyY1n5pvpj7b438h4QtNbgvb/0vJoECk2F99QjEQxX+XJlCGGAGi
 J8O/L2cfrzZUXLfRZQW1VfcIkgLvlGo1A489/MFYQuv+irzJckHmnY3LeqrRfKCB
 udYbaaAdtCiPQ/OlCzsDXucjOXBMqrGba+f3g+P4xmtDPYTlSvvOjFroR54v9xa8
 n8qLLelEiH8uhW3vl8aeEsdPMV3yrX2yz0HzMqhzXTajJJs8wxECtvYKWaSzosKP
 64pbSlHD8iwl1oHJ+ZOlwaCpAcaVnBLwz65VSB6EUxAGPpeFqp0q6uATc7t9dO3o
 PRAijt5IrNpKHehTmcZ1bAsO59KeCBFwMyGL8clX2jD1Vjj8zD9CrkCDL4tpJlsL
 uGHn9RyywsWcMGsjpP6JXo3uYMK1Wbozt0XRnWWex+wsW3qOdCHVGIhXyyDJBLMD
 +r+hc49//GA58GtdTDVsU/qI4NgLfVSp2qk4lKlYVu1kxp5azPs2OpRn44Hv6YID
 2VmepX4ug/pfJ0y2AHyYuJEO+auHKzunjDCrnrBqQEMrON2hmvqtHnS/G496+lIG
 146ctV+2sSPz2vxOvTwf
 =dbln
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170207-1' into staging

target-arm:
 * new "unimplemented" device for stubbing out devices in a
   system model so accesses can be logged
 * stellaris: document the SoC memory map
 * arm: create instruction syndromes for AArch32 data aborts
 * arm: Correctly handle watchpoints for BE32 CPUs
 * Fix Thumb-1 BE32 execution and disassembly
 * arm: Add cfgend parameter for ARM CPU selection
 * sd: sdhci: check data length during dma_memory_read
 * aspeed: add a watchdog controller
 * integratorcp: adding vmstate for save/restore

# gpg: Signature made Tue 07 Feb 2017 19:20:19 GMT
# gpg:                using RSA key 0x3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20170207-1:
  stellaris: Use the 'unimplemented' device for parts we don't implement
  hw/misc: New "unimplemented" sysbus device
  stellaris: Document memory map and which SoC devices are unimplemented
  target/arm: A32, T32: Create Instruction Syndromes for Data Aborts
  target/arm: Abstract out pbit/wbit tests in ARM ldr/str decode
  arm: Correctly handle watchpoints for BE32 CPUs
  Fix Thumb-1 BE32 execution and disassembly.
  target/arm: Add cfgend parameter for ARM CPU selection.
  hw/arm/integratorcp: Support specifying features via -cpu
  sd: sdhci: check data length during dma_memory_read
  aspeed: add a watchdog controller
  wdt: Add Aspeed watchdog device model
  integratorcp: adding vmstate for save/restore

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-02-07 19:21:30 +00:00
commit f073cd3a2b
24 changed files with 801 additions and 70 deletions

View File

@ -190,6 +190,7 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code,
s.cpu = cpu; s.cpu = cpu;
s.info.read_memory_func = target_read_memory; s.info.read_memory_func = target_read_memory;
s.info.read_memory_inner_func = NULL;
s.info.buffer_vma = code; s.info.buffer_vma = code;
s.info.buffer_length = size; s.info.buffer_length = size;
s.info.print_address_func = generic_print_address; s.info.print_address_func = generic_print_address;

1
exec.c
View File

@ -2115,6 +2115,7 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags)
return; return;
} }
vaddr = (cpu->mem_io_vaddr & TARGET_PAGE_MASK) + offset; vaddr = (cpu->mem_io_vaddr & TARGET_PAGE_MASK) + offset;
vaddr = cc->adjust_watchpoint_address(cpu, vaddr, len);
QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
if (cpu_watchpoint_address_matches(wp, vaddr, len) if (cpu_watchpoint_address_matches(wp, vaddr, len)
&& (wp->flags & flags)) { && (wp->flags & flags)) {

View File

@ -31,6 +31,7 @@
#define ASPEED_SOC_SCU_BASE 0x1E6E2000 #define ASPEED_SOC_SCU_BASE 0x1E6E2000
#define ASPEED_SOC_SRAM_BASE 0x1E720000 #define ASPEED_SOC_SRAM_BASE 0x1E720000
#define ASPEED_SOC_TIMER_BASE 0x1E782000 #define ASPEED_SOC_TIMER_BASE 0x1E782000
#define ASPEED_SOC_WDT_BASE 0x1E785000
#define ASPEED_SOC_I2C_BASE 0x1E78A000 #define ASPEED_SOC_I2C_BASE 0x1E78A000
static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; 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); sc->info->silicon_rev);
object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc),
"ram-size", &error_abort); "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) static void aspeed_soc_realize(DeviceState *dev, Error **errp)
@ -286,6 +291,14 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
return; return;
} }
sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, ASPEED_SOC_SDMC_BASE); 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) static void aspeed_soc_class_init(ObjectClass *oc, void *data)

View File

@ -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 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, static uint64_t integratorcm_read(void *opaque, hwaddr offset,
unsigned size) unsigned size)
{ {
@ -309,6 +329,18 @@ typedef struct icp_pic_state {
qemu_irq parent_fiq; qemu_irq parent_fiq;
} icp_pic_state; } 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) static void icp_pic_update(icp_pic_state *s)
{ {
uint32_t flags; uint32_t flags;
@ -438,6 +470,16 @@ typedef struct ICPCtrlRegsState {
#define ICP_INTREG_WPROT (1 << 0) #define ICP_INTREG_WPROT (1 << 0)
#define ICP_INTREG_CARDIN (1 << 3) #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, static uint64_t icp_control_read(void *opaque, hwaddr offset,
unsigned size) unsigned size)
{ {
@ -535,27 +577,42 @@ static void integratorcp_init(MachineState *machine)
const char *kernel_filename = machine->kernel_filename; const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline; const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename; const char *initrd_filename = machine->initrd_filename;
char **cpustr;
ObjectClass *cpu_oc; ObjectClass *cpu_oc;
CPUClass *cc;
Object *cpuobj; Object *cpuobj;
ARMCPU *cpu; ARMCPU *cpu;
const char *typename;
MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *ram_alias = g_new(MemoryRegion, 1); MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
qemu_irq pic[32]; qemu_irq pic[32];
DeviceState *dev, *sic, *icp; DeviceState *dev, *sic, *icp;
int i; int i;
Error *err = NULL;
if (!cpu_model) { if (!cpu_model) {
cpu_model = "arm926"; 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) { if (!cpu_oc) {
fprintf(stderr, "Unable to find CPU definition\n"); fprintf(stderr, "Unable to find CPU definition\n");
exit(1); 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 /* By default ARM1176 CPUs have EL3 enabled. This board does not
* currently support EL3 so the CPU EL3 property is disabled before * currently support EL3 so the CPU EL3 property is disabled before
@ -640,6 +697,21 @@ static void core_class_init(ObjectClass *klass, void *data)
dc->props = core_properties; dc->props = core_properties;
dc->realize = integratorcm_realize; 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 = { static const TypeInfo core_info = {
@ -655,6 +727,7 @@ static const TypeInfo icp_pic_info = {
.parent = TYPE_SYS_BUS_DEVICE, .parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(icp_pic_state), .instance_size = sizeof(icp_pic_state),
.instance_init = icp_pic_init, .instance_init = icp_pic_init,
.class_init = icp_pic_class_init,
}; };
static const TypeInfo icp_ctrl_regs_info = { static const TypeInfo icp_ctrl_regs_info = {
@ -662,6 +735,7 @@ static const TypeInfo icp_ctrl_regs_info = {
.parent = TYPE_SYS_BUS_DEVICE, .parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ICPCtrlRegsState), .instance_size = sizeof(ICPCtrlRegsState),
.instance_init = icp_control_init, .instance_init = icp_control_init,
.class_init = icp_control_class_init,
}; };
static void integratorcp_register_types(void) static void integratorcp_register_types(void)

View File

@ -21,6 +21,7 @@
#include "exec/address-spaces.h" #include "exec/address-spaces.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "hw/char/pl011.h" #include "hw/char/pl011.h"
#include "hw/misc/unimp.h"
#define GPIO_A 0 #define GPIO_A 0
#define GPIO_B 1 #define GPIO_B 1
@ -1220,6 +1221,40 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
0x40024000, 0x40025000, 0x40026000}; 0x40024000, 0x40025000, 0x40026000};
static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; 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; DeviceState *gpio_dev[7], *nvic;
qemu_irq gpio_in[7][8]; qemu_irq gpio_in[7][8];
qemu_irq gpio_out[7][8]; qemu_irq gpio_out[7][8];
@ -1370,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. */ /* FIXME: Figure out how to generate these from stellaris_boards. */

View File

@ -6,6 +6,8 @@ common-obj-$(CONFIG_SGA) += sga.o
common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o
common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o
common-obj-y += unimp.o
obj-$(CONFIG_VMPORT) += vmport.o obj-$(CONFIG_VMPORT) += vmport.o
# ARM devices # ARM devices

107
hw/misc/unimp.c Normal file
View File

@ -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)

View File

@ -536,7 +536,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
boundary_count -= block_size - begin; boundary_count -= block_size - begin;
} }
dma_memory_read(&address_space_memory, s->sdmasysad, 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; s->sdmasysad += s->data_count - begin;
if (s->data_count == block_size) { if (s->data_count == block_size) {
for (n = 0; n < block_size; n++) { for (n = 0; n < block_size; n++) {

View File

@ -2,3 +2,4 @@ common-obj-y += watchdog.o
common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o
common-obj-$(CONFIG_ASPEED_SOC) += wdt_aspeed.o

225
hw/watchdog/wdt_aspeed.c Normal file
View File

@ -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)

View File

@ -295,6 +295,7 @@ typedef struct disassemble_info {
The bottom 16 bits are for the internal use of the disassembler. */ The bottom 16 bits are for the internal use of the disassembler. */
unsigned long flags; unsigned long flags;
#define INSN_HAS_RELOC 0x80000000 #define INSN_HAS_RELOC 0x80000000
#define INSN_ARM_BE32 0x00010000
PTR private_data; PTR private_data;
/* Function used to get bytes to disassemble. MEMADDR is the /* 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, (bfd_vma memaddr, bfd_byte *myaddr, int length,
struct disassemble_info *info); 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 /* 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 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 MEMADDR is the address that we were trying to read. INFO is a

View File

@ -19,6 +19,7 @@
#include "hw/timer/aspeed_timer.h" #include "hw/timer/aspeed_timer.h"
#include "hw/i2c/aspeed_i2c.h" #include "hw/i2c/aspeed_i2c.h"
#include "hw/ssi/aspeed_smc.h" #include "hw/ssi/aspeed_smc.h"
#include "hw/watchdog/wdt_aspeed.h"
#define ASPEED_SPIS_NUM 2 #define ASPEED_SPIS_NUM 2
@ -37,6 +38,7 @@ typedef struct AspeedSoCState {
AspeedSMCState fmc; AspeedSMCState fmc;
AspeedSMCState spi[ASPEED_SPIS_NUM]; AspeedSMCState spi[ASPEED_SPIS_NUM];
AspeedSDMCState sdmc; AspeedSDMCState sdmc;
AspeedWDTState wdt;
} AspeedSoCState; } AspeedSoCState;
#define TYPE_ASPEED_SOC "aspeed-soc" #define TYPE_ASPEED_SOC "aspeed-soc"

39
include/hw/misc/unimp.h Normal file
View File

@ -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

View File

@ -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 */

View File

@ -132,6 +132,8 @@ struct TranslationBlock;
* @cpu_exec_exit: Callback for cpu_exec cleanup. * @cpu_exec_exit: Callback for cpu_exec cleanup.
* @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec. * @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec.
* @disas_set_info: Setup architecture specific components of disassembly info * @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. * Represents a CPU family or model.
*/ */
@ -195,6 +197,7 @@ typedef struct CPUClass {
bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request);
void (*disas_set_info)(CPUState *cpu, disassemble_info *info); void (*disas_set_info)(CPUState *cpu, disassemble_info *info);
vaddr (*adjust_watchpoint_address)(CPUState *cpu, vaddr addr, int len);
} CPUClass; } CPUClass;
#ifdef HOST_WORDS_BIGENDIAN #ifdef HOST_WORDS_BIGENDIAN

View File

@ -391,6 +391,11 @@ static int64_t cpu_common_get_arch_id(CPUState *cpu)
return cpu->cpu_index; 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) static void cpu_class_init(ObjectClass *klass, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(klass); 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_enter = cpu_common_noop;
k->cpu_exec_exit = cpu_common_noop; k->cpu_exec_exit = cpu_common_noop;
k->cpu_exec_interrupt = cpu_common_exec_interrupt; k->cpu_exec_interrupt = cpu_common_exec_interrupt;
k->adjust_watchpoint_address = cpu_adjust_watchpoint_address;
set_bit(DEVICE_CATEGORY_CPU, dc->categories); set_bit(DEVICE_CATEGORY_CPU, dc->categories);
dc->realize = cpu_common_realizefn; dc->realize = cpu_common_realizefn;
dc->unrealize = cpu_common_unrealizefn; dc->unrealize = cpu_common_unrealizefn;

View File

@ -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, static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr,
bool sctlr_b) 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)) { if (bswap_code(sctlr_b)) {
return bswap16(insn); return bswap16(insn);
} }

View File

@ -446,6 +446,21 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info)
return print_insn_arm(pc | 1, 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) static void arm_disas_set_info(CPUState *cpu, disassemble_info *info)
{ {
ARMCPU *ac = ARM_CPU(cpu); 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; info->endian = BFD_ENDIAN_BIG;
#endif #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) static void arm_cpu_initfn(Object *obj)
@ -541,6 +564,9 @@ static Property arm_cpu_has_el2_property =
static Property arm_cpu_has_el3_property = static Property arm_cpu_has_el3_property =
DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); 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 */ /* use property name "pmu" to match other archs and virt tools */
static Property arm_cpu_has_pmu_property = static Property arm_cpu_has_pmu_property =
DEFINE_PROP_BOOL("pmu", ARMCPU, has_pmu, true); DEFINE_PROP_BOOL("pmu", ARMCPU, has_pmu, true);
@ -608,6 +634,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) static void arm_cpu_finalizefn(Object *obj)
@ -728,6 +756,14 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
cpu->reset_sctlr |= (1 << 13); 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 (!cpu->has_el3) {
/* If the has_el3 CPU property is disabled then we need to disable the /* If the has_el3 CPU property is disabled then we need to disable the
* feature. * feature.
@ -1639,6 +1675,9 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
cc->gdb_stop_before_watchpoint = true; cc->gdb_stop_before_watchpoint = true;
cc->debug_excp_handler = arm_debug_excp_handler; cc->debug_excp_handler = arm_debug_excp_handler;
cc->debug_check_watchpoint = arm_debug_check_watchpoint; 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; cc->disas_set_info = arm_disas_set_info;
} }

View File

@ -676,6 +676,13 @@ struct ARMCPU {
int gic_vpribits; /* number of virtual priority bits */ int gic_vpribits; /* number of virtual priority bits */
int gic_vprebits; /* number of virtual preemption 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; ARMELChangeHook *el_change_hook;
void *el_change_hook_opaque; void *el_change_hook_opaque;
}; };

View File

@ -444,6 +444,11 @@ void hw_breakpoint_update_all(ARMCPU *cpu);
/* Callback function for checking if a watchpoint should trigger. */ /* Callback function for checking if a watchpoint should trigger. */
bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); 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. */ /* Callback function for when a watchpoint or breakpoint triggers. */
void arm_debug_excp_handler(CPUState *cs); void arm_debug_excp_handler(CPUState *cs);

View File

@ -1225,6 +1225,28 @@ bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp)
return check_watchpoints(cpu); 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) void arm_debug_excp_handler(CPUState *cs)
{ {
/* Called by core code when a watchpoint or breakpoint fires; /* Called by core code when a watchpoint or breakpoint fires;

View File

@ -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) static void unallocated_encoding(DisasContext *s)
{ {
/* Unallocated and reserved encodings are uncategorized */ /* Unallocated and reserved encodings are uncategorized */

View File

@ -102,6 +102,49 @@ void arm_translate_init(void)
a64_translate_init(); 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) static inline ARMMMUIdx get_a32_user_mem_index(DisasContext *s)
{ {
/* Return the mmu_idx to use for A32/T32 "unprivileged load/store" /* 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) \ TCGv_i32 a32, int index) \
{ \ { \
gen_aa32_ld_i32(s, val, a32, index, OPC | s->be_data); \ 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) \ #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) \ TCGv_i32 a32, int index) \
{ \ { \
gen_aa32_st_i32(s, val, a32, index, OPC | s->be_data); \ 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) 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(); tmp = tcg_temp_new_i32();
switch (op1) { switch (op1) {
case 0: /* lda */ case 0: /* lda */
gen_aa32_ld32u(s, tmp, addr, gen_aa32_ld32u_iss(s, tmp, addr,
get_mem_index(s)); get_mem_index(s),
rd | ISSIsAcqRel);
break; break;
case 2: /* ldab */ case 2: /* ldab */
gen_aa32_ld8u(s, tmp, addr, gen_aa32_ld8u_iss(s, tmp, addr,
get_mem_index(s)); get_mem_index(s),
rd | ISSIsAcqRel);
break; break;
case 3: /* ldah */ case 3: /* ldah */
gen_aa32_ld16u(s, tmp, addr, gen_aa32_ld16u_iss(s, tmp, addr,
get_mem_index(s)); get_mem_index(s),
rd | ISSIsAcqRel);
break; break;
default: default:
abort(); abort();
@ -8702,16 +8764,19 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
tmp = load_reg(s, rm); tmp = load_reg(s, rm);
switch (op1) { switch (op1) {
case 0: /* stl */ case 0: /* stl */
gen_aa32_st32(s, tmp, addr, gen_aa32_st32_iss(s, tmp, addr,
get_mem_index(s)); get_mem_index(s),
rm | ISSIsAcqRel);
break; break;
case 2: /* stlb */ case 2: /* stlb */
gen_aa32_st8(s, tmp, addr, gen_aa32_st8_iss(s, tmp, addr,
get_mem_index(s)); get_mem_index(s),
rm | ISSIsAcqRel);
break; break;
case 3: /* stlh */ case 3: /* stlh */
gen_aa32_st16(s, tmp, addr, gen_aa32_st16_iss(s, tmp, addr,
get_mem_index(s)); get_mem_index(s),
rm | ISSIsAcqRel);
break; break;
default: default:
abort(); abort();
@ -8782,11 +8847,18 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
} else { } else {
int address_offset; int address_offset;
bool load = insn & (1 << 20); bool load = insn & (1 << 20);
bool wbit = insn & (1 << 21);
bool pbit = insn & (1 << 24);
bool doubleword = false; bool doubleword = false;
ISSInfo issinfo;
/* Misc load/store */ /* Misc load/store */
rn = (insn >> 16) & 0xf; rn = (insn >> 16) & 0xf;
rd = (insn >> 12) & 0xf; rd = (insn >> 12) & 0xf;
/* ISS not valid if writeback */
issinfo = (pbit & !wbit) ? rd : ISSInvalid;
if (!load && (sh & 2)) { if (!load && (sh & 2)) {
/* doubleword */ /* doubleword */
ARCH(5TE); ARCH(5TE);
@ -8799,8 +8871,9 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
} }
addr = load_reg(s, rn); addr = load_reg(s, rn);
if (insn & (1 << 24)) if (pbit) {
gen_add_datah_offset(s, insn, 0, addr); gen_add_datah_offset(s, insn, 0, addr);
}
address_offset = 0; address_offset = 0;
if (doubleword) { if (doubleword) {
@ -8829,30 +8902,33 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
tmp = tcg_temp_new_i32(); tmp = tcg_temp_new_i32();
switch (sh) { switch (sh) {
case 1: 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; break;
case 2: 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; break;
default: default:
case 3: 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; break;
} }
} else { } else {
/* store */ /* store */
tmp = load_reg(s, rd); 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); tcg_temp_free_i32(tmp);
} }
/* Perform base writeback before the loaded value to /* Perform base writeback before the loaded value to
ensure correct behavior with overlapping index registers. ensure correct behavior with overlapping index registers.
ldrd with base writeback is undefined if the ldrd with base writeback is undefined if the
destination and index registers overlap. */ destination and index registers overlap. */
if (!(insn & (1 << 24))) { if (!pbit) {
gen_add_datah_offset(s, insn, address_offset, addr); gen_add_datah_offset(s, insn, address_offset, addr);
store_reg(s, rn, addr); store_reg(s, rn, addr);
} else if (insn & (1 << 21)) { } else if (wbit) {
if (address_offset) if (address_offset)
tcg_gen_addi_i32(addr, addr, address_offset); tcg_gen_addi_i32(addr, addr, address_offset);
store_reg(s, rn, addr); store_reg(s, rn, addr);
@ -9195,17 +9271,17 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
/* load */ /* load */
tmp = tcg_temp_new_i32(); tmp = tcg_temp_new_i32();
if (insn & (1 << 22)) { if (insn & (1 << 22)) {
gen_aa32_ld8u(s, tmp, tmp2, i); gen_aa32_ld8u_iss(s, tmp, tmp2, i, rd);
} else { } else {
gen_aa32_ld32u(s, tmp, tmp2, i); gen_aa32_ld32u_iss(s, tmp, tmp2, i, rd);
} }
} else { } else {
/* store */ /* store */
tmp = load_reg(s, rd); tmp = load_reg(s, rd);
if (insn & (1 << 22)) { if (insn & (1 << 22)) {
gen_aa32_st8(s, tmp, tmp2, i); gen_aa32_st8_iss(s, tmp, tmp2, i, rd);
} else { } else {
gen_aa32_st32(s, tmp, tmp2, i); gen_aa32_st32_iss(s, tmp, tmp2, i, rd);
} }
tcg_temp_free_i32(tmp); tcg_temp_free_i32(tmp);
} }
@ -9666,13 +9742,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
tmp = tcg_temp_new_i32(); tmp = tcg_temp_new_i32();
switch (op) { switch (op) {
case 0: /* ldab */ 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; break;
case 1: /* ldah */ 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; break;
case 2: /* lda */ 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; break;
default: default:
abort(); abort();
@ -9682,13 +9761,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
tmp = load_reg(s, rs); tmp = load_reg(s, rs);
switch (op) { switch (op) {
case 0: /* stlb */ 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; break;
case 1: /* stlh */ 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; break;
case 2: /* stl */ 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; break;
default: default:
abort(); abort();
@ -10634,6 +10716,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
int postinc = 0; int postinc = 0;
int writeback = 0; int writeback = 0;
int memidx; int memidx;
ISSInfo issinfo;
if ((insn & 0x01100000) == 0x01000000) { if ((insn & 0x01100000) == 0x01000000) {
if (disas_neon_ls_insn(s, insn)) { if (disas_neon_ls_insn(s, insn)) {
goto illegal_op; goto illegal_op;
@ -10737,24 +10821,27 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
} }
} }
} }
issinfo = writeback ? ISSInvalid : rs;
if (insn & (1 << 20)) { if (insn & (1 << 20)) {
/* Load. */ /* Load. */
tmp = tcg_temp_new_i32(); tmp = tcg_temp_new_i32();
switch (op) { switch (op) {
case 0: case 0:
gen_aa32_ld8u(s, tmp, addr, memidx); gen_aa32_ld8u_iss(s, tmp, addr, memidx, issinfo);
break; break;
case 4: case 4:
gen_aa32_ld8s(s, tmp, addr, memidx); gen_aa32_ld8s_iss(s, tmp, addr, memidx, issinfo);
break; break;
case 1: case 1:
gen_aa32_ld16u(s, tmp, addr, memidx); gen_aa32_ld16u_iss(s, tmp, addr, memidx, issinfo);
break; break;
case 5: case 5:
gen_aa32_ld16s(s, tmp, addr, memidx); gen_aa32_ld16s_iss(s, tmp, addr, memidx, issinfo);
break; break;
case 2: case 2:
gen_aa32_ld32u(s, tmp, addr, memidx); gen_aa32_ld32u_iss(s, tmp, addr, memidx, issinfo);
break; break;
default: default:
tcg_temp_free_i32(tmp); tcg_temp_free_i32(tmp);
@ -10771,13 +10858,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
tmp = load_reg(s, rs); tmp = load_reg(s, rs);
switch (op) { switch (op) {
case 0: case 0:
gen_aa32_st8(s, tmp, addr, memidx); gen_aa32_st8_iss(s, tmp, addr, memidx, issinfo);
break; break;
case 1: case 1:
gen_aa32_st16(s, tmp, addr, memidx); gen_aa32_st16_iss(s, tmp, addr, memidx, issinfo);
break; break;
case 2: case 2:
gen_aa32_st32(s, tmp, addr, memidx); gen_aa32_st32_iss(s, tmp, addr, memidx, issinfo);
break; break;
default: default:
tcg_temp_free_i32(tmp); tcg_temp_free_i32(tmp);
@ -10914,7 +11001,8 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
addr = tcg_temp_new_i32(); addr = tcg_temp_new_i32();
tcg_gen_movi_i32(addr, val); tcg_gen_movi_i32(addr, val);
tmp = tcg_temp_new_i32(); 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); tcg_temp_free_i32(addr);
store_reg(s, rd, tmp); store_reg(s, rd, tmp);
break; break;
@ -11117,28 +11205,28 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
switch (op) { switch (op) {
case 0: /* str */ 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; break;
case 1: /* strh */ 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; break;
case 2: /* strb */ 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; break;
case 3: /* ldrsb */ 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; break;
case 4: /* ldr */ 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; break;
case 5: /* ldrh */ 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; break;
case 6: /* ldrb */ 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; break;
case 7: /* ldrsh */ 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; break;
} }
if (op >= 3) { /* load */ if (op >= 3) { /* load */
@ -11182,12 +11270,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
if (insn & (1 << 11)) { if (insn & (1 << 11)) {
/* load */ /* load */
tmp = tcg_temp_new_i32(); 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); store_reg(s, rd, tmp);
} else { } else {
/* store */ /* store */
tmp = load_reg(s, rd); 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(tmp);
} }
tcg_temp_free_i32(addr); tcg_temp_free_i32(addr);
@ -11204,12 +11292,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
if (insn & (1 << 11)) { if (insn & (1 << 11)) {
/* load */ /* load */
tmp = tcg_temp_new_i32(); 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); store_reg(s, rd, tmp);
} else { } else {
/* store */ /* store */
tmp = load_reg(s, rd); 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(tmp);
} }
tcg_temp_free_i32(addr); tcg_temp_free_i32(addr);
@ -11225,12 +11313,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
if (insn & (1 << 11)) { if (insn & (1 << 11)) {
/* load */ /* load */
tmp = tcg_temp_new_i32(); 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); store_reg(s, rd, tmp);
} else { } else {
/* store */ /* store */
tmp = load_reg(s, rd); 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(tmp);
} }
tcg_temp_free_i32(addr); tcg_temp_free_i32(addr);
@ -11712,6 +11800,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb)
store_cpu_field(tmp, condexec_bits); store_cpu_field(tmp, condexec_bits);
} }
do { do {
dc->insn_start_idx = tcg_op_buf_count();
tcg_gen_insn_start(dc->pc, tcg_gen_insn_start(dc->pc,
(dc->condexec_cond << 4) | (dc->condexec_mask >> 1), (dc->condexec_cond << 4) | (dc->condexec_mask >> 1),
0); 0);

View File

@ -104,6 +104,20 @@ static inline int default_exception_el(DisasContext *s)
? 3 : MAX(1, s->current_el); ? 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 */ /* target-specific extra values for is_jmp */
/* These instructions trap after executing, so the A32/T32 decoder must /* These instructions trap after executing, so the A32/T32 decoder must
* defer them until after the conditional execution state has been updated. * defer them until after the conditional execution state has been updated.