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

target-arm queue:
 * implement FCMA and RDM v8.1 and v8.3 instructions
 * enable Cortex-M33 v8M core, and provide new mps2-an505 board model
   that uses it
 * decodetree: Propagate return value from translate subroutines
 * xlnx-zynqmp: Implement the RTC device

# gpg: Signature made Fri 02 Mar 2018 11:05:40 GMT
# gpg:                using RSA key 3C2525ED14360CDE
# 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-20180302: (39 commits)
  target/arm: Enable ARM_FEATURE_V8_FCMA
  target/arm: Decode t32 simd 3reg and 2reg_scalar extension
  target/arm: Decode aa32 armv8.3 2-reg-index
  target/arm: Decode aa32 armv8.3 3-same
  target/arm: Decode aa64 armv8.3 fcmla
  target/arm: Decode aa64 armv8.3 fcadd
  target/arm: Add ARM_FEATURE_V8_FCMA
  target/arm: Enable ARM_FEATURE_V8_RDM
  target/arm: Decode aa32 armv8.1 two reg and a scalar
  target/arm: Decode aa32 armv8.1 three same
  target/arm: Decode aa64 armv8.1 scalar/vector x indexed element
  target/arm: Decode aa64 armv8.1 three same extra
  target/arm: Decode aa64 armv8.1 scalar three same extra
  target/arm: Refactor disas_simd_indexed size checks
  target/arm: Refactor disas_simd_indexed decode
  target/arm: Add ARM_FEATURE_V8_RDM
  mps2-an505: New board model: MPS2 with AN505 Cortex-M33 FPGA image
  hw/arm/iotkit: Model Arm IOT Kit
  hw/misc/iotkit-secctl: Add remaining simple registers
  hw/misc/iotkit-secctl: Add handling for PPCs
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2018-03-02 14:37:10 +00:00
45 changed files with 4668 additions and 200 deletions
+5
View File
@@ -102,8 +102,13 @@ CONFIG_STM32F205_SOC=y
CONFIG_CMSDK_APB_TIMER=y
CONFIG_CMSDK_APB_UART=y
CONFIG_MPS2_FPGAIO=y
CONFIG_MPS2_SCC=y
CONFIG_TZ_PPC=y
CONFIG_IOTKIT=y
CONFIG_IOTKIT_SECCTL=y
CONFIG_VERSATILE_PCI=y
CONFIG_VERSATILE_I2C=y
+2
View File
@@ -19,4 +19,6 @@ obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o
obj-$(CONFIG_MPS2) += mps2.o
obj-$(CONFIG_MPS2) += mps2-tz.o
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
obj-$(CONFIG_IOTKIT) += iotkit.o
+32 -3
View File
@@ -19,6 +19,7 @@
#include "sysemu/qtest.h"
#include "qemu/error-report.h"
#include "exec/address-spaces.h"
#include "target/arm/idau.h"
/* Bitbanded IO. Each word corresponds to a single bit. */
@@ -162,6 +163,21 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory",
&error_abort);
if (object_property_find(OBJECT(s->cpu), "idau", NULL)) {
object_property_set_link(OBJECT(s->cpu), s->idau, "idau", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
}
if (object_property_find(OBJECT(s->cpu), "init-svtor", NULL)) {
object_property_set_uint(OBJECT(s->cpu), s->init_svtor,
"init-svtor", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
}
object_property_set_bool(OBJECT(s->cpu), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
@@ -217,6 +233,8 @@ static Property armv7m_properties[] = {
DEFINE_PROP_STRING("cpu-type", ARMv7MState, cpu_type),
DEFINE_PROP_LINK("memory", ARMv7MState, board_memory, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *),
DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -270,6 +288,9 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size)
uint64_t entry;
uint64_t lowaddr;
int big_endian;
AddressSpace *as;
int asidx;
CPUState *cs = CPU(cpu);
#ifdef TARGET_WORDS_BIGENDIAN
big_endian = 1;
@@ -282,11 +303,19 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size)
exit(1);
}
if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) {
asidx = ARMASIdx_S;
} else {
asidx = ARMASIdx_NS;
}
as = cpu_get_address_space(cs, asidx);
if (kernel_filename) {
image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
NULL, big_endian, EM_ARM, 1, 0);
image_size = load_elf_as(kernel_filename, NULL, NULL, &entry, &lowaddr,
NULL, big_endian, EM_ARM, 1, 0, as);
if (image_size < 0) {
image_size = load_image_targphys(kernel_filename, 0, mem_size);
image_size = load_image_targphys_as(kernel_filename, 0,
mem_size, as);
lowaddr = 0;
}
if (image_size < 0) {
+76 -43
View File
@@ -36,6 +36,25 @@
#define ARM64_TEXT_OFFSET_OFFSET 8
#define ARM64_MAGIC_OFFSET 56
static AddressSpace *arm_boot_address_space(ARMCPU *cpu,
const struct arm_boot_info *info)
{
/* Return the address space to use for bootloader reads and writes.
* We prefer the secure address space if the CPU has it and we're
* going to boot the guest into it.
*/
int asidx;
CPUState *cs = CPU(cpu);
if (arm_feature(&cpu->env, ARM_FEATURE_EL3) && info->secure_boot) {
asidx = ARMASIdx_S;
} else {
asidx = ARMASIdx_NS;
}
return cpu_get_address_space(cs, asidx);
}
typedef enum {
FIXUP_NONE = 0, /* do nothing */
FIXUP_TERMINATOR, /* end of insns */
@@ -125,7 +144,8 @@ static const ARMInsnFixup smpboot[] = {
};
static void write_bootloader(const char *name, hwaddr addr,
const ARMInsnFixup *insns, uint32_t *fixupcontext)
const ARMInsnFixup *insns, uint32_t *fixupcontext,
AddressSpace *as)
{
/* Fix up the specified bootloader fragment and write it into
* guest memory using rom_add_blob_fixed(). fixupcontext is
@@ -164,7 +184,7 @@ static void write_bootloader(const char *name, hwaddr addr,
code[i] = tswap32(insn);
}
rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr);
rom_add_blob_fixed_as(name, code, len * sizeof(uint32_t), addr, as);
g_free(code);
}
@@ -173,6 +193,7 @@ static void default_write_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
uint32_t fixupcontext[FIXUP_MAX];
AddressSpace *as = arm_boot_address_space(cpu, info);
fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr;
fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr;
@@ -183,13 +204,14 @@ static void default_write_secondary(ARMCPU *cpu,
}
write_bootloader("smpboot", info->smp_loader_start,
smpboot, fixupcontext);
smpboot, fixupcontext, as);
}
void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
const struct arm_boot_info *info,
hwaddr mvbar_addr)
{
AddressSpace *as = arm_boot_address_space(cpu, info);
int n;
uint32_t mvbar_blob[] = {
/* mvbar_addr: secure monitor vectors
@@ -227,22 +249,23 @@ void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
for (n = 0; n < ARRAY_SIZE(mvbar_blob); n++) {
mvbar_blob[n] = tswap32(mvbar_blob[n]);
}
rom_add_blob_fixed("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob),
mvbar_addr);
rom_add_blob_fixed_as("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob),
mvbar_addr, as);
for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
board_setup_blob[n] = tswap32(board_setup_blob[n]);
}
rom_add_blob_fixed("board-setup", board_setup_blob,
sizeof(board_setup_blob), info->board_setup_addr);
rom_add_blob_fixed_as("board-setup", board_setup_blob,
sizeof(board_setup_blob), info->board_setup_addr, as);
}
static void default_reset_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
AddressSpace *as = arm_boot_address_space(cpu, info);
CPUState *cs = CPU(cpu);
address_space_stl_notdirty(&address_space_memory, info->smp_bootreg_addr,
address_space_stl_notdirty(as, info->smp_bootreg_addr,
0, MEMTXATTRS_UNSPECIFIED, NULL);
cpu_set_pc(cs, info->smp_loader_start);
}
@@ -253,12 +276,12 @@ static inline bool have_dtb(const struct arm_boot_info *info)
}
#define WRITE_WORD(p, value) do { \
address_space_stl_notdirty(&address_space_memory, p, value, \
address_space_stl_notdirty(as, p, value, \
MEMTXATTRS_UNSPECIFIED, NULL); \
p += 4; \
} while (0)
static void set_kernel_args(const struct arm_boot_info *info)
static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as)
{
int initrd_size = info->initrd_size;
hwaddr base = info->loader_start;
@@ -289,8 +312,9 @@ static void set_kernel_args(const struct arm_boot_info *info)
int cmdline_size;
cmdline_size = strlen(info->kernel_cmdline);
cpu_physical_memory_write(p + 8, info->kernel_cmdline,
cmdline_size + 1);
address_space_write(as, p + 8, MEMTXATTRS_UNSPECIFIED,
(const uint8_t *)info->kernel_cmdline,
cmdline_size + 1);
cmdline_size = (cmdline_size >> 2) + 1;
WRITE_WORD(p, cmdline_size + 2);
WRITE_WORD(p, 0x54410009);
@@ -304,7 +328,8 @@ static void set_kernel_args(const struct arm_boot_info *info)
atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3;
WRITE_WORD(p, (atag_board_len + 8) >> 2);
WRITE_WORD(p, 0x414f4d50);
cpu_physical_memory_write(p, atag_board_buf, atag_board_len);
address_space_write(as, p, MEMTXATTRS_UNSPECIFIED,
atag_board_buf, atag_board_len);
p += atag_board_len;
}
/* ATAG_END */
@@ -312,7 +337,8 @@ static void set_kernel_args(const struct arm_boot_info *info)
WRITE_WORD(p, 0);
}
static void set_kernel_args_old(const struct arm_boot_info *info)
static void set_kernel_args_old(const struct arm_boot_info *info,
AddressSpace *as)
{
hwaddr p;
const char *s;
@@ -380,7 +406,8 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
}
s = info->kernel_cmdline;
if (s) {
cpu_physical_memory_write(p, s, strlen(s) + 1);
address_space_write(as, p, MEMTXATTRS_UNSPECIFIED,
(const uint8_t *)s, strlen(s) + 1);
} else {
WRITE_WORD(p, 0);
}
@@ -454,6 +481,7 @@ static void fdt_add_psci_node(void *fdt)
* @addr: the address to load the image at
* @binfo: struct describing the boot environment
* @addr_limit: upper limit of the available memory area at @addr
* @as: address space to load image to
*
* Load a device tree supplied by the machine or by the user with the
* '-dtb' command line option, and put it at offset @addr in target
@@ -470,7 +498,7 @@ static void fdt_add_psci_node(void *fdt)
* Note: Must not be called unless have_dtb(binfo) is true.
*/
static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
hwaddr addr_limit)
hwaddr addr_limit, AddressSpace *as)
{
void *fdt = NULL;
int size, rc;
@@ -616,7 +644,7 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
/* Put the DTB into the memory map as a ROM image: this will ensure
* the DTB is copied again upon reset, even if addr points into RAM.
*/
rom_add_blob_fixed("dtb", fdt, size, addr);
rom_add_blob_fixed_as("dtb", fdt, size, addr, as);
g_free(fdt);
@@ -703,13 +731,15 @@ static void do_cpu_reset(void *opaque)
}
if (cs == first_cpu) {
AddressSpace *as = arm_boot_address_space(cpu, info);
cpu_set_pc(cs, info->loader_start);
if (!have_dtb(info)) {
if (old_param) {
set_kernel_args_old(info);
set_kernel_args_old(info, as);
} else {
set_kernel_args(info);
set_kernel_args(info, as);
}
}
} else {
@@ -784,7 +814,7 @@ static int do_arm_linux_init(Object *obj, void *opaque)
static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
uint64_t *lowaddr, uint64_t *highaddr,
int elf_machine)
int elf_machine, AddressSpace *as)
{
bool elf_is64;
union {
@@ -827,9 +857,9 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
}
}
ret = load_elf(info->kernel_filename, NULL, NULL,
pentry, lowaddr, highaddr, big_endian, elf_machine,
1, data_swab);
ret = load_elf_as(info->kernel_filename, NULL, NULL,
pentry, lowaddr, highaddr, big_endian, elf_machine,
1, data_swab, as);
if (ret <= 0) {
/* The header loaded but the image didn't */
exit(1);
@@ -839,7 +869,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
}
static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
hwaddr *entry)
hwaddr *entry, AddressSpace *as)
{
hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR;
uint8_t *buffer;
@@ -874,7 +904,7 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
}
*entry = mem_base + kernel_load_offset;
rom_add_blob_fixed(filename, buffer, size, *entry);
rom_add_blob_fixed_as(filename, buffer, size, *entry, as);
g_free(buffer);
@@ -896,6 +926,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
ARMCPU *cpu = n->cpu;
struct arm_boot_info *info =
container_of(n, struct arm_boot_info, load_kernel_notifier);
AddressSpace *as = arm_boot_address_space(cpu, info);
/* The board code is not supposed to set secure_board_setup unless
* running its code in secure mode is actually possible, and KVM
@@ -913,7 +944,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
* the kernel is supposed to be loaded by the bootloader), copy the
* DTB to the base of RAM for the bootloader to pick up.
*/
if (load_dtb(info->loader_start, info, 0) < 0) {
if (load_dtb(info->loader_start, info, 0, as) < 0) {
exit(1);
}
}
@@ -988,7 +1019,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
/* Assume that raw images are linux kernels, and ELF images are not. */
kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr,
&elf_high_addr, elf_machine);
&elf_high_addr, elf_machine, as);
if (kernel_size > 0 && have_dtb(info)) {
/* If there is still some room left at the base of RAM, try and put
* the DTB there like we do for images loaded with -bios or -pflash.
@@ -1001,25 +1032,26 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
if (elf_low_addr < info->loader_start) {
elf_low_addr = 0;
}
if (load_dtb(info->loader_start, info, elf_low_addr) < 0) {
if (load_dtb(info->loader_start, info, elf_low_addr, as) < 0) {
exit(1);
}
}
}
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
&is_linux, NULL, NULL);
kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL,
&is_linux, NULL, NULL, as);
}
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
kernel_size = load_aarch64_image(info->kernel_filename,
info->loader_start, &entry);
info->loader_start, &entry, as);
is_linux = 1;
} else if (kernel_size < 0) {
/* 32-bit ARM */
entry = info->loader_start + KERNEL_LOAD_ADDR;
kernel_size = load_image_targphys(info->kernel_filename, entry,
info->ram_size - KERNEL_LOAD_ADDR);
kernel_size = load_image_targphys_as(info->kernel_filename, entry,
info->ram_size - KERNEL_LOAD_ADDR,
as);
is_linux = 1;
}
if (kernel_size < 0) {
@@ -1031,15 +1063,16 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
uint32_t fixupcontext[FIXUP_MAX];
if (info->initrd_filename) {
initrd_size = load_ramdisk(info->initrd_filename,
info->initrd_start,
info->ram_size -
info->initrd_start);
initrd_size = load_ramdisk_as(info->initrd_filename,
info->initrd_start,
info->ram_size - info->initrd_start,
as);
if (initrd_size < 0) {
initrd_size = load_image_targphys(info->initrd_filename,
info->initrd_start,
info->ram_size -
info->initrd_start);
initrd_size = load_image_targphys_as(info->initrd_filename,
info->initrd_start,
info->ram_size -
info->initrd_start,
as);
}
if (initrd_size < 0) {
error_report("could not load initrd '%s'",
@@ -1080,7 +1113,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
/* Place the DTB after the initrd in memory with alignment. */
dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align);
if (load_dtb(dtb_start, info, 0) < 0) {
if (load_dtb(dtb_start, info, 0, as) < 0) {
exit(1);
}
fixupcontext[FIXUP_ARGPTR] = dtb_start;
@@ -1096,7 +1129,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
fixupcontext[FIXUP_ENTRYPOINT] = entry;
write_bootloader("bootloader", info->loader_start,
primary_loader, fixupcontext);
primary_loader, fixupcontext, as);
if (info->nb_cpus > 1) {
info->write_secondary_boot(cpu, info);
+598
View File
File diff suppressed because it is too large Load Diff
+503
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -53,6 +53,9 @@
#define IPI_ADDR 0xFF300000
#define IPI_IRQ 64
#define RTC_ADDR 0xffa60000
#define RTC_IRQ 26
#define SDHCI_CAPABILITIES 0x280737ec6481 /* Datasheet: UG1085 (v1.7) */
static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
@@ -191,6 +194,9 @@ static void xlnx_zynqmp_init(Object *obj)
object_initialize(&s->ipi, sizeof(s->ipi), TYPE_XLNX_ZYNQMP_IPI);
qdev_set_parent_bus(DEVICE(&s->ipi), sysbus_get_default());
object_initialize(&s->rtc, sizeof(s->rtc), TYPE_XLNX_ZYNQMP_RTC);
qdev_set_parent_bus(DEVICE(&s->rtc), sysbus_get_default());
}
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
@@ -476,6 +482,14 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->ipi), 0, IPI_ADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->ipi), 0, gic_spi[IPI_IRQ]);
object_property_set_bool(OBJECT(&s->rtc), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]);
}
static Property xlnx_zynqmp_props[] = {
+1
View File
@@ -18,6 +18,7 @@ common-obj-$(CONFIG_FITLOADER) += loader-fit.o
common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
common-obj-$(CONFIG_SOFTMMU) += register.o
common-obj-$(CONFIG_SOFTMMU) += or-irq.o
common-obj-$(CONFIG_SOFTMMU) += split-irq.o
common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o
obj-$(CONFIG_SOFTMMU) += generic-loader.o
+7 -1
View File
@@ -729,9 +729,15 @@ int load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr,
/* Load a ramdisk. */
int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz)
{
return load_ramdisk_as(filename, addr, max_sz, NULL);
}
int load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz,
AddressSpace *as)
{
return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK,
NULL, NULL, NULL);
NULL, NULL, as);
}
/* Load a gzip-compressed kernel to a dynamically allocated buffer. */
+5 -3
View File
@@ -385,15 +385,17 @@ static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev,
return ngl;
}
void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler,
const char *name, int n)
void qdev_init_gpio_in_named_with_opaque(DeviceState *dev,
qemu_irq_handler handler,
void *opaque,
const char *name, int n)
{
int i;
NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
assert(gpio_list->num_out == 0 || !name);
gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
dev, n);
opaque, n);
if (!name) {
name = "unnamed-gpio-in";
+89
View File
@@ -0,0 +1,89 @@
/*
* IRQ splitter device.
*
* Copyright (c) 2018 Linaro Limited.
* Written by Peter Maydell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "hw/core/split-irq.h"
#include "qapi/error.h"
static void split_irq_handler(void *opaque, int n, int level)
{
SplitIRQ *s = SPLIT_IRQ(opaque);
int i;
for (i = 0; i < s->num_lines; i++) {
qemu_set_irq(s->out_irq[i], level);
}
}
static void split_irq_init(Object *obj)
{
qdev_init_gpio_in(DEVICE(obj), split_irq_handler, 1);
}
static void split_irq_realize(DeviceState *dev, Error **errp)
{
SplitIRQ *s = SPLIT_IRQ(dev);
if (s->num_lines < 1 || s->num_lines >= MAX_SPLIT_LINES) {
error_setg(errp,
"IRQ splitter number of lines %d is not between 1 and %d",
s->num_lines, MAX_SPLIT_LINES);
return;
}
qdev_init_gpio_out(dev, s->out_irq, s->num_lines);
}
static Property split_irq_properties[] = {
DEFINE_PROP_UINT16("num-lines", SplitIRQ, num_lines, 1),
DEFINE_PROP_END_OF_LIST(),
};
static void split_irq_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
/* No state to reset or migrate */
dc->props = split_irq_properties;
dc->realize = split_irq_realize;
/* Reason: Needs to be wired up to work */
dc->user_creatable = false;
}
static const TypeInfo split_irq_type_info = {
.name = TYPE_SPLIT_IRQ,
.parent = TYPE_DEVICE,
.instance_size = sizeof(SplitIRQ),
.instance_init = split_irq_init,
.class_init = split_irq_class_init,
};
static void split_irq_register_types(void)
{
type_register_static(&split_irq_type_info);
}
type_init(split_irq_register_types)
+4
View File
@@ -58,8 +58,12 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o
obj-$(CONFIG_MIPS_CPS) += mips_cpc.o
obj-$(CONFIG_MIPS_ITU) += mips_itu.o
obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o
obj-$(CONFIG_MPS2_SCC) += mps2-scc.o
obj-$(CONFIG_TZ_PPC) += tz-ppc.o
obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
obj-$(CONFIG_AUX) += auxbus.o
File diff suppressed because it is too large Load Diff
+176
View File
@@ -0,0 +1,176 @@
/*
* ARM MPS2 AN505 FPGAIO emulation
*
* Copyright (c) 2018 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
/* This is a model of the "FPGA system control and I/O" block found
* in the AN505 FPGA image for the MPS2 devboard.
* It is documented in AN505:
* http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "trace.h"
#include "hw/sysbus.h"
#include "hw/registerfields.h"
#include "hw/misc/mps2-fpgaio.h"
REG32(LED0, 0)
REG32(BUTTON, 8)
REG32(CLK1HZ, 0x10)
REG32(CLK100HZ, 0x14)
REG32(COUNTER, 0x18)
REG32(PRESCALE, 0x1c)
REG32(PSCNTR, 0x20)
REG32(MISC, 0x4c)
static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
{
MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
uint64_t r;
switch (offset) {
case A_LED0:
r = s->led0;
break;
case A_BUTTON:
/* User-pressable board buttons. We don't model that, so just return
* zeroes.
*/
r = 0;
break;
case A_PRESCALE:
r = s->prescale;
break;
case A_MISC:
r = s->misc;
break;
case A_CLK1HZ:
case A_CLK100HZ:
case A_COUNTER:
case A_PSCNTR:
/* These are all upcounters of various frequencies. */
qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n");
r = 0;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"MPS2 FPGAIO read: bad offset %x\n", (int) offset);
r = 0;
break;
}
trace_mps2_fpgaio_read(offset, r, size);
return r;
}
static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
trace_mps2_fpgaio_write(offset, value, size);
switch (offset) {
case A_LED0:
/* LED bits [1:0] control board LEDs. We don't currently have
* a mechanism for displaying this graphically, so use a trace event.
*/
trace_mps2_fpgaio_leds(value & 0x02 ? '*' : '.',
value & 0x01 ? '*' : '.');
s->led0 = value & 0x3;
break;
case A_PRESCALE:
s->prescale = value;
break;
case A_MISC:
/* These are control bits for some of the other devices on the
* board (SPI, CLCD, etc). We don't implement that yet, so just
* make the bits read as written.
*/
qemu_log_mask(LOG_UNIMP,
"MPS2 FPGAIO: MISC control bits unimplemented\n");
s->misc = value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset);
break;
}
}
static const MemoryRegionOps mps2_fpgaio_ops = {
.read = mps2_fpgaio_read,
.write = mps2_fpgaio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void mps2_fpgaio_reset(DeviceState *dev)
{
MPS2FPGAIO *s = MPS2_FPGAIO(dev);
trace_mps2_fpgaio_reset();
s->led0 = 0;
s->prescale = 0;
s->misc = 0;
}
static void mps2_fpgaio_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MPS2FPGAIO *s = MPS2_FPGAIO(obj);
memory_region_init_io(&s->iomem, obj, &mps2_fpgaio_ops, s,
"mps2-fpgaio", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
}
static const VMStateDescription mps2_fpgaio_vmstate = {
.name = "mps2-fpgaio",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(led0, MPS2FPGAIO),
VMSTATE_UINT32(prescale, MPS2FPGAIO),
VMSTATE_UINT32(misc, MPS2FPGAIO),
VMSTATE_END_OF_LIST()
}
};
static Property mps2_fpgaio_properties[] = {
/* Frequency of the prescale counter */
DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000),
DEFINE_PROP_END_OF_LIST(),
};
static void mps2_fpgaio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &mps2_fpgaio_vmstate;
dc->reset = mps2_fpgaio_reset;
dc->props = mps2_fpgaio_properties;
}
static const TypeInfo mps2_fpgaio_info = {
.name = TYPE_MPS2_FPGAIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MPS2FPGAIO),
.instance_init = mps2_fpgaio_init,
.class_init = mps2_fpgaio_class_init,
};
static void mps2_fpgaio_register_types(void)
{
type_register_static(&mps2_fpgaio_info);
}
type_init(mps2_fpgaio_register_types);
+24
View File
@@ -62,6 +62,12 @@ mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2,
mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32
mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32
# hw/misc/mps2_fpgaio.c
mps2_fpgaio_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
mps2_fpgaio_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
mps2_fpgaio_reset(void) "MPS2 FPGAIO: reset"
mps2_fpgaio_leds(char led1, char led0) "MPS2 FPGAIO LEDs: %c%c"
# hw/misc/msf2-sysreg.c
msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32
msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32
@@ -77,3 +83,21 @@ mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d co
mos6522_set_sr_int(void) "set sr_int"
mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
# hw/misc/tz-ppc.c
tz_ppc_reset(void) "TZ PPC: reset"
tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d"
tz_ppc_cfg_ap(int n, int level) "TZ PPC: cfg_ap[%d] = %d"
tz_ppc_cfg_sec_resp(int level) "TZ PPC: cfg_sec_resp = %d"
tz_ppc_irq_enable(int level) "TZ PPC: int_enable = %d"
tz_ppc_irq_clear(int level) "TZ PPC: int_clear = %d"
tz_ppc_update_irq(int level) "TZ PPC: setting irq line to %d"
tz_ppc_read_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " read (secure %d user %d) blocked"
tz_ppc_write_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " write (secure %d user %d) blocked"
# hw/misc/iotkit-secctl.c
iotkit_secctl_s_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs read: offset 0x%x data 0x%" PRIx64 " size %u"
iotkit_secctl_s_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs write: offset 0x%x data 0x%" PRIx64 " size %u"
iotkit_secctl_ns_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs read: offset 0x%x data 0x%" PRIx64 " size %u"
iotkit_secctl_ns_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs write: offset 0x%x data 0x%" PRIx64 " size %u"
iotkit_secctl_reset(void) "IoTKit SecCtl: reset"
+302
View File
@@ -0,0 +1,302 @@
/*
* ARM TrustZone peripheral protection controller emulation
*
* Copyright (c) 2018 Linaro Limited
* Written by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or
* (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "trace.h"
#include "hw/sysbus.h"
#include "hw/registerfields.h"
#include "hw/misc/tz-ppc.h"
static void tz_ppc_update_irq(TZPPC *s)
{
bool level = s->irq_status && s->irq_enable;
trace_tz_ppc_update_irq(level);
qemu_set_irq(s->irq, level);
}
static void tz_ppc_cfg_nonsec(void *opaque, int n, int level)
{
TZPPC *s = TZ_PPC(opaque);
assert(n < TZ_NUM_PORTS);
trace_tz_ppc_cfg_nonsec(n, level);
s->cfg_nonsec[n] = level;
}
static void tz_ppc_cfg_ap(void *opaque, int n, int level)
{
TZPPC *s = TZ_PPC(opaque);
assert(n < TZ_NUM_PORTS);
trace_tz_ppc_cfg_ap(n, level);
s->cfg_ap[n] = level;
}
static void tz_ppc_cfg_sec_resp(void *opaque, int n, int level)
{
TZPPC *s = TZ_PPC(opaque);
trace_tz_ppc_cfg_sec_resp(level);
s->cfg_sec_resp = level;
}
static void tz_ppc_irq_enable(void *opaque, int n, int level)
{
TZPPC *s = TZ_PPC(opaque);
trace_tz_ppc_irq_enable(level);
s->irq_enable = level;
tz_ppc_update_irq(s);
}
static void tz_ppc_irq_clear(void *opaque, int n, int level)
{
TZPPC *s = TZ_PPC(opaque);
trace_tz_ppc_irq_clear(level);
s->irq_clear = level;
if (level) {
s->irq_status = false;
tz_ppc_update_irq(s);
}
}
static bool tz_ppc_check(TZPPC *s, int n, MemTxAttrs attrs)
{
/* Check whether to allow an access to port n; return true if
* the check passes, and false if the transaction must be blocked.
* If the latter, the caller must check cfg_sec_resp to determine
* whether to abort or RAZ/WI the transaction.
* The checks are:
* + nonsec_mask suppresses any check of the secure attribute
* + otherwise, block if cfg_nonsec is 1 and transaction is secure,
* or if cfg_nonsec is 0 and transaction is non-secure
* + block if transaction is usermode and cfg_ap is 0
*/
if ((attrs.secure == s->cfg_nonsec[n] && !(s->nonsec_mask & (1 << n))) ||
(attrs.user && !s->cfg_ap[n])) {
/* Block the transaction. */
if (!s->irq_clear) {
/* Note that holding irq_clear high suppresses interrupts */
s->irq_status = true;
tz_ppc_update_irq(s);
}
return false;
}
return true;
}
static MemTxResult tz_ppc_read(void *opaque, hwaddr addr, uint64_t *pdata,
unsigned size, MemTxAttrs attrs)
{
TZPPCPort *p = opaque;
TZPPC *s = p->ppc;
int n = p - s->port;
AddressSpace *as = &p->downstream_as;
uint64_t data;
MemTxResult res;
if (!tz_ppc_check(s, n, attrs)) {
trace_tz_ppc_read_blocked(n, addr, attrs.secure, attrs.user);
if (s->cfg_sec_resp) {
return MEMTX_ERROR;
} else {
*pdata = 0;
return MEMTX_OK;
}
}
switch (size) {
case 1:
data = address_space_ldub(as, addr, attrs, &res);
break;
case 2:
data = address_space_lduw_le(as, addr, attrs, &res);
break;
case 4:
data = address_space_ldl_le(as, addr, attrs, &res);
break;
case 8:
data = address_space_ldq_le(as, addr, attrs, &res);
break;
default:
g_assert_not_reached();
}
*pdata = data;
return res;
}
static MemTxResult tz_ppc_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size, MemTxAttrs attrs)
{
TZPPCPort *p = opaque;
TZPPC *s = p->ppc;
AddressSpace *as = &p->downstream_as;
int n = p - s->port;
MemTxResult res;
if (!tz_ppc_check(s, n, attrs)) {
trace_tz_ppc_write_blocked(n, addr, attrs.secure, attrs.user);
if (s->cfg_sec_resp) {
return MEMTX_ERROR;
} else {
return MEMTX_OK;
}
}
switch (size) {
case 1:
address_space_stb(as, addr, val, attrs, &res);
break;
case 2:
address_space_stw_le(as, addr, val, attrs, &res);
break;
case 4:
address_space_stl_le(as, addr, val, attrs, &res);
break;
case 8:
address_space_stq_le(as, addr, val, attrs, &res);
break;
default:
g_assert_not_reached();
}
return res;
}
static const MemoryRegionOps tz_ppc_ops = {
.read_with_attrs = tz_ppc_read,
.write_with_attrs = tz_ppc_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void tz_ppc_reset(DeviceState *dev)
{
TZPPC *s = TZ_PPC(dev);
trace_tz_ppc_reset();
s->cfg_sec_resp = false;
memset(s->cfg_nonsec, 0, sizeof(s->cfg_nonsec));
memset(s->cfg_ap, 0, sizeof(s->cfg_ap));
}
static void tz_ppc_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
TZPPC *s = TZ_PPC(obj);
qdev_init_gpio_in_named(dev, tz_ppc_cfg_nonsec, "cfg_nonsec", TZ_NUM_PORTS);
qdev_init_gpio_in_named(dev, tz_ppc_cfg_ap, "cfg_ap", TZ_NUM_PORTS);
qdev_init_gpio_in_named(dev, tz_ppc_cfg_sec_resp, "cfg_sec_resp", 1);
qdev_init_gpio_in_named(dev, tz_ppc_irq_enable, "irq_enable", 1);
qdev_init_gpio_in_named(dev, tz_ppc_irq_clear, "irq_clear", 1);
qdev_init_gpio_out_named(dev, &s->irq, "irq", 1);
}
static void tz_ppc_realize(DeviceState *dev, Error **errp)
{
Object *obj = OBJECT(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
TZPPC *s = TZ_PPC(dev);
int i;
/* We can't create the upstream end of the port until realize,
* as we don't know the size of the MR used as the downstream until then.
*/
for (i = 0; i < TZ_NUM_PORTS; i++) {
TZPPCPort *port = &s->port[i];
char *name;
uint64_t size;
if (!port->downstream) {
continue;
}
name = g_strdup_printf("tz-ppc-port[%d]", i);
port->ppc = s;
address_space_init(&port->downstream_as, port->downstream, name);
size = memory_region_size(port->downstream);
memory_region_init_io(&port->upstream, obj, &tz_ppc_ops,
port, name, size);
sysbus_init_mmio(sbd, &port->upstream);
g_free(name);
}
}
static const VMStateDescription tz_ppc_vmstate = {
.name = "tz-ppc",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_BOOL_ARRAY(cfg_nonsec, TZPPC, 16),
VMSTATE_BOOL_ARRAY(cfg_ap, TZPPC, 16),
VMSTATE_BOOL(cfg_sec_resp, TZPPC),
VMSTATE_BOOL(irq_enable, TZPPC),
VMSTATE_BOOL(irq_clear, TZPPC),
VMSTATE_BOOL(irq_status, TZPPC),
VMSTATE_END_OF_LIST()
}
};
#define DEFINE_PORT(N) \
DEFINE_PROP_LINK("port[" #N "]", TZPPC, port[N].downstream, \
TYPE_MEMORY_REGION, MemoryRegion *)
static Property tz_ppc_properties[] = {
DEFINE_PROP_UINT32("NONSEC_MASK", TZPPC, nonsec_mask, 0),
DEFINE_PORT(0),
DEFINE_PORT(1),
DEFINE_PORT(2),
DEFINE_PORT(3),
DEFINE_PORT(4),
DEFINE_PORT(5),
DEFINE_PORT(6),
DEFINE_PORT(7),
DEFINE_PORT(8),
DEFINE_PORT(9),
DEFINE_PORT(10),
DEFINE_PORT(11),
DEFINE_PORT(12),
DEFINE_PORT(13),
DEFINE_PORT(14),
DEFINE_PORT(15),
DEFINE_PROP_END_OF_LIST(),
};
static void tz_ppc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = tz_ppc_realize;
dc->vmsd = &tz_ppc_vmstate;
dc->reset = tz_ppc_reset;
dc->props = tz_ppc_properties;
}
static const TypeInfo tz_ppc_info = {
.name = TYPE_TZ_PPC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(TZPPC),
.instance_init = tz_ppc_init,
.class_init = tz_ppc_class_init,
};
static void tz_ppc_register_types(void)
{
type_register_static(&tz_ppc_info);
}
type_init(tz_ppc_register_types);
-10
View File
@@ -18,16 +18,6 @@
#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);
+1
View File
@@ -21,6 +21,7 @@ common-obj-$(CONFIG_IMX) += imx_epit.o
common-obj-$(CONFIG_IMX) += imx_gpt.o
common-obj-$(CONFIG_LM32) += lm32_timer.o
common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o
common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o
obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o
+3
View File
@@ -60,3 +60,6 @@ systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr
cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset"
# hw/timer/xlnx-zynqmp-rtc.c
xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d"
+272
View File
@@ -0,0 +1,272 @@
/*
* QEMU model of the Xilinx ZynqMP Real Time Clock (RTC).
*
* Copyright (c) 2017 Xilinx Inc.
*
* Written-by: Alistair Francis <alistair.francis@xilinx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/register.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
#include "hw/ptimer.h"
#include "qemu/cutils.h"
#include "sysemu/sysemu.h"
#include "trace.h"
#include "hw/timer/xlnx-zynqmp-rtc.h"
#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG
#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0
#endif
static void rtc_int_update_irq(XlnxZynqMPRTC *s)
{
bool pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK];
qemu_set_irq(s->irq_rtc_int, pending);
}
static void addr_error_int_update_irq(XlnxZynqMPRTC *s)
{
bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK];
qemu_set_irq(s->irq_addr_error_int, pending);
}
static uint32_t rtc_get_count(XlnxZynqMPRTC *s)
{
int64_t now = qemu_clock_get_ns(rtc_clock);
return s->tick_offset + now / NANOSECONDS_PER_SECOND;
}
static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
return rtc_get_count(s);
}
static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
rtc_int_update_irq(s);
}
static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
s->regs[R_RTC_INT_MASK] &= (uint32_t) ~val64;
rtc_int_update_irq(s);
return 0;
}
static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
s->regs[R_RTC_INT_MASK] |= (uint32_t) val64;
rtc_int_update_irq(s);
return 0;
}
static void addr_error_postw(RegisterInfo *reg, uint64_t val64)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
addr_error_int_update_irq(s);
}
static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
s->regs[R_ADDR_ERROR_INT_MASK] &= (uint32_t) ~val64;
addr_error_int_update_irq(s);
return 0;
}
static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque);
s->regs[R_ADDR_ERROR_INT_MASK] |= (uint32_t) val64;
addr_error_int_update_irq(s);
return 0;
}
static const RegisterAccessInfo rtc_regs_info[] = {
{ .name = "SET_TIME_WRITE", .addr = A_SET_TIME_WRITE,
.unimp = MAKE_64BIT_MASK(0, 32),
},{ .name = "SET_TIME_READ", .addr = A_SET_TIME_READ,
.ro = 0xffffffff,
.post_read = current_time_postr,
},{ .name = "CALIB_WRITE", .addr = A_CALIB_WRITE,
.unimp = MAKE_64BIT_MASK(0, 32),
},{ .name = "CALIB_READ", .addr = A_CALIB_READ,
.ro = 0x1fffff,
},{ .name = "CURRENT_TIME", .addr = A_CURRENT_TIME,
.ro = 0xffffffff,
.post_read = current_time_postr,
},{ .name = "CURRENT_TICK", .addr = A_CURRENT_TICK,
.ro = 0xffff,
},{ .name = "ALARM", .addr = A_ALARM,
},{ .name = "RTC_INT_STATUS", .addr = A_RTC_INT_STATUS,
.w1c = 0x3,
.post_write = rtc_int_status_postw,
},{ .name = "RTC_INT_MASK", .addr = A_RTC_INT_MASK,
.reset = 0x3,
.ro = 0x3,
},{ .name = "RTC_INT_EN", .addr = A_RTC_INT_EN,
.pre_write = rtc_int_en_prew,
},{ .name = "RTC_INT_DIS", .addr = A_RTC_INT_DIS,
.pre_write = rtc_int_dis_prew,
},{ .name = "ADDR_ERROR", .addr = A_ADDR_ERROR,
.w1c = 0x1,
.post_write = addr_error_postw,
},{ .name = "ADDR_ERROR_INT_MASK", .addr = A_ADDR_ERROR_INT_MASK,
.reset = 0x1,
.ro = 0x1,
},{ .name = "ADDR_ERROR_INT_EN", .addr = A_ADDR_ERROR_INT_EN,
.pre_write = addr_error_int_en_prew,
},{ .name = "ADDR_ERROR_INT_DIS", .addr = A_ADDR_ERROR_INT_DIS,
.pre_write = addr_error_int_dis_prew,
},{ .name = "CONTROL", .addr = A_CONTROL,
.reset = 0x1000000,
.rsvd = 0x70fffffe,
},{ .name = "SAFETY_CHK", .addr = A_SAFETY_CHK,
}
};
static void rtc_reset(DeviceState *dev)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev);
unsigned int i;
for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
register_reset(&s->regs_info[i]);
}
rtc_int_update_irq(s);
addr_error_int_update_irq(s);
}
static const MemoryRegionOps rtc_ops = {
.read = register_read_memory,
.write = register_write_memory,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static void rtc_init(Object *obj)
{
XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
RegisterInfoArray *reg_array;
struct tm current_tm;
memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC,
XLNX_ZYNQMP_RTC_R_MAX * 4);
reg_array =
register_init_block32(DEVICE(obj), rtc_regs_info,
ARRAY_SIZE(rtc_regs_info),
s->regs_info, s->regs,
&rtc_ops,
XLNX_ZYNQMP_RTC_ERR_DEBUG,
XLNX_ZYNQMP_RTC_R_MAX * 4);
memory_region_add_subregion(&s->iomem,
0x0,
&reg_array->mem);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq_rtc_int);
sysbus_init_irq(sbd, &s->irq_addr_error_int);
qemu_get_timedate(&current_tm, 0);
s->tick_offset = mktimegm(&current_tm) -
qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
trace_xlnx_zynqmp_rtc_gettime(current_tm.tm_year, current_tm.tm_mon,
current_tm.tm_mday, current_tm.tm_hour,
current_tm.tm_min, current_tm.tm_sec);
}
static int rtc_pre_save(void *opaque)
{
XlnxZynqMPRTC *s = opaque;
int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
/* Add the time at migration */
s->tick_offset = s->tick_offset + now;
return 0;
}
static int rtc_post_load(void *opaque, int version_id)
{
XlnxZynqMPRTC *s = opaque;
int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
/* Subtract the time after migration. This combined with the pre_save
* action results in us having subtracted the time that the guest was
* stopped to the offset.
*/
s->tick_offset = s->tick_offset - now;
return 0;
}
static const VMStateDescription vmstate_rtc = {
.name = TYPE_XLNX_ZYNQMP_RTC,
.version_id = 1,
.minimum_version_id = 1,
.pre_save = rtc_pre_save,
.post_load = rtc_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX),
VMSTATE_UINT32(tick_offset, XlnxZynqMPRTC),
VMSTATE_END_OF_LIST(),
}
};
static void rtc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = rtc_reset;
dc->vmsd = &vmstate_rtc;
}
static const TypeInfo rtc_info = {
.name = TYPE_XLNX_ZYNQMP_RTC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(XlnxZynqMPRTC),
.class_init = rtc_class_init,
.instance_init = rtc_init,
};
static void rtc_register_types(void)
{
type_register_static(&rtc_info);
}
type_init(rtc_register_types)

Some files were not shown because too many files have changed in this diff Show More