aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--hw/arm/aspeed.c9
-rw-r--r--hw/arm/exynos4210.c27
-rw-r--r--hw/arm/exynos4_boards.c50
-rw-r--r--hw/intc/arm_gic_kvm.c9
-rw-r--r--hw/intc/arm_gicv3_common.c1
-rw-r--r--hw/intc/arm_gicv3_its_common.c12
-rw-r--r--hw/intc/arm_gicv3_its_kvm.c131
-rw-r--r--hw/intc/arm_gicv3_kvm.c48
-rw-r--r--hw/intc/exynos4210_gic.c14
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/exynos4210_pmu.c20
-rw-r--r--hw/misc/tmp421.c402
-rw-r--r--hw/timer/aspeed_timer.c37
-rw-r--r--hw/timer/exynos4210_mct.c50
-rw-r--r--include/hw/arm/exynos4210.h5
-rw-r--r--include/hw/intc/arm_gicv3_its_common.h8
-rw-r--r--include/migration/vmstate.h2
-rw-r--r--include/qemu/timer.h5
-rw-r--r--include/sysemu/kvm.h11
-rw-r--r--kvm-all.c14
21 files changed, 741 insertions, 116 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 78d7af03a2..93e995d318 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -15,6 +15,7 @@ CONFIG_TWL92230=y
CONFIG_TSC2005=y
CONFIG_LM832X=y
CONFIG_TMP105=y
+CONFIG_TMP421=y
CONFIG_STELLARIS=y
CONFIG_STELLARIS_INPUT=y
CONFIG_STELLARIS_ENET=y
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index e824ea87a9..155eeb242b 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -239,10 +239,19 @@ static void aspeed_board_init(MachineState *machine,
static void palmetto_bmc_i2c_init(AspeedBoardState *bmc)
{
AspeedSoCState *soc = &bmc->soc;
+ DeviceState *dev;
/* The palmetto platform expects a ds3231 RTC but a ds1338 is
* enough to provide basic RTC features. Alarms will be missing */
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), "ds1338", 0x68);
+
+ /* add a TMP423 temperature sensor */
+ dev = i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 2),
+ "tmp423", 0x4c);
+ object_property_set_int(OBJECT(dev), 31000, "temperature0", &error_abort);
+ object_property_set_int(OBJECT(dev), 28000, "temperature1", &error_abort);
+ object_property_set_int(OBJECT(dev), 20000, "temperature2", &error_abort);
+ object_property_set_int(OBJECT(dev), 110000, "temperature3", &error_abort);
}
static void palmetto_bmc_init(MachineState *machine)
diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c
index 960f27e45a..0050626a69 100644
--- a/hw/arm/exynos4210.c
+++ b/hw/arm/exynos4210.c
@@ -26,6 +26,7 @@
#include "qemu-common.h"
#include "qemu/log.h"
#include "cpu.h"
+#include "hw/cpu/a9mpcore.h"
#include "hw/boards.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
@@ -160,16 +161,14 @@ static uint64_t exynos4210_calc_affinity(int cpu)
return mp_affinity;
}
-Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
- unsigned long ram_size)
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem)
{
- int i, n;
Exynos4210State *s = g_new(Exynos4210State, 1);
qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS];
- unsigned long mem_size;
- DeviceState *dev;
SysBusDevice *busdev;
ObjectClass *cpu_oc;
+ DeviceState *dev;
+ int i, n;
cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, "cortex-a9");
assert(cpu_oc);
@@ -213,7 +212,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
}
/* Private memory region and Internal GIC */
- dev = qdev_create(NULL, "a9mpcore_priv");
+ dev = qdev_create(NULL, TYPE_A9MPCORE_PRIV);
qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS);
qdev_init_nofail(dev);
busdev = SYS_BUS_DEVICE(dev);
@@ -299,22 +298,6 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
&s->iram_mem);
- /* DRAM */
- mem_size = ram_size;
- if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
- memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1",
- mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_fatal);
- vmstate_register_ram_global(&s->dram1_mem);
- memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
- &s->dram1_mem);
- mem_size = EXYNOS4210_DRAM_MAX_SIZE;
- }
- memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size,
- &error_fatal);
- vmstate_register_ram_global(&s->dram0_mem);
- memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
- &s->dram0_mem);
-
/* PMU.
* The only reason of existence at the moment is that secondary CPU boot
* loader uses PMU INFORM5 register as a holding pen.
diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c
index 4853c31802..6240b26839 100644
--- a/hw/arm/exynos4_boards.c
+++ b/hw/arm/exynos4_boards.c
@@ -22,6 +22,7 @@
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu-common.h"
#include "cpu.h"
@@ -56,6 +57,12 @@ typedef enum Exynos4BoardType {
EXYNOS4_NUM_OF_BOARDS
} Exynos4BoardType;
+typedef struct Exynos4BoardState {
+ Exynos4210State *soc;
+ MemoryRegion dram0_mem;
+ MemoryRegion dram1_mem;
+} Exynos4BoardState;
+
static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = {
[EXYNOS4_BOARD_NURI] = 0xD33,
[EXYNOS4_BOARD_SMDKC210] = 0xB16,
@@ -96,9 +103,34 @@ static void lan9215_init(uint32_t base, qemu_irq irq)
}
}
-static Exynos4210State *exynos4_boards_init_common(MachineState *machine,
- Exynos4BoardType board_type)
+static void exynos4_boards_init_ram(Exynos4BoardState *s,
+ MemoryRegion *system_mem,
+ unsigned long ram_size)
+{
+ unsigned long mem_size = ram_size;
+
+ if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
+ memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1",
+ mem_size - EXYNOS4210_DRAM_MAX_SIZE,
+ &error_fatal);
+ vmstate_register_ram_global(&s->dram1_mem);
+ memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
+ &s->dram1_mem);
+ mem_size = EXYNOS4210_DRAM_MAX_SIZE;
+ }
+
+ memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size,
+ &error_fatal);
+ vmstate_register_ram_global(&s->dram0_mem);
+ memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
+ &s->dram0_mem);
+}
+
+static Exynos4BoardState *
+exynos4_boards_init_common(MachineState *machine,
+ Exynos4BoardType board_type)
{
+ Exynos4BoardState *s = g_new(Exynos4BoardState, 1);
MachineClass *mc = MACHINE_GET_CLASS(machine);
if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) {
@@ -127,8 +159,12 @@ static Exynos4210State *exynos4_boards_init_common(MachineState *machine,
machine->kernel_cmdline,
machine->initrd_filename);
- return exynos4210_init(get_system_memory(),
- exynos4_board_ram_size[board_type]);
+ exynos4_boards_init_ram(s, get_system_memory(),
+ exynos4_board_ram_size[board_type]);
+
+ s->soc = exynos4210_init(get_system_memory());
+
+ return s;
}
static void nuri_init(MachineState *machine)
@@ -140,11 +176,11 @@ static void nuri_init(MachineState *machine)
static void smdkc210_init(MachineState *machine)
{
- Exynos4210State *s = exynos4_boards_init_common(machine,
- EXYNOS4_BOARD_SMDKC210);
+ Exynos4BoardState *s = exynos4_boards_init_common(machine,
+ EXYNOS4_BOARD_SMDKC210);
lan9215_init(SMDK_LAN9118_BASE_ADDR,
- qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)]));
+ qemu_irq_invert(s->soc->irq_table[exynos4210_get_irq(37, 1)]));
arm_load_kernel(ARM_CPU(first_cpu), &exynos4_board_binfo);
}
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index af5cd367e9..ae095d08a3 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -100,14 +100,14 @@ static void kvm_gicd_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
- KVM_VGIC_ATTR(offset, cpu), val, write);
+ KVM_VGIC_ATTR(offset, cpu), val, write, &error_abort);
}
static void kvm_gicc_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
- KVM_VGIC_ATTR(offset, cpu), val, write);
+ KVM_VGIC_ATTR(offset, cpu), val, write, &error_abort);
}
#define for_each_irq_reg(_ctr, _max_irq, _field_width) \
@@ -538,13 +538,14 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
uint32_t numirqs = s->num_irq;
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0,
- &numirqs, true);
+ &numirqs, true, &error_abort);
}
/* Tell the kernel to complete VGIC initialization now */
if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT)) {
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true,
+ &error_abort);
}
} else if (ret != -ENODEV && ret != -ENOTSUP) {
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index c6493d6c07..4228b7ca00 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -145,6 +145,7 @@ static const VMStateDescription vmstate_gicv3 = {
.minimum_version_id = 1,
.pre_save = gicv3_pre_save,
.post_load = gicv3_post_load,
+ .priority = MIG_PRI_GICV3,
.fields = (VMStateField[]) {
VMSTATE_UINT32(gicd_ctlr, GICv3State),
VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2),
diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c
index 9d67c5c1ee..68b20fccd1 100644
--- a/hw/intc/arm_gicv3_its_common.c
+++ b/hw/intc/arm_gicv3_its_common.c
@@ -48,7 +48,16 @@ static const VMStateDescription vmstate_its = {
.name = "arm_gicv3_its",
.pre_save = gicv3_its_pre_save,
.post_load = gicv3_its_post_load,
- .unmigratable = true,
+ .priority = MIG_PRI_GICV3_ITS,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ctlr, GICv3ITSState),
+ VMSTATE_UINT32(iidr, GICv3ITSState),
+ VMSTATE_UINT64(cbaser, GICv3ITSState),
+ VMSTATE_UINT64(cwriter, GICv3ITSState),
+ VMSTATE_UINT64(creadr, GICv3ITSState),
+ VMSTATE_UINT64_ARRAY(baser, GICv3ITSState, 8),
+ VMSTATE_END_OF_LIST()
+ },
};
static MemTxResult gicv3_its_trans_read(void *opaque, hwaddr offset,
@@ -118,6 +127,7 @@ static void gicv3_its_common_reset(DeviceState *dev)
s->cbaser = 0;
s->cwriter = 0;
s->creadr = 0;
+ s->iidr = 0;
memset(&s->baser, 0, sizeof(s->baser));
gicv3_its_post_load(s, 0);
diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
index a0441d6bd1..1f8991b8a6 100644
--- a/hw/intc/arm_gicv3_its_kvm.c
+++ b/hw/intc/arm_gicv3_its_kvm.c
@@ -53,23 +53,38 @@ static int kvm_its_send_msi(GICv3ITSState *s, uint32_t value, uint16_t devid)
return kvm_vm_ioctl(kvm_state, KVM_SIGNAL_MSI, &msi);
}
-static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
+/**
+ * vm_change_state_handler - VM change state callback aiming at flushing
+ * ITS tables into guest RAM
+ *
+ * The tables get flushed to guest RAM whenever the VM gets stopped.
+ */
+static void vm_change_state_handler(void *opaque, int running,
+ RunState state)
{
- GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
- Error *local_err = NULL;
+ GICv3ITSState *s = (GICv3ITSState *)opaque;
+ Error *err = NULL;
+ int ret;
- /*
- * Block migration of a KVM GICv3 ITS device: the API for saving and
- * restoring the state in the kernel is not yet available
- */
- error_setg(&s->migration_blocker, "vITS migration is not implemented");
- migrate_add_blocker(s->migration_blocker, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- error_free(s->migration_blocker);
+ if (running) {
return;
}
+ ret = kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_ITS_SAVE_TABLES, NULL, true, &err);
+ if (err) {
+ error_report_err(err);
+ }
+ if (ret < 0 && ret != -EFAULT) {
+ abort();
+ }
+}
+
+static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
+{
+ GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev);
+ Error *local_err = NULL;
+
s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_ITS, false);
if (s->dev_fd < 0) {
error_setg_errno(errp, -s->dev_fd, "error creating in-kernel ITS");
@@ -78,7 +93,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
/* explicit init of the ITS */
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort);
/* register the base address */
kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
@@ -86,9 +101,23 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
gicv3_its_init_mmio(s, NULL);
+ if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CTLR)) {
+ error_setg(&s->migration_blocker, "This operating system kernel "
+ "does not support vITS migration");
+ migrate_add_blocker(s->migration_blocker, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ error_free(s->migration_blocker);
+ return;
+ }
+ }
+
kvm_msi_use_devid = true;
kvm_gsi_direct_mapping = false;
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
+
+ qemu_add_vm_change_state_handler(vm_change_state_handler, s);
}
static void kvm_arm_its_init(Object *obj)
@@ -102,6 +131,80 @@ static void kvm_arm_its_init(Object *obj)
&error_abort);
}
+/**
+ * kvm_arm_its_pre_save - handles the saving of ITS registers.
+ * ITS tables are flushed into guest RAM separately and earlier,
+ * through the VM change state handler, since at the moment pre_save()
+ * is called, the guest RAM has already been saved.
+ */
+static void kvm_arm_its_pre_save(GICv3ITSState *s)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_BASER + i * 8, &s->baser[i], false,
+ &error_abort);
+ }
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CTLR, &s->ctlr, false, &error_abort);
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CBASER, &s->cbaser, false, &error_abort);
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CREADR, &s->creadr, false, &error_abort);
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CWRITER, &s->cwriter, false, &error_abort);
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_IIDR, &s->iidr, false, &error_abort);
+}
+
+/**
+ * kvm_arm_its_post_load - Restore both the ITS registers and tables
+ */
+static void kvm_arm_its_post_load(GICv3ITSState *s)
+{
+ int i;
+
+ if (!s->iidr) {
+ return;
+ }
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_IIDR, &s->iidr, true, &error_abort);
+
+ /*
+ * must be written before GITS_CREADR since GITS_CBASER write
+ * access resets GITS_CREADR.
+ */
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CBASER, &s->cbaser, true, &error_abort);
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CREADR, &s->creadr, true, &error_abort);
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CWRITER, &s->cwriter, true, &error_abort);
+
+
+ for (i = 0; i < 8; i++) {
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_BASER + i * 8, &s->baser[i], true,
+ &error_abort);
+ }
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_ITS_RESTORE_TABLES, NULL, true,
+ &error_abort);
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+ GITS_CTLR, &s->ctlr, true, &error_abort);
+}
+
static void kvm_arm_its_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -109,6 +212,8 @@ static void kvm_arm_its_class_init(ObjectClass *klass, void *data)
dc->realize = kvm_arm_its_realize;
icc->send_msi = kvm_its_send_msi;
+ icc->pre_save = kvm_arm_its_pre_save;
+ icc->post_load = kvm_arm_its_post_load;
}
static const TypeInfo kvm_arm_its_info = {
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 4ee2baa691..6051c77705 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -25,6 +25,7 @@
#include "hw/sysbus.h"
#include "qemu/error-report.h"
#include "sysemu/kvm.h"
+#include "sysemu/sysemu.h"
#include "kvm_arm.h"
#include "gicv3_internal.h"
#include "vgic_common.h"
@@ -93,7 +94,7 @@ static inline void kvm_gicd_access(GICv3State *s, int offset,
{
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
KVM_VGIC_ATTR(offset, 0),
- val, write);
+ val, write, &error_abort);
}
static inline void kvm_gicr_access(GICv3State *s, int offset, int cpu,
@@ -101,7 +102,7 @@ static inline void kvm_gicr_access(GICv3State *s, int offset, int cpu,
{
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
KVM_VGIC_ATTR(offset, s->cpu[cpu].gicr_typer),
- val, write);
+ val, write, &error_abort);
}
static inline void kvm_gicc_access(GICv3State *s, uint64_t reg, int cpu,
@@ -109,7 +110,7 @@ static inline void kvm_gicc_access(GICv3State *s, uint64_t reg, int cpu,
{
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
KVM_VGIC_ATTR(reg, s->cpu[cpu].gicr_typer),
- val, write);
+ val, write, &error_abort);
}
static inline void kvm_gic_line_level_access(GICv3State *s, int irq, int cpu,
@@ -119,7 +120,7 @@ static inline void kvm_gic_line_level_access(GICv3State *s, int irq, int cpu,
KVM_VGIC_ATTR(irq, s->cpu[cpu].gicr_typer) |
(VGIC_LEVEL_INFO_LINE_LEVEL <<
KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT),
- val, write);
+ val, write, &error_abort);
}
/* Loop through each distributor IRQ related register; since bits
@@ -630,7 +631,7 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
/* Initialize to actual HW supported configuration */
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
KVM_VGIC_ATTR(ICC_CTLR_EL1, cpu->mp_affinity),
- &c->icc_ctlr_el1[GICV3_NS], false);
+ &c->icc_ctlr_el1[GICV3_NS], false, &error_abort);
c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS];
}
@@ -680,6 +681,35 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = {
REGINFO_SENTINEL
};
+/**
+ * vm_change_state_handler - VM change state callback aiming at flushing
+ * RDIST pending tables into guest RAM
+ *
+ * The tables get flushed to guest RAM whenever the VM gets stopped.
+ */
+static void vm_change_state_handler(void *opaque, int running,
+ RunState state)
+{
+ GICv3State *s = (GICv3State *)opaque;
+ Error *err = NULL;
+ int ret;
+
+ if (running) {
+ return;
+ }
+
+ ret = kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES,
+ NULL, true, &err);
+ if (err) {
+ error_report_err(err);
+ }
+ if (ret < 0 && ret != -EFAULT) {
+ abort();
+ }
+}
+
+
static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
{
GICv3State *s = KVM_ARM_GICV3(dev);
@@ -717,11 +747,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
}
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
- 0, &s->num_irq, true);
+ 0, &s->num_irq, true, &error_abort);
/* Tell the kernel to complete VGIC initialization now */
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort);
kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd);
@@ -751,6 +781,10 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
return;
}
}
+ if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES)) {
+ qemu_add_vm_change_state_handler(vm_change_state_handler, s);
+ }
}
static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data)
diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c
index 2a55817b76..b6b00a4f58 100644
--- a/hw/intc/exynos4210_gic.c
+++ b/hw/intc/exynos4210_gic.c
@@ -116,7 +116,7 @@ enum ExtInt {
* which is INTG16 in Internal Interrupt Combiner.
*/
-static uint32_t
+static const uint32_t
combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
/* int combiner groups 16-19 */
{ }, { }, { }, { },
@@ -286,21 +286,21 @@ static void exynos4210_gic_init(Object *obj)
DeviceState *dev = DEVICE(obj);
Exynos4210GicState *s = EXYNOS4210_GIC(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- uint32_t i;
const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
const char dist_prefix[] = "exynos4210-gic-alias_dist";
char cpu_alias_name[sizeof(cpu_prefix) + 3];
char dist_alias_name[sizeof(cpu_prefix) + 3];
- SysBusDevice *busdev;
+ SysBusDevice *gicbusdev;
+ uint32_t i;
s->gic = qdev_create(NULL, "arm_gic");
qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ);
qdev_init_nofail(s->gic);
- busdev = SYS_BUS_DEVICE(s->gic);
+ gicbusdev = SYS_BUS_DEVICE(s->gic);
/* Pass through outbound IRQ lines from the GIC */
- sysbus_pass_irq(sbd, busdev);
+ sysbus_pass_irq(sbd, gicbusdev);
/* Pass through inbound GPIO lines to the GIC */
qdev_init_gpio_in(dev, exynos4210_gic_set_irq,
@@ -316,7 +316,7 @@ static void exynos4210_gic_init(Object *obj)
sprintf(cpu_alias_name, "%s%x", cpu_prefix, i);
memory_region_init_alias(&s->cpu_alias[i], obj,
cpu_alias_name,
- sysbus_mmio_get_region(busdev, 1),
+ sysbus_mmio_get_region(gicbusdev, 1),
0,
EXYNOS4210_GIC_CPU_REGION_SIZE);
memory_region_add_subregion(&s->cpu_container,
@@ -326,7 +326,7 @@ static void exynos4210_gic_init(Object *obj)
sprintf(dist_alias_name, "%s%x", dist_prefix, i);
memory_region_init_alias(&s->dist_alias[i], obj,
dist_alias_name,
- sysbus_mmio_get_region(busdev, 0),
+ sysbus_mmio_get_region(gicbusdev, 0),
0,
EXYNOS4210_GIC_DIST_REGION_SIZE);
memory_region_add_subregion(&s->dist_container,
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index c8b489390f..20198466f0 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -1,6 +1,7 @@
common-obj-$(CONFIG_APPLESMC) += applesmc.o
common-obj-$(CONFIG_MAX111X) += max111x.o
common-obj-$(CONFIG_TMP105) += tmp105.o
+common-obj-$(CONFIG_TMP421) += tmp421.o
common-obj-$(CONFIG_ISA_DEBUG) += debugexit.o
common-obj-$(CONFIG_SGA) += sga.o
common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o
diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c
index 63a8ccd355..0d7b64c5b3 100644
--- a/hw/misc/exynos4210_pmu.c
+++ b/hw/misc/exynos4210_pmu.c
@@ -26,6 +26,7 @@
#include "qemu/osdep.h"
#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
#ifndef DEBUG_PMU
#define DEBUG_PMU 0
@@ -350,7 +351,11 @@ static const Exynos4210PmuReg exynos4210_pmu_regs[] = {
{"PAD_RETENTION_MMCB_OPTION", PAD_RETENTION_MMCB_OPTION, 0x00000000},
{"PAD_RETENTION_EBIA_OPTION", PAD_RETENTION_EBIA_OPTION, 0x00000000},
{"PAD_RETENTION_EBIB_OPTION", PAD_RETENTION_EBIB_OPTION, 0x00000000},
- {"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200},
+ /*
+ * PS_HOLD_CONTROL: reset value and manually toggle high the DATA bit.
+ * DATA bit high, set usually by bootloader, keeps system on.
+ */
+ {"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200 | BIT(8)},
{"XUSBXTI_CONFIGURATION", XUSBXTI_CONFIGURATION, 0x00000001},
{"XUSBXTI_STATUS", XUSBXTI_STATUS, 0x00000001},
{"XUSBXTI_DURATION", XUSBXTI_DURATION, 0xFFF00000},
@@ -397,6 +402,12 @@ typedef struct Exynos4210PmuState {
uint32_t reg[PMU_NUM_OF_REGISTERS];
} Exynos4210PmuState;
+static void exynos4210_pmu_poweroff(void)
+{
+ PRINT_DEBUG("QEMU PMU: PS_HOLD bit down, powering off\n");
+ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+}
+
static uint64_t exynos4210_pmu_read(void *opaque, hwaddr offset,
unsigned size)
{
@@ -428,6 +439,13 @@ static void exynos4210_pmu_write(void *opaque, hwaddr offset,
PRINT_DEBUG_EXTEND("%s <0x%04x> <- 0x%04x\n", reg_p->name,
(uint32_t)offset, (uint32_t)val);
s->reg[i] = val;
+ if ((offset == PS_HOLD_CONTROL) && ((val & BIT(8)) == 0)) {
+ /*
+ * We are interested only in setting data bit
+ * of PS_HOLD_CONTROL register to indicate power off request.
+ */
+ exynos4210_pmu_poweroff();
+ }
return;
}
reg_p++;
diff --git a/hw/misc/tmp421.c b/hw/misc/tmp421.c
new file mode 100644
index 0000000000..4a505abbce
--- /dev/null
+++ b/hw/misc/tmp421.c
@@ -0,0 +1,402 @@
+/*
+ * Texas Instruments TMP421 temperature sensor.
+ *
+ * Copyright (c) 2016 IBM Corporation.
+ *
+ * Largely inspired by :
+ *
+ * Texas Instruments TMP105 temperature sensor.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+
+/* Manufacturer / Device ID's */
+#define TMP421_MANUFACTURER_ID 0x55
+#define TMP421_DEVICE_ID 0x21
+#define TMP422_DEVICE_ID 0x22
+#define TMP423_DEVICE_ID 0x23
+
+typedef struct DeviceInfo {
+ int model;
+ const char *name;
+} DeviceInfo;
+
+static const DeviceInfo devices[] = {
+ { TMP421_DEVICE_ID, "tmp421" },
+ { TMP422_DEVICE_ID, "tmp422" },
+ { TMP423_DEVICE_ID, "tmp423" },
+};
+
+typedef struct TMP421State {
+ /*< private >*/
+ I2CSlave i2c;
+ /*< public >*/
+
+ int16_t temperature[4];
+
+ uint8_t status;
+ uint8_t config[2];
+ uint8_t rate;
+
+ uint8_t len;
+ uint8_t buf[2];
+ uint8_t pointer;
+
+} TMP421State;
+
+typedef struct TMP421Class {
+ I2CSlaveClass parent_class;
+ DeviceInfo *dev;
+} TMP421Class;
+
+#define TYPE_TMP421 "tmp421-generic"
+#define TMP421(obj) OBJECT_CHECK(TMP421State, (obj), TYPE_TMP421)
+
+#define TMP421_CLASS(klass) \
+ OBJECT_CLASS_CHECK(TMP421Class, (klass), TYPE_TMP421)
+#define TMP421_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(TMP421Class, (obj), TYPE_TMP421)
+
+/* the TMP421 registers */
+#define TMP421_STATUS_REG 0x08
+#define TMP421_STATUS_BUSY (1 << 7)
+#define TMP421_CONFIG_REG_1 0x09
+#define TMP421_CONFIG_RANGE (1 << 2)
+#define TMP421_CONFIG_SHUTDOWN (1 << 6)
+#define TMP421_CONFIG_REG_2 0x0A
+#define TMP421_CONFIG_RC (1 << 2)
+#define TMP421_CONFIG_LEN (1 << 3)
+#define TMP421_CONFIG_REN (1 << 4)
+#define TMP421_CONFIG_REN2 (1 << 5)
+#define TMP421_CONFIG_REN3 (1 << 6)
+
+#define TMP421_CONVERSION_RATE_REG 0x0B
+#define TMP421_ONE_SHOT 0x0F
+
+#define TMP421_RESET 0xFC
+#define TMP421_MANUFACTURER_ID_REG 0xFE
+#define TMP421_DEVICE_ID_REG 0xFF
+
+#define TMP421_TEMP_MSB0 0x00
+#define TMP421_TEMP_MSB1 0x01
+#define TMP421_TEMP_MSB2 0x02
+#define TMP421_TEMP_MSB3 0x03
+#define TMP421_TEMP_LSB0 0x10
+#define TMP421_TEMP_LSB1 0x11
+#define TMP421_TEMP_LSB2 0x12
+#define TMP421_TEMP_LSB3 0x13
+
+static const int32_t mins[2] = { -40000, -55000 };
+static const int32_t maxs[2] = { 127000, 150000 };
+
+static void tmp421_get_temperature(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ TMP421State *s = TMP421(obj);
+ bool ext_range = (s->config[0] & TMP421_CONFIG_RANGE);
+ int offset = ext_range * 64 * 256;
+ int64_t value;
+ int tempid;
+
+ if (sscanf(name, "temperature%d", &tempid) != 1) {
+ error_setg(errp, "error reading %s: %m", name);
+ return;
+ }
+
+ if (tempid >= 4 || tempid < 0) {
+ error_setg(errp, "error reading %s", name);
+ return;
+ }
+
+ value = ((s->temperature[tempid] - offset) * 1000 + 128) / 256;
+
+ visit_type_int(v, name, &value, errp);
+}
+
+/* Units are 0.001 centigrades relative to 0 C. s->temperature is 8.8
+ * fixed point, so units are 1/256 centigrades. A simple ratio will do.
+ */
+static void tmp421_set_temperature(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ TMP421State *s = TMP421(obj);
+ Error *local_err = NULL;
+ int64_t temp;
+ bool ext_range = (s->config[0] & TMP421_CONFIG_RANGE);
+ int offset = ext_range * 64 * 256;
+ int tempid;
+
+ visit_type_int(v, name, &temp, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (temp >= maxs[ext_range] || temp < mins[ext_range]) {
+ error_setg(errp, "value %" PRId64 ".%03" PRIu64 " °C is out of range",
+ temp / 1000, temp % 1000);
+ return;
+ }
+
+ if (sscanf(name, "temperature%d", &tempid) != 1) {
+ error_setg(errp, "error reading %s: %m", name);
+ return;
+ }
+
+ if (tempid >= 4 || tempid < 0) {
+ error_setg(errp, "error reading %s", name);
+ return;
+ }
+
+ s->temperature[tempid] = (int16_t) ((temp * 256 - 128) / 1000) + offset;
+}
+
+static void tmp421_read(TMP421State *s)
+{
+ TMP421Class *sc = TMP421_GET_CLASS(s);
+
+ s->len = 0;
+
+ switch (s->pointer) {
+ case TMP421_MANUFACTURER_ID_REG:
+ s->buf[s->len++] = TMP421_MANUFACTURER_ID;
+ break;
+ case TMP421_DEVICE_ID_REG:
+ s->buf[s->len++] = sc->dev->model;
+ break;
+ case TMP421_CONFIG_REG_1:
+ s->buf[s->len++] = s->config[0];
+ break;
+ case TMP421_CONFIG_REG_2:
+ s->buf[s->len++] = s->config[1];
+ break;
+ case TMP421_CONVERSION_RATE_REG:
+ s->buf[s->len++] = s->rate;
+ break;
+ case TMP421_STATUS_REG:
+ s->buf[s->len++] = s->status;
+ break;
+
+ /* FIXME: check for channel enablement in config registers */
+ case TMP421_TEMP_MSB0:
+ s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 8);
+ s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0;
+ break;
+ case TMP421_TEMP_MSB1:
+ s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 8);
+ s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0;
+ break;
+ case TMP421_TEMP_MSB2:
+ s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 8);
+ s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0;
+ break;
+ case TMP421_TEMP_MSB3:
+ s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 8);
+ s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0;
+ break;
+ case TMP421_TEMP_LSB0:
+ s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0;
+ break;
+ case TMP421_TEMP_LSB1:
+ s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0;
+ break;
+ case TMP421_TEMP_LSB2:
+ s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0;
+ break;
+ case TMP421_TEMP_LSB3:
+ s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0;
+ break;
+ }
+}
+
+static void tmp421_reset(I2CSlave *i2c);
+
+static void tmp421_write(TMP421State *s)
+{
+ switch (s->pointer) {
+ case TMP421_CONVERSION_RATE_REG:
+ s->rate = s->buf[0];
+ break;
+ case TMP421_CONFIG_REG_1:
+ s->config[0] = s->buf[0];
+ break;
+ case TMP421_CONFIG_REG_2:
+ s->config[1] = s->buf[0];
+ break;
+ case TMP421_RESET:
+ tmp421_reset(I2C_SLAVE(s));
+ break;
+ }
+}
+
+static int tmp421_rx(I2CSlave *i2c)
+{
+ TMP421State *s = TMP421(i2c);
+
+ if (s->len < 2) {
+ return s->buf[s->len++];
+ } else {
+ return 0xff;
+ }
+}
+
+static int tmp421_tx(I2CSlave *i2c, uint8_t data)
+{
+ TMP421State *s = TMP421(i2c);
+
+ if (s->len == 0) {
+ /* first byte is the register pointer for a read or write
+ * operation */
+ s->pointer = data;
+ s->len++;
+ } else if (s->len == 1) {
+ /* second byte is the data to write. The device only supports
+ * one byte writes */
+ s->buf[0] = data;
+ tmp421_write(s);
+ }
+
+ return 0;
+}
+
+static int tmp421_event(I2CSlave *i2c, enum i2c_event event)
+{
+ TMP421State *s = TMP421(i2c);
+
+ if (event == I2C_START_RECV) {
+ tmp421_read(s);
+ }
+
+ s->len = 0;
+ return 0;
+}
+
+static const VMStateDescription vmstate_tmp421 = {
+ .name = "TMP421",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(len, TMP421State),
+ VMSTATE_UINT8_ARRAY(buf, TMP421State, 2),
+ VMSTATE_UINT8(pointer, TMP421State),
+ VMSTATE_UINT8_ARRAY(config, TMP421State, 2),
+ VMSTATE_UINT8(status, TMP421State),
+ VMSTATE_UINT8(rate, TMP421State),
+ VMSTATE_INT16_ARRAY(temperature, TMP421State, 4),
+ VMSTATE_I2C_SLAVE(i2c, TMP421State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void tmp421_reset(I2CSlave *i2c)
+{
+ TMP421State *s = TMP421(i2c);
+ TMP421Class *sc = TMP421_GET_CLASS(s);
+
+ memset(s->temperature, 0, sizeof(s->temperature));
+ s->pointer = 0;
+
+ s->config[0] = 0; /* TMP421_CONFIG_RANGE */
+
+ /* resistance correction and channel enablement */
+ switch (sc->dev->model) {
+ case TMP421_DEVICE_ID:
+ s->config[1] = 0x1c;
+ break;
+ case TMP422_DEVICE_ID:
+ s->config[1] = 0x3c;
+ break;
+ case TMP423_DEVICE_ID:
+ s->config[1] = 0x7c;
+ break;
+ }
+
+ s->rate = 0x7; /* 8Hz */
+ s->status = 0;
+}
+
+static int tmp421_init(I2CSlave *i2c)
+{
+ TMP421State *s = TMP421(i2c);
+
+ tmp421_reset(&s->i2c);
+
+ return 0;
+}
+
+static void tmp421_initfn(Object *obj)
+{
+ object_property_add(obj, "temperature0", "int",
+ tmp421_get_temperature,
+ tmp421_set_temperature, NULL, NULL, NULL);
+ object_property_add(obj, "temperature1", "int",
+ tmp421_get_temperature,
+ tmp421_set_temperature, NULL, NULL, NULL);
+ object_property_add(obj, "temperature2", "int",
+ tmp421_get_temperature,
+ tmp421_set_temperature, NULL, NULL, NULL);
+ object_property_add(obj, "temperature3", "int",
+ tmp421_get_temperature,
+ tmp421_set_temperature, NULL, NULL, NULL);
+}
+
+static void tmp421_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+ TMP421Class *sc = TMP421_CLASS(klass);
+
+ k->init = tmp421_init;
+ k->event = tmp421_event;
+ k->recv = tmp421_rx;
+ k->send = tmp421_tx;
+ dc->vmsd = &vmstate_tmp421;
+ sc->dev = (DeviceInfo *) data;
+}
+
+static const TypeInfo tmp421_info = {
+ .name = TYPE_TMP421,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(TMP421State),
+ .class_size = sizeof(TMP421Class),
+ .instance_init = tmp421_initfn,
+ .abstract = true,
+};
+
+static void tmp421_register_types(void)
+{
+ int i;
+
+ type_register_static(&tmp421_info);
+ for (i = 0; i < ARRAY_SIZE(devices); ++i) {
+ TypeInfo ti = {
+ .name = devices[i].name,
+ .parent = TYPE_TMP421,
+ .class_init = tmp421_class_init,
+ .class_data = (void *) &devices[i],
+ };
+ type_register(&ti);
+ }
+}
+
+type_init(tmp421_register_types)
diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
index 9b70ee09b0..50acbf530a 100644
--- a/hw/timer/aspeed_timer.c
+++ b/hw/timer/aspeed_timer.c
@@ -130,15 +130,26 @@ static uint64_t calculate_next(struct AspeedTimer *t)
next = seq[1];
} else if (now < seq[2]) {
next = seq[2];
- } else {
+ } else if (t->reload) {
reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
t->start = now - ((now - t->start) % reload_ns);
+ } else {
+ /* no reload value, return 0 */
+ break;
}
}
return next;
}
+static void aspeed_timer_mod(AspeedTimer *t)
+{
+ uint64_t next = calculate_next(t);
+ if (next) {
+ timer_mod(&t->timer, next);
+ }
+}
+
static void aspeed_timer_expire(void *opaque)
{
AspeedTimer *t = opaque;
@@ -164,7 +175,7 @@ static void aspeed_timer_expire(void *opaque)
qemu_set_irq(t->irq, t->level);
}
- timer_mod(&t->timer, calculate_next(t));
+ aspeed_timer_mod(t);
}
static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg)
@@ -227,10 +238,23 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
uint32_t value)
{
AspeedTimer *t;
+ uint32_t old_reload;
trace_aspeed_timer_set_value(timer, reg, value);
t = &s->timers[timer];
switch (reg) {
+ case TIMER_REG_RELOAD:
+ old_reload = t->reload;
+ t->reload = value;
+
+ /* If the reload value was not previously set, or zero, and
+ * the current value is valid, try to start the timer if it is
+ * enabled.
+ */
+ if (old_reload || !t->reload) {
+ break;
+ }
+
case TIMER_REG_STATUS:
if (timer_enabled(t)) {
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@@ -238,17 +262,14 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
uint32_t rate = calculate_rate(t);
t->start += muldiv64(delta, NANOSECONDS_PER_SECOND, rate);
- timer_mod(&t->timer, calculate_next(t));
+ aspeed_timer_mod(t);
}
break;
- case TIMER_REG_RELOAD:
- t->reload = value;
- break;
case TIMER_REG_MATCH_FIRST:
case TIMER_REG_MATCH_SECOND:
t->match[reg - 2] = value;
if (timer_enabled(t)) {
- timer_mod(&t->timer, calculate_next(t));
+ aspeed_timer_mod(t);
}
break;
default:
@@ -268,7 +289,7 @@ static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable)
trace_aspeed_timer_ctrl_enable(t->id, enable);
if (enable) {
t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- timer_mod(&t->timer, calculate_next(t));
+ aspeed_timer_mod(t);
} else {
timer_del(&t->timer);
}
diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c
index a2ec3920f8..e4ef4cfd36 100644
--- a/hw/timer/exynos4210_mct.c
+++ b/hw/timer/exynos4210_mct.c
@@ -173,13 +173,10 @@ enum LocalTimerRegCntIndexes {
L_REG_CNT_AMOUNT
};
-#define MCT_NIRQ 6
#define MCT_SFR_SIZE 0x444
#define MCT_GT_CMP_NUM 4
-#define MCT_GT_MAX_VAL UINT64_MAX
-
#define MCT_GT_COUNTER_STEP 0x100000000ULL
#define MCT_LT_COUNTER_STEP 0x100000000ULL
#define MCT_LT_CNT_LOW_LIMIT 0x100
@@ -937,7 +934,7 @@ static void exynos4210_mct_update_freq(Exynos4210MCTState *s)
{
uint32_t freq = s->freq;
s->freq = 24000000 /
- ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) *
+ ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg) + 1) *
MCT_CFG_GET_DIVIDER(s->reg_mct_cfg));
if (freq != s->freq) {
@@ -1016,9 +1013,9 @@ static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset,
case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
- index = GET_G_COMP_IDX(offset);
- shift = 8 * (offset & 0x4);
- value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift);
+ index = GET_G_COMP_IDX(offset);
+ shift = 8 * (offset & 0x4);
+ value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift);
break;
case G_TCON:
@@ -1067,7 +1064,6 @@ static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset,
lt_i = GET_L_TIMER_IDX(offset);
value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]);
-
break;
case L0_TCON: case L1_TCON:
@@ -1153,23 +1149,23 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset,
case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
- index = GET_G_COMP_IDX(offset);
- shift = 8 * (offset & 0x4);
- s->g_timer.reg.comp[index] =
- (s->g_timer.reg.comp[index] &
- (((uint64_t)UINT32_MAX << 32) >> shift)) +
- (value << shift);
+ index = GET_G_COMP_IDX(offset);
+ shift = 8 * (offset & 0x4);
+ s->g_timer.reg.comp[index] =
+ (s->g_timer.reg.comp[index] &
+ (((uint64_t)UINT32_MAX << 32) >> shift)) +
+ (value << shift);
- DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);
+ DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);
- if (offset&0x4) {
- s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);
- } else {
- s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
- }
+ if (offset & 0x4) {
+ s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);
+ } else {
+ s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
+ }
- exynos4210_gfrc_restart(s);
- break;
+ exynos4210_gfrc_restart(s);
+ break;
case G_TCON:
old_val = s->g_timer.reg.tcon;
@@ -1207,7 +1203,6 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset,
break;
case G_INT_ENB:
-
/* Raise IRQ if transition from disabled to enabled and CSTAT pending */
for (i = 0; i < MCT_GT_CMP_NUM; i++) {
if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon &
@@ -1288,7 +1283,6 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset,
break;
case L0_TCNTB: case L1_TCNTB:
-
lt_i = GET_L_TIMER_IDX(offset);
index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
@@ -1316,7 +1310,6 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset,
break;
case L0_ICNTB: case L1_ICNTB:
-
lt_i = GET_L_TIMER_IDX(offset);
index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
@@ -1353,13 +1346,12 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset,
if (icntb_max[lt_i] < value) {
icntb_max[lt_i] = value;
}
-DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",
- lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);
+ DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",
+ lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);
#endif
-break;
+ break;
case L0_FRCNTB: case L1_FRCNTB:
-
lt_i = GET_L_TIMER_IDX(offset);
index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
diff --git a/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h
index d9e08014d6..098a69ec73 100644
--- a/include/hw/arm/exynos4210.h
+++ b/include/hw/arm/exynos4210.h
@@ -93,8 +93,6 @@ typedef struct Exynos4210State {
MemoryRegion iram_mem;
MemoryRegion irom_mem;
MemoryRegion irom_alias_mem;
- MemoryRegion dram0_mem;
- MemoryRegion dram1_mem;
MemoryRegion boot_secondary;
MemoryRegion bootreg_mem;
I2CBus *i2c_if[EXYNOS4210_I2C_NUMBER];
@@ -103,8 +101,7 @@ typedef struct Exynos4210State {
void exynos4210_write_secondary(ARMCPU *cpu,
const struct arm_boot_info *info);
-Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
- unsigned long ram_size);
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem);
/* Initialize exynos4210 IRQ subsystem stub */
qemu_irq *exynos4210_init_irq(Exynos4210Irq *env);
diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
index 1ba18944cf..fd1fe64c03 100644
--- a/include/hw/intc/arm_gicv3_its_common.h
+++ b/include/hw/intc/arm_gicv3_its_common.h
@@ -28,6 +28,13 @@
#define ITS_TRANS_SIZE 0x10000
#define ITS_SIZE (ITS_CONTROL_SIZE + ITS_TRANS_SIZE)
+#define GITS_CTLR 0x0
+#define GITS_IIDR 0x4
+#define GITS_CBASER 0x80
+#define GITS_CWRITER 0x88
+#define GITS_CREADR 0x90
+#define GITS_BASER 0x100
+
struct GICv3ITSState {
SysBusDevice parent_obj;
@@ -43,6 +50,7 @@ struct GICv3ITSState {
/* Registers */
uint32_t ctlr;
+ uint32_t iidr;
uint64_t cbaser;
uint64_t cwriter;
uint64_t creadr;
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 79a4b350a8..f3f3c2af4d 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -149,6 +149,8 @@ enum VMStateFlags {
typedef enum {
MIG_PRI_DEFAULT = 0,
MIG_PRI_IOMMU, /* Must happen before PCI devices */
+ MIG_PRI_GICV3_ITS, /* Must happen before PCI devices */
+ MIG_PRI_GICV3, /* Must happen before the ITS */
MIG_PRI_MAX,
} MigrationPriority;
diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index 8a1eb74839..1b518bca30 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -1020,10 +1020,9 @@ static inline int64_t cpu_get_host_ticks(void)
/* The host CPU doesn't have an easily accessible cycle counter.
Just return a monotonically increasing value. This will be
totally wrong, but hopefully better than nothing. */
-static inline int64_t cpu_get_host_ticks (void)
+static inline int64_t cpu_get_host_ticks(void)
{
- static int64_t ticks = 0;
- return ticks++;
+ return get_clock();
}
#endif
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index a45c145560..1e91613ca9 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -294,12 +294,15 @@ int kvm_device_check_attr(int fd, uint32_t group, uint64_t attr);
* @attr: the attribute of that group to set or get
* @val: pointer to a storage area for the value
* @write: true for set and false for get operation
+ * @errp: error object handle
*
- * This function is not allowed to fail. Use kvm_device_check_attr()
- * in order to check for the availability of optional attributes.
+ * Returns: 0 on success
+ * < 0 on error
+ * Use kvm_device_check_attr() in order to check for the availability
+ * of optional attributes.
*/
-void kvm_device_access(int fd, int group, uint64_t attr,
- void *val, bool write);
+int kvm_device_access(int fd, int group, uint64_t attr,
+ void *val, bool write, Error **errp);
/**
* kvm_create_device - create a KVM device for the device control API
diff --git a/kvm-all.c b/kvm-all.c
index 44b3cf43cc..ab8262f672 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -23,6 +23,7 @@
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
@@ -2216,8 +2217,8 @@ int kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr)
return kvm_device_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute) ? 0 : 1;
}
-void kvm_device_access(int fd, int group, uint64_t attr,
- void *val, bool write)
+int kvm_device_access(int fd, int group, uint64_t attr,
+ void *val, bool write, Error **errp)
{
struct kvm_device_attr kvmattr;
int err;
@@ -2231,11 +2232,12 @@ void kvm_device_access(int fd, int group, uint64_t attr,
write ? KVM_SET_DEVICE_ATTR : KVM_GET_DEVICE_ATTR,
&kvmattr);
if (err < 0) {
- error_report("KVM_%s_DEVICE_ATTR failed: %s",
- write ? "SET" : "GET", strerror(-err));
- error_printf("Group %d attr 0x%016" PRIx64 "\n", group, attr);
- abort();
+ error_setg_errno(errp, -err,
+ "KVM_%s_DEVICE_ATTR failed: Group %d "
+ "attr 0x%016" PRIx64,
+ write ? "SET" : "GET", group, attr);
}
+ return err;
}
/* Return 1 on success, 0 on failure */