mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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[] = {
|
||||
|
||||
@@ -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
@@ -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
@@ -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";
|
||||
|
||||
@@ -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)
|
||||
@@ -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
@@ -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);
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
®_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(¤t_tm, 0);
|
||||
s->tick_offset = mktimegm(¤t_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
Reference in New Issue
Block a user