aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2017-07-13 10:47:10 +0100
committerPeter Maydell <peter.maydell@linaro.org>2017-07-13 10:47:10 +0100
commit6e2c46334385c7e295ac883c801c81b4925fb54f (patch)
tree44ddadc2d77a47bb95e77fb5b7463792cb2e598c /hw
parent31fe1c414501047cbb91b695bdccc0068496dcf6 (diff)
parent792dac309c8660306557ba058b8b5a6a75ab3c1f (diff)
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170711' into staging
target-arm queue: * v7M: ignore writes to CONTROL.SPSEL from Thread mode * KVM: Enable in-kernel timers with user space gic * aspeed: Register all watchdogs * hw/misc: Add Exynos4210 Pseudo Random Number Generator # gpg: Signature made Tue 11 Jul 2017 11:28:15 BST # 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-20170711: target-arm: v7M: ignore writes to CONTROL.SPSEL from Thread mode ARM: KVM: Enable in-kernel timers with user space gic aspeed: Register all watchdogs hw/misc: Add Exynos4210 Pseudo Random Number Generator Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/aspeed_soc.c25
-rw-r--r--hw/arm/exynos4210.c4
-rw-r--r--hw/intc/arm_gic.c7
-rw-r--r--hw/misc/Makefile.objs2
-rw-r--r--hw/misc/exynos4210_rng.c277
5 files changed, 306 insertions, 9 deletions
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index 4937e2bc83..3034849c80 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -62,6 +62,7 @@ static const AspeedSoCInfo aspeed_socs[] = {
.spi_bases = aspeed_soc_ast2400_spi_bases,
.fmc_typename = "aspeed.smc.fmc",
.spi_typename = aspeed_soc_ast2400_typenames,
+ .wdts_num = 2,
}, {
.name = "ast2400-a1",
.cpu_model = "arm926",
@@ -72,6 +73,7 @@ static const AspeedSoCInfo aspeed_socs[] = {
.spi_bases = aspeed_soc_ast2400_spi_bases,
.fmc_typename = "aspeed.smc.fmc",
.spi_typename = aspeed_soc_ast2400_typenames,
+ .wdts_num = 2,
}, {
.name = "ast2400",
.cpu_model = "arm926",
@@ -82,6 +84,7 @@ static const AspeedSoCInfo aspeed_socs[] = {
.spi_bases = aspeed_soc_ast2400_spi_bases,
.fmc_typename = "aspeed.smc.fmc",
.spi_typename = aspeed_soc_ast2400_typenames,
+ .wdts_num = 2,
}, {
.name = "ast2500-a1",
.cpu_model = "arm1176",
@@ -92,6 +95,7 @@ static const AspeedSoCInfo aspeed_socs[] = {
.spi_bases = aspeed_soc_ast2500_spi_bases,
.fmc_typename = "aspeed.smc.ast2500-fmc",
.spi_typename = aspeed_soc_ast2500_typenames,
+ .wdts_num = 3,
},
};
@@ -175,9 +179,11 @@ static void aspeed_soc_init(Object *obj)
object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc),
"ram-size", &error_abort);
- object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ASPEED_WDT);
- object_property_add_child(obj, "wdt", OBJECT(&s->wdt), NULL);
- qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default());
+ for (i = 0; i < sc->info->wdts_num; i++) {
+ object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_ASPEED_WDT);
+ object_property_add_child(obj, "wdt[*]", OBJECT(&s->wdt[i]), NULL);
+ qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus_get_default());
+ }
object_initialize(&s->ftgmac100, sizeof(s->ftgmac100), TYPE_FTGMAC100);
object_property_add_child(obj, "ftgmac100", OBJECT(&s->ftgmac100), NULL);
@@ -300,12 +306,15 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
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;
+ for (i = 0; i < sc->info->wdts_num; i++) {
+ object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0,
+ ASPEED_SOC_WDT_BASE + i * 0x20);
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, ASPEED_SOC_WDT_BASE);
/* Net */
qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]);
diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c
index 0050626a69..ee851e3ae5 100644
--- a/hw/arm/exynos4210.c
+++ b/hw/arm/exynos4210.c
@@ -87,6 +87,9 @@
/* Clock controller SFR base address */
#define EXYNOS4210_CLK_BASE_ADDR 0x10030000
+/* PRNG/HASH SFR base address */
+#define EXYNOS4210_RNG_BASE_ADDR 0x10830400
+
/* Display controllers (FIMD) */
#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000
@@ -305,6 +308,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem)
sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
sysbus_create_simple("exynos4210.clk", EXYNOS4210_CLK_BASE_ADDR, NULL);
+ sysbus_create_simple("exynos4210.rng", EXYNOS4210_RNG_BASE_ADDR, NULL);
/* PWM */
sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index b305d9032a..5a0e2a3c1a 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -25,6 +25,7 @@
#include "qom/cpu.h"
#include "qemu/log.h"
#include "trace.h"
+#include "sysemu/kvm.h"
/* #define DEBUG_GIC */
@@ -1412,6 +1413,12 @@ static void arm_gic_realize(DeviceState *dev, Error **errp)
return;
}
+ if (kvm_enabled() && !kvm_arm_supports_user_irq()) {
+ error_setg(errp, "KVM with user space irqchip only works when the "
+ "host kernel supports KVM_CAP_ARM_USER_IRQ");
+ return;
+ }
+
/* This creates distributor and main CPU interface (s->cpuiomem[0]) */
gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops);
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 44e0e79ba7..7e373dbbff 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -28,7 +28,7 @@ obj-$(CONFIG_IVSHMEM) += ivshmem.o
obj-$(CONFIG_REALVIEW) += arm_sysctl.o
obj-$(CONFIG_NSERIES) += cbus.o
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
-obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o exynos4210_clk.o
+obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o exynos4210_clk.o exynos4210_rng.o
obj-$(CONFIG_IMX) += imx_ccm.o
obj-$(CONFIG_IMX) += imx31_ccm.o
obj-$(CONFIG_IMX) += imx25_ccm.o
diff --git a/hw/misc/exynos4210_rng.c b/hw/misc/exynos4210_rng.c
new file mode 100644
index 0000000000..31ebe38e26
--- /dev/null
+++ b/hw/misc/exynos4210_rng.c
@@ -0,0 +1,277 @@
+/*
+ * Exynos4210 Pseudo Random Nubmer Generator Emulation
+ *
+ * Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/random.h"
+#include "hw/sysbus.h"
+#include "qemu/log.h"
+
+#define DEBUG_EXYNOS_RNG 0
+
+#define DPRINTF(fmt, ...) \
+ do { \
+ if (DEBUG_EXYNOS_RNG) { \
+ printf("exynos4210_rng: " fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define TYPE_EXYNOS4210_RNG "exynos4210.rng"
+#define EXYNOS4210_RNG(obj) \
+ OBJECT_CHECK(Exynos4210RngState, (obj), TYPE_EXYNOS4210_RNG)
+
+/*
+ * Exynos4220, PRNG, only polling mode is supported.
+ */
+
+/* RNG_CONTROL_1 register bitfields, reset value: 0x0 */
+#define EXYNOS4210_RNG_CONTROL_1_PRNG 0x8
+#define EXYNOS4210_RNG_CONTROL_1_START_INIT BIT(4)
+/* RNG_STATUS register bitfields, reset value: 0x1 */
+#define EXYNOS4210_RNG_STATUS_PRNG_ERROR BIT(7)
+#define EXYNOS4210_RNG_STATUS_PRNG_DONE BIT(5)
+#define EXYNOS4210_RNG_STATUS_MSG_DONE BIT(4)
+#define EXYNOS4210_RNG_STATUS_PARTIAL_DONE BIT(3)
+#define EXYNOS4210_RNG_STATUS_PRNG_BUSY BIT(2)
+#define EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE BIT(1)
+#define EXYNOS4210_RNG_STATUS_BUFFER_READY BIT(0)
+#define EXYNOS4210_RNG_STATUS_WRITE_MASK (EXYNOS4210_RNG_STATUS_PRNG_DONE \
+ | EXYNOS4210_RNG_STATUS_MSG_DONE \
+ | EXYNOS4210_RNG_STATUS_PARTIAL_DONE)
+
+#define EXYNOS4210_RNG_CONTROL_1 0x0
+#define EXYNOS4210_RNG_STATUS 0x10
+#define EXYNOS4210_RNG_SEED_IN 0x140
+#define EXYNOS4210_RNG_SEED_IN_OFFSET(n) (EXYNOS4210_RNG_SEED_IN + (n * 0x4))
+#define EXYNOS4210_RNG_PRNG 0x160
+#define EXYNOS4210_RNG_PRNG_OFFSET(n) (EXYNOS4210_RNG_PRNG + (n * 0x4))
+
+#define EXYNOS4210_RNG_PRNG_NUM 5
+
+#define EXYNOS4210_RNG_REGS_MEM_SIZE 0x200
+
+typedef struct Exynos4210RngState {
+ SysBusDevice parent_obj;
+ MemoryRegion iomem;
+
+ int32_t randr_value[EXYNOS4210_RNG_PRNG_NUM];
+ /* bits from 0 to EXYNOS4210_RNG_PRNG_NUM if given seed register was set */
+ uint32_t seed_set;
+
+ /* Register values */
+ uint32_t reg_control;
+ uint32_t reg_status;
+} Exynos4210RngState;
+
+static bool exynos4210_rng_seed_ready(const Exynos4210RngState *s)
+{
+ uint32_t mask = MAKE_64BIT_MASK(0, EXYNOS4210_RNG_PRNG_NUM);
+
+ /* Return true if all the seed-set bits are set. */
+ return (s->seed_set & mask) == mask;
+}
+
+static void exynos4210_rng_set_seed(Exynos4210RngState *s, unsigned int i,
+ uint64_t val)
+{
+ /*
+ * We actually ignore the seed and always generate true random numbers.
+ * Theoretically this should not match the device as Exynos has
+ * a Pseudo Random Number Generator but testing shown that it always
+ * generates random numbers regardless of the seed value.
+ */
+ s->seed_set |= BIT(i);
+
+ /* If all seeds were written, update the status to reflect it */
+ if (exynos4210_rng_seed_ready(s)) {
+ s->reg_status |= EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE;
+ } else {
+ s->reg_status &= ~EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE;
+ }
+}
+
+static void exynos4210_rng_run_engine(Exynos4210RngState *s)
+{
+ Error *err = NULL;
+ int ret;
+
+ /* Seed set? */
+ if ((s->reg_status & EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE) == 0) {
+ goto out;
+ }
+
+ /* PRNG engine chosen? */
+ if ((s->reg_control & EXYNOS4210_RNG_CONTROL_1_PRNG) == 0) {
+ goto out;
+ }
+
+ /* PRNG engine started? */
+ if ((s->reg_control & EXYNOS4210_RNG_CONTROL_1_START_INIT) == 0) {
+ goto out;
+ }
+
+ /* Get randoms */
+ ret = qcrypto_random_bytes((uint8_t *)s->randr_value,
+ sizeof(s->randr_value), &err);
+ if (!ret) {
+ /* Notify that PRNG is ready */
+ s->reg_status |= EXYNOS4210_RNG_STATUS_PRNG_DONE;
+ } else {
+ error_report_err(err);
+ }
+
+out:
+ /* Always clear start engine bit */
+ s->reg_control &= ~EXYNOS4210_RNG_CONTROL_1_START_INIT;
+}
+
+static uint64_t exynos4210_rng_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ Exynos4210RngState *s = (Exynos4210RngState *)opaque;
+ uint32_t val = 0;
+
+ assert(size == 4);
+
+ switch (offset) {
+ case EXYNOS4210_RNG_CONTROL_1:
+ val = s->reg_control;
+ break;
+
+ case EXYNOS4210_RNG_STATUS:
+ val = s->reg_status;
+ break;
+
+ case EXYNOS4210_RNG_PRNG_OFFSET(0):
+ case EXYNOS4210_RNG_PRNG_OFFSET(1):
+ case EXYNOS4210_RNG_PRNG_OFFSET(2):
+ case EXYNOS4210_RNG_PRNG_OFFSET(3):
+ case EXYNOS4210_RNG_PRNG_OFFSET(4):
+ val = s->randr_value[(offset - EXYNOS4210_RNG_PRNG_OFFSET(0)) / 4];
+ DPRINTF("returning random @0x%" HWADDR_PRIx ": 0x%" PRIx32 "\n",
+ offset, val);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad read offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ }
+
+ return val;
+}
+
+static void exynos4210_rng_write(void *opaque, hwaddr offset,
+ uint64_t val, unsigned size)
+{
+ Exynos4210RngState *s = (Exynos4210RngState *)opaque;
+
+ assert(size == 4);
+
+ switch (offset) {
+ case EXYNOS4210_RNG_CONTROL_1:
+ DPRINTF("RNG_CONTROL_1 = 0x%" PRIx64 "\n", val);
+ s->reg_control = val;
+ exynos4210_rng_run_engine(s);
+ break;
+
+ case EXYNOS4210_RNG_STATUS:
+ /* For clearing status fields */
+ s->reg_status &= ~EXYNOS4210_RNG_STATUS_WRITE_MASK;
+ s->reg_status |= val & EXYNOS4210_RNG_STATUS_WRITE_MASK;
+ break;
+
+ case EXYNOS4210_RNG_SEED_IN_OFFSET(0):
+ case EXYNOS4210_RNG_SEED_IN_OFFSET(1):
+ case EXYNOS4210_RNG_SEED_IN_OFFSET(2):
+ case EXYNOS4210_RNG_SEED_IN_OFFSET(3):
+ case EXYNOS4210_RNG_SEED_IN_OFFSET(4):
+ exynos4210_rng_set_seed(s,
+ (offset - EXYNOS4210_RNG_SEED_IN_OFFSET(0)) / 4,
+ val);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad write offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ }
+}
+
+static const MemoryRegionOps exynos4210_rng_ops = {
+ .read = exynos4210_rng_read,
+ .write = exynos4210_rng_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void exynos4210_rng_reset(DeviceState *dev)
+{
+ Exynos4210RngState *s = EXYNOS4210_RNG(dev);
+
+ s->reg_control = 0;
+ s->reg_status = EXYNOS4210_RNG_STATUS_BUFFER_READY;
+ memset(s->randr_value, 0, sizeof(s->randr_value));
+ s->seed_set = 0;
+}
+
+static void exynos4210_rng_init(Object *obj)
+{
+ Exynos4210RngState *s = EXYNOS4210_RNG(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
+
+ memory_region_init_io(&s->iomem, obj, &exynos4210_rng_ops, s,
+ TYPE_EXYNOS4210_RNG, EXYNOS4210_RNG_REGS_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+}
+
+static const VMStateDescription exynos4210_rng_vmstate = {
+ .name = TYPE_EXYNOS4210_RNG,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32_ARRAY(randr_value, Exynos4210RngState,
+ EXYNOS4210_RNG_PRNG_NUM),
+ VMSTATE_UINT32(seed_set, Exynos4210RngState),
+ VMSTATE_UINT32(reg_status, Exynos4210RngState),
+ VMSTATE_UINT32(reg_control, Exynos4210RngState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void exynos4210_rng_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = exynos4210_rng_reset;
+ dc->vmsd = &exynos4210_rng_vmstate;
+}
+
+static const TypeInfo exynos4210_rng_info = {
+ .name = TYPE_EXYNOS4210_RNG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210RngState),
+ .instance_init = exynos4210_rng_init,
+ .class_init = exynos4210_rng_class_init,
+};
+
+static void exynos4210_rng_register(void)
+{
+ type_register_static(&exynos4210_rng_info);
+}
+
+type_init(exynos4210_rng_register)