mirror of https://github.com/xqemu/xqemu.git
target-arm queue:
* AES instruction support for 32 bit ARM * pflash01: much better emulation of 2x16bit and similar configs where multiple flash devices are banked together * fixed CBAR handling on Zynq, Highbank * initial AArch64 KVM control support * first two chunks of patches for A64 instruction emulation * new board: canon-a1100 (Canon DIGIC SoC) * new board: cubieboard (Allwinner A10 SoC) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABCAAGBQJSsLGfAAoJEDwlJe0UNgzehVQQAK1nL/AjTSaOhu7o2ZI+3OIO SSYuJk61QIE6RTLgV9SV+ifSjeOec3ngFpbv0Kc+M02xSEmEJmIHa5ZXLPDLYKMP 7v5zVmS/my3lDsBj6OW/M+VOv1HULm+8jVwVqwm9+ijcmG+Z4gVadvXRcnjKW9se KUaD27X28+by3RjE9w/IXJbGUvvEpwdzFvUBN9B2lQgsmM7z8ankzZL9TIHS+Jyx HquJ8FIeBYMoAk/lWD9REPXgYjl7QjD7NE0ue2CUL5A8MwInbxlypMkHxjsk2DWg 1C6gqK/d8fWHRT+a5KAKAbW4Atn9tSRW7hiPv2BIKYuDkfTictBIhHoRdX3rPlJc DmM8+aYRSjL27cwNzLbgX3ZSWMIlP7p2d0Mi3aWtFDUgXp1S+JOmdJmQAbDgjzn0 FEDfBJR5bLeIXT1gpSLZJmxOJjSfnhCQM/hNS2ArTLBO/vNL2lIEEFzQprqantep l++l4GfqNOf2XglHRZP2M24/a3qXzQefaCZXH2+ESW3hegsZKZ8A0UsNr0oWenF7 C2Kf78EmMMCEql9Gz5kmhbQIeLE1tXKopeZcWOKkbOYuPZk1KDNIzlz7MPZwu8LJ 6iYA8lczNUTXpu0ErRfalR+HWGy5jqbXyE0/IUamYQOOmVi2SnZlx9ewmmKI5tDO MGrrlkf3ojOv2hukzaUk =I3Of -----END PGP SIGNATURE----- Merge remote-tracking branch 'pmaydell/tags/pull-target-arm-20131217' into staging target-arm queue: * AES instruction support for 32 bit ARM * pflash01: much better emulation of 2x16bit and similar configs where multiple flash devices are banked together * fixed CBAR handling on Zynq, Highbank * initial AArch64 KVM control support * first two chunks of patches for A64 instruction emulation * new board: canon-a1100 (Canon DIGIC SoC) * new board: cubieboard (Allwinner A10 SoC) # gpg: Signature made Tue 17 Dec 2013 12:18:39 PM PST using RSA key ID 14360CDE # gpg: Can't check signature: public key not found # By Alexander Graf (14) and others # Via Peter Maydell * pmaydell/tags/pull-target-arm-20131217: (62 commits) MAINTAINERS: add myself to maintain allwinner-a10 hw/arm: add cubieboard support hw/arm: add allwinner a10 SoC support hw/intc: add allwinner A10 interrupt controller hw/timer: add allwinner a10 timer vmstate: Add support for an array of ptimer_state * MAINTAINERS: Document 'Canon DIGIC' machine hw/arm/digic: add NOR ROM support hw/arm/digic: add UART support hw/arm/digic: add timer support hw/arm/digic: prepare DIGIC-based boards support hw/arm: add very initial support for Canon DIGIC SoC target-arm: A64: add support for logical (immediate) insns target-arm: A64: add support for 1-src CLS insn host-utils: add clrsb32/64 - count leading redundant sign bits target-arm: A64: add support for bitfield insns target-arm: A64: add support for 1-src REV insns target-arm: A64: add support for 1-src RBIT insn target-arm: A64: add support for 1-src data processing and CLZ target-arm: A64: add support for 2-src shift reg insns ... Message-id: 1387312160-12318-1-git-send-email-peter.maydell@linaro.org Signed-off-by: Anthony Liguori <aliguori@amazon.com>
This commit is contained in:
commit
3dc7e2a3fe
13
MAINTAINERS
13
MAINTAINERS
|
@ -219,6 +219,13 @@ F: *win32*
|
|||
|
||||
ARM Machines
|
||||
------------
|
||||
Allwinner-a10
|
||||
M: Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
S: Maintained
|
||||
F: hw/*/allwinner-a10*
|
||||
F: include/hw/*/allwinner-a10*
|
||||
F: hw/arm/cubieboard.c
|
||||
|
||||
Exynos
|
||||
M: Evgeny Voevodin <e.voevodin@samsung.com>
|
||||
M: Maksim Kozlov <m.kozlov@samsung.com>
|
||||
|
@ -233,6 +240,12 @@ S: Supported
|
|||
F: hw/arm/highbank.c
|
||||
F: hw/net/xgmac.c
|
||||
|
||||
Canon DIGIC
|
||||
M: Antony Pavlov <antonynpavlov@gmail.com>
|
||||
S: Maintained
|
||||
F: include/hw/arm/digic.h
|
||||
F: hw/*/digic*
|
||||
|
||||
Gumstix
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Orphan
|
||||
|
|
|
@ -4438,7 +4438,7 @@ case "$target_name" in
|
|||
aarch64)
|
||||
TARGET_BASE_ARCH=arm
|
||||
bflt="yes"
|
||||
gdb_xml_files="aarch64-core.xml"
|
||||
gdb_xml_files="aarch64-core.xml aarch64-fpu.xml"
|
||||
;;
|
||||
cris)
|
||||
;;
|
||||
|
@ -4550,7 +4550,7 @@ case "$target_name" in
|
|||
*)
|
||||
esac
|
||||
case "$target_name" in
|
||||
arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
|
||||
aarch64|arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
|
||||
# Make sure the target and host cpus are compatible
|
||||
if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
|
||||
\( "$target_name" = "$cpu" -o \
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# Default configuration for aarch64-softmmu
|
||||
|
||||
# We support all the 32 bit boards so need all their config
|
||||
include arm-softmmu.mak
|
||||
|
||||
# Currently no 64-bit specific config requirements
|
|
@ -64,6 +64,7 @@ CONFIG_XILINX_SPIPS=y
|
|||
|
||||
CONFIG_ARM11SCU=y
|
||||
CONFIG_A9SCU=y
|
||||
CONFIG_DIGIC=y
|
||||
CONFIG_MARVELL_88W8618=y
|
||||
CONFIG_OMAP=y
|
||||
CONFIG_TSC210X=y
|
||||
|
@ -82,3 +83,7 @@ CONFIG_VERSATILE_I2C=y
|
|||
|
||||
CONFIG_SDHCI=y
|
||||
CONFIG_INTEGRATOR_DEBUG=y
|
||||
|
||||
CONFIG_ALLWINNER_A10_PIT=y
|
||||
CONFIG_ALLWINNER_A10_PIC=y
|
||||
CONFIG_ALLWINNER_A10=y
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2009-2012 Free Software Foundation, Inc.
|
||||
Contributed by ARM Ltd.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||
<feature name="org.gnu.gdb.aarch64.fpu">
|
||||
<vector id="v2d" type="ieee_double" count="2"/>
|
||||
<vector id="v2u" type="uint64" count="2"/>
|
||||
<vector id="v2i" type="int64" count="2"/>
|
||||
<vector id="v4f" type="ieee_single" count="4"/>
|
||||
<vector id="v4u" type="uint32" count="4"/>
|
||||
<vector id="v4i" type="int32" count="4"/>
|
||||
<vector id="v8u" type="uint16" count="8"/>
|
||||
<vector id="v8i" type="int16" count="8"/>
|
||||
<vector id="v16u" type="uint8" count="16"/>
|
||||
<vector id="v16i" type="int8" count="16"/>
|
||||
<vector id="v1u" type="uint128" count="1"/>
|
||||
<vector id="v1i" type="int128" count="1"/>
|
||||
<union id="vnd">
|
||||
<field name="f" type="v2d"/>
|
||||
<field name="u" type="v2u"/>
|
||||
<field name="s" type="v2i"/>
|
||||
</union>
|
||||
<union id="vns">
|
||||
<field name="f" type="v4f"/>
|
||||
<field name="u" type="v4u"/>
|
||||
<field name="s" type="v4i"/>
|
||||
</union>
|
||||
<union id="vnh">
|
||||
<field name="u" type="v8u"/>
|
||||
<field name="s" type="v8i"/>
|
||||
</union>
|
||||
<union id="vnb">
|
||||
<field name="u" type="v16u"/>
|
||||
<field name="s" type="v16i"/>
|
||||
</union>
|
||||
<union id="vnq">
|
||||
<field name="u" type="v1u"/>
|
||||
<field name="s" type="v1i"/>
|
||||
</union>
|
||||
<union id="aarch64v">
|
||||
<field name="d" type="vnd"/>
|
||||
<field name="s" type="vns"/>
|
||||
<field name="h" type="vnh"/>
|
||||
<field name="b" type="vnb"/>
|
||||
<field name="q" type="vnq"/>
|
||||
</union>
|
||||
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
|
||||
<reg name="v1" bitsize="128" type="aarch64v" />
|
||||
<reg name="v2" bitsize="128" type="aarch64v" />
|
||||
<reg name="v3" bitsize="128" type="aarch64v" />
|
||||
<reg name="v4" bitsize="128" type="aarch64v" />
|
||||
<reg name="v5" bitsize="128" type="aarch64v" />
|
||||
<reg name="v6" bitsize="128" type="aarch64v" />
|
||||
<reg name="v7" bitsize="128" type="aarch64v" />
|
||||
<reg name="v8" bitsize="128" type="aarch64v" />
|
||||
<reg name="v9" bitsize="128" type="aarch64v" />
|
||||
<reg name="v10" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v11" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v12" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v13" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v14" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v15" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v16" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v17" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v18" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v19" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v20" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v21" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v22" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v23" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v24" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v25" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v26" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v27" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v28" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v29" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v30" bitsize="128" type="aarch64v"/>
|
||||
<reg name="v31" bitsize="128" type="aarch64v"/>
|
||||
<reg name="fpsr" bitsize="32"/>
|
||||
<reg name="fpcr" bitsize="32"/>
|
||||
</feature>
|
|
@ -1,7 +1,10 @@
|
|||
obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
|
||||
obj-$(CONFIG_DIGIC) += digic_boards.o
|
||||
obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o
|
||||
obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
|
||||
obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
|
||||
|
||||
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
|
||||
obj-$(CONFIG_DIGIC) += digic.o
|
||||
obj-y += omap1.o omap2.o strongarm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Allwinner A10 SoC emulation
|
||||
*
|
||||
* Copyright (C) 2013 Li Guang
|
||||
* Written by Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/devices.h"
|
||||
#include "hw/arm/allwinner-a10.h"
|
||||
|
||||
static void aw_a10_init(Object *obj)
|
||||
{
|
||||
AwA10State *s = AW_A10(obj);
|
||||
|
||||
object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU);
|
||||
object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL);
|
||||
|
||||
object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC);
|
||||
qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default());
|
||||
|
||||
object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
|
||||
qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
|
||||
}
|
||||
|
||||
static void aw_a10_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
AwA10State *s = AW_A10(dev);
|
||||
SysBusDevice *sysbusdev;
|
||||
uint8_t i;
|
||||
qemu_irq fiq, irq;
|
||||
Error *err = NULL;
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ);
|
||||
fiq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->intc), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbusdev = SYS_BUS_DEVICE(&s->intc);
|
||||
sysbus_mmio_map(sysbusdev, 0, AW_A10_PIC_REG_BASE);
|
||||
sysbus_connect_irq(sysbusdev, 0, irq);
|
||||
sysbus_connect_irq(sysbusdev, 1, fiq);
|
||||
for (i = 0; i < AW_A10_PIC_INT_NR; i++) {
|
||||
s->irq[i] = qdev_get_gpio_in(DEVICE(&s->intc), i);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->timer), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbusdev = SYS_BUS_DEVICE(&s->timer);
|
||||
sysbus_mmio_map(sysbusdev, 0, AW_A10_PIT_REG_BASE);
|
||||
sysbus_connect_irq(sysbusdev, 0, s->irq[22]);
|
||||
sysbus_connect_irq(sysbusdev, 1, s->irq[23]);
|
||||
sysbus_connect_irq(sysbusdev, 2, s->irq[24]);
|
||||
sysbus_connect_irq(sysbusdev, 3, s->irq[25]);
|
||||
sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
|
||||
sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
|
||||
|
||||
serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
|
||||
115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
|
||||
}
|
||||
|
||||
static void aw_a10_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = aw_a10_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo aw_a10_type_info = {
|
||||
.name = TYPE_AW_A10,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(AwA10State),
|
||||
.instance_init = aw_a10_init,
|
||||
.class_init = aw_a10_class_init,
|
||||
};
|
||||
|
||||
static void aw_a10_register_types(void)
|
||||
{
|
||||
type_register_static(&aw_a10_type_info);
|
||||
}
|
||||
|
||||
type_init(aw_a10_register_types)
|
193
hw/arm/boot.c
193
hw/arm/boot.c
|
@ -17,18 +17,55 @@
|
|||
#include "sysemu/device_tree.h"
|
||||
#include "qemu/config-file.h"
|
||||
|
||||
/* Kernel boot protocol is specified in the kernel docs
|
||||
* Documentation/arm/Booting and Documentation/arm64/booting.txt
|
||||
* They have different preferred image load offsets from system RAM base.
|
||||
*/
|
||||
#define KERNEL_ARGS_ADDR 0x100
|
||||
#define KERNEL_LOAD_ADDR 0x00010000
|
||||
#define KERNEL64_LOAD_ADDR 0x00080000
|
||||
|
||||
typedef enum {
|
||||
FIXUP_NONE = 0, /* do nothing */
|
||||
FIXUP_TERMINATOR, /* end of insns */
|
||||
FIXUP_BOARDID, /* overwrite with board ID number */
|
||||
FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
|
||||
FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
|
||||
FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
|
||||
FIXUP_BOOTREG, /* overwrite with boot register address */
|
||||
FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
|
||||
FIXUP_MAX,
|
||||
} FixupType;
|
||||
|
||||
typedef struct ARMInsnFixup {
|
||||
uint32_t insn;
|
||||
FixupType fixup;
|
||||
} ARMInsnFixup;
|
||||
|
||||
static const ARMInsnFixup bootloader_aarch64[] = {
|
||||
{ 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
|
||||
{ 0xaa1f03e1 }, /* mov x1, xzr */
|
||||
{ 0xaa1f03e2 }, /* mov x2, xzr */
|
||||
{ 0xaa1f03e3 }, /* mov x3, xzr */
|
||||
{ 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
|
||||
{ 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
|
||||
{ 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */
|
||||
{ 0 }, /* .word @DTB Higher 32-bits */
|
||||
{ 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */
|
||||
{ 0 }, /* .word @Kernel Entry Higher 32-bits */
|
||||
{ 0, FIXUP_TERMINATOR }
|
||||
};
|
||||
|
||||
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
|
||||
static uint32_t bootloader[] = {
|
||||
0xe3a00000, /* mov r0, #0 */
|
||||
0xe59f1004, /* ldr r1, [pc, #4] */
|
||||
0xe59f2004, /* ldr r2, [pc, #4] */
|
||||
0xe59ff004, /* ldr pc, [pc, #4] */
|
||||
0, /* Board ID */
|
||||
0, /* Address of kernel args. Set by integratorcp_init. */
|
||||
0 /* Kernel entry point. Set by integratorcp_init. */
|
||||
static const ARMInsnFixup bootloader[] = {
|
||||
{ 0xe3a00000 }, /* mov r0, #0 */
|
||||
{ 0xe59f1004 }, /* ldr r1, [pc, #4] */
|
||||
{ 0xe59f2004 }, /* ldr r2, [pc, #4] */
|
||||
{ 0xe59ff004 }, /* ldr pc, [pc, #4] */
|
||||
{ 0, FIXUP_BOARDID },
|
||||
{ 0, FIXUP_ARGPTR },
|
||||
{ 0, FIXUP_ENTRYPOINT },
|
||||
{ 0, FIXUP_TERMINATOR }
|
||||
};
|
||||
|
||||
/* Handling for secondary CPU boot in a multicore system.
|
||||
|
@ -48,39 +85,83 @@ static uint32_t bootloader[] = {
|
|||
#define DSB_INSN 0xf57ff04f
|
||||
#define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */
|
||||
|
||||
static uint32_t smpboot[] = {
|
||||
0xe59f2028, /* ldr r2, gic_cpu_if */
|
||||
0xe59f0028, /* ldr r0, startaddr */
|
||||
0xe3a01001, /* mov r1, #1 */
|
||||
0xe5821000, /* str r1, [r2] - set GICC_CTLR.Enable */
|
||||
0xe3a010ff, /* mov r1, #0xff */
|
||||
0xe5821004, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
|
||||
DSB_INSN, /* dsb */
|
||||
0xe320f003, /* wfi */
|
||||
0xe5901000, /* ldr r1, [r0] */
|
||||
0xe1110001, /* tst r1, r1 */
|
||||
0x0afffffb, /* beq <wfi> */
|
||||
0xe12fff11, /* bx r1 */
|
||||
0, /* gic_cpu_if: base address of GIC CPU interface */
|
||||
0 /* bootreg: Boot register address is held here */
|
||||
static const ARMInsnFixup smpboot[] = {
|
||||
{ 0xe59f2028 }, /* ldr r2, gic_cpu_if */
|
||||
{ 0xe59f0028 }, /* ldr r0, bootreg_addr */
|
||||
{ 0xe3a01001 }, /* mov r1, #1 */
|
||||
{ 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */
|
||||
{ 0xe3a010ff }, /* mov r1, #0xff */
|
||||
{ 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
|
||||
{ 0, FIXUP_DSB }, /* dsb */
|
||||
{ 0xe320f003 }, /* wfi */
|
||||
{ 0xe5901000 }, /* ldr r1, [r0] */
|
||||
{ 0xe1110001 }, /* tst r1, r1 */
|
||||
{ 0x0afffffb }, /* beq <wfi> */
|
||||
{ 0xe12fff11 }, /* bx r1 */
|
||||
{ 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */
|
||||
{ 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */
|
||||
{ 0, FIXUP_TERMINATOR }
|
||||
};
|
||||
|
||||
static void write_bootloader(const char *name, hwaddr addr,
|
||||
const ARMInsnFixup *insns, uint32_t *fixupcontext)
|
||||
{
|
||||
/* Fix up the specified bootloader fragment and write it into
|
||||
* guest memory using rom_add_blob_fixed(). fixupcontext is
|
||||
* an array giving the values to write in for the fixup types
|
||||
* which write a value into the code array.
|
||||
*/
|
||||
int i, len;
|
||||
uint32_t *code;
|
||||
|
||||
len = 0;
|
||||
while (insns[len].fixup != FIXUP_TERMINATOR) {
|
||||
len++;
|
||||
}
|
||||
|
||||
code = g_new0(uint32_t, len);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
uint32_t insn = insns[i].insn;
|
||||
FixupType fixup = insns[i].fixup;
|
||||
|
||||
switch (fixup) {
|
||||
case FIXUP_NONE:
|
||||
break;
|
||||
case FIXUP_BOARDID:
|
||||
case FIXUP_ARGPTR:
|
||||
case FIXUP_ENTRYPOINT:
|
||||
case FIXUP_GIC_CPU_IF:
|
||||
case FIXUP_BOOTREG:
|
||||
case FIXUP_DSB:
|
||||
insn = fixupcontext[fixup];
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
code[i] = tswap32(insn);
|
||||
}
|
||||
|
||||
rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr);
|
||||
|
||||
g_free(code);
|
||||
}
|
||||
|
||||
static void default_write_secondary(ARMCPU *cpu,
|
||||
const struct arm_boot_info *info)
|
||||
{
|
||||
int n;
|
||||
smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
|
||||
smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
|
||||
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
|
||||
/* Replace DSB with the pre-v7 DSB if necessary. */
|
||||
if (!arm_feature(&cpu->env, ARM_FEATURE_V7) &&
|
||||
smpboot[n] == DSB_INSN) {
|
||||
smpboot[n] = CP15_DSB_INSN;
|
||||
}
|
||||
smpboot[n] = tswap32(smpboot[n]);
|
||||
uint32_t fixupcontext[FIXUP_MAX];
|
||||
|
||||
fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr;
|
||||
fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr;
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_V7)) {
|
||||
fixupcontext[FIXUP_DSB] = DSB_INSN;
|
||||
} else {
|
||||
fixupcontext[FIXUP_DSB] = CP15_DSB_INSN;
|
||||
}
|
||||
rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
|
||||
info->smp_loader_start);
|
||||
|
||||
write_bootloader("smpboot", info->smp_loader_start,
|
||||
smpboot, fixupcontext);
|
||||
}
|
||||
|
||||
static void default_reset_secondary(ARMCPU *cpu,
|
||||
|
@ -334,7 +415,12 @@ static void do_cpu_reset(void *opaque)
|
|||
env->thumb = info->entry & 1;
|
||||
} else {
|
||||
if (CPU(cpu) == first_cpu) {
|
||||
env->regs[15] = info->loader_start;
|
||||
if (env->aarch64) {
|
||||
env->pc = info->loader_start;
|
||||
} else {
|
||||
env->regs[15] = info->loader_start;
|
||||
}
|
||||
|
||||
if (!info->dtb_filename) {
|
||||
if (old_param) {
|
||||
set_kernel_args_old(info);
|
||||
|
@ -354,11 +440,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
CPUState *cs = CPU(cpu);
|
||||
int kernel_size;
|
||||
int initrd_size;
|
||||
int n;
|
||||
int is_linux = 0;
|
||||
uint64_t elf_entry;
|
||||
hwaddr entry;
|
||||
hwaddr entry, kernel_load_offset;
|
||||
int big_endian;
|
||||
static const ARMInsnFixup *primary_loader;
|
||||
|
||||
/* Load the kernel. */
|
||||
if (!info->kernel_filename) {
|
||||
|
@ -368,6 +454,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
return;
|
||||
}
|
||||
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||
primary_loader = bootloader_aarch64;
|
||||
kernel_load_offset = KERNEL64_LOAD_ADDR;
|
||||
} else {
|
||||
primary_loader = bootloader;
|
||||
kernel_load_offset = KERNEL_LOAD_ADDR;
|
||||
}
|
||||
|
||||
info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
|
||||
|
||||
if (!info->secondary_cpu_reset_hook) {
|
||||
|
@ -408,9 +502,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
&is_linux);
|
||||
}
|
||||
if (kernel_size < 0) {
|
||||
entry = info->loader_start + KERNEL_LOAD_ADDR;
|
||||
entry = info->loader_start + kernel_load_offset;
|
||||
kernel_size = load_image_targphys(info->kernel_filename, entry,
|
||||
info->ram_size - KERNEL_LOAD_ADDR);
|
||||
info->ram_size - kernel_load_offset);
|
||||
is_linux = 1;
|
||||
}
|
||||
if (kernel_size < 0) {
|
||||
|
@ -420,6 +514,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
}
|
||||
info->entry = entry;
|
||||
if (is_linux) {
|
||||
uint32_t fixupcontext[FIXUP_MAX];
|
||||
|
||||
if (info->initrd_filename) {
|
||||
initrd_size = load_ramdisk(info->initrd_filename,
|
||||
info->initrd_start,
|
||||
|
@ -441,7 +537,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
}
|
||||
info->initrd_size = initrd_size;
|
||||
|
||||
bootloader[4] = info->board_id;
|
||||
fixupcontext[FIXUP_BOARDID] = info->board_id;
|
||||
|
||||
/* for device tree boot, we pass the DTB directly in r2. Otherwise
|
||||
* we point to the kernel args.
|
||||
|
@ -456,9 +552,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
if (load_dtb(dtb_start, info)) {
|
||||
exit(1);
|
||||
}
|
||||
bootloader[5] = dtb_start;
|
||||
fixupcontext[FIXUP_ARGPTR] = dtb_start;
|
||||
} else {
|
||||
bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
|
||||
fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR;
|
||||
if (info->ram_size >= (1ULL << 32)) {
|
||||
fprintf(stderr, "qemu: RAM size must be less than 4GB to boot"
|
||||
" Linux kernel using ATAGS (try passing a device tree"
|
||||
|
@ -466,12 +562,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
|||
exit(1);
|
||||
}
|
||||
}
|
||||
bootloader[6] = entry;
|
||||
for (n = 0; n < sizeof(bootloader) / 4; n++) {
|
||||
bootloader[n] = tswap32(bootloader[n]);
|
||||
}
|
||||
rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
|
||||
info->loader_start);
|
||||
fixupcontext[FIXUP_ENTRYPOINT] = entry;
|
||||
|
||||
write_bootloader("bootloader", info->loader_start,
|
||||
primary_loader, fixupcontext);
|
||||
|
||||
if (info->nb_cpus > 1) {
|
||||
info->write_secondary_boot(cpu, info);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* cubieboard emulation
|
||||
*
|
||||
* Copyright (C) 2013 Li Guang
|
||||
* Written by Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/devices.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/arm/allwinner-a10.h"
|
||||
|
||||
static struct arm_boot_info cubieboard_binfo = {
|
||||
.loader_start = AW_A10_SDRAM_BASE,
|
||||
.board_id = 0x1008,
|
||||
};
|
||||
|
||||
typedef struct CubieBoardState {
|
||||
AwA10State *a10;
|
||||
MemoryRegion sdram;
|
||||
} CubieBoardState;
|
||||
|
||||
static void cubieboard_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
CubieBoardState *s = g_new(CubieBoardState, 1);
|
||||
Error *err = NULL;
|
||||
|
||||
s->a10 = AW_A10(object_new(TYPE_AW_A10));
|
||||
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't realize Allwinner A10: %s\n",
|
||||
error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memory_region_init_ram(&s->sdram, NULL, "cubieboard.ram", args->ram_size);
|
||||
vmstate_register_ram_global(&s->sdram);
|
||||
memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE,
|
||||
&s->sdram);
|
||||
|
||||
cubieboard_binfo.ram_size = args->ram_size;
|
||||
cubieboard_binfo.kernel_filename = args->kernel_filename;
|
||||
cubieboard_binfo.kernel_cmdline = args->kernel_cmdline;
|
||||
arm_load_kernel(&s->a10->cpu, &cubieboard_binfo);
|
||||
}
|
||||
|
||||
static QEMUMachine cubieboard_machine = {
|
||||
.name = "cubieboard",
|
||||
.desc = "cubietech cubieboard",
|
||||
.init = cubieboard_init,
|
||||
};
|
||||
|
||||
|
||||
static void cubieboard_machine_init(void)
|
||||
{
|
||||
qemu_register_machine(&cubieboard_machine);
|
||||
}
|
||||
|
||||
machine_init(cubieboard_machine_init)
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* QEMU model of the Canon DIGIC SoC.
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This model is based on reverse engineering efforts
|
||||
* made by CHDK (http://chdk.wikia.com) and
|
||||
* Magic Lantern (http://www.magiclantern.fm) projects
|
||||
* contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/arm/digic.h"
|
||||
|
||||
#define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100)
|
||||
|
||||
#define DIGIC_UART_BASE 0xc0800000
|
||||
|
||||
static void digic_init(Object *obj)
|
||||
{
|
||||
DigicState *s = DIGIC(obj);
|
||||
DeviceState *dev;
|
||||
int i;
|
||||
|
||||
object_initialize(&s->cpu, sizeof(s->cpu), "arm946-" TYPE_ARM_CPU);
|
||||
object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL);
|
||||
|
||||
for (i = 0; i < DIGIC4_NB_TIMERS; i++) {
|
||||
#define DIGIC_TIMER_NAME_MLEN 11
|
||||
char name[DIGIC_TIMER_NAME_MLEN];
|
||||
|
||||
object_initialize(&s->timer[i], sizeof(s->timer[i]), TYPE_DIGIC_TIMER);
|
||||
dev = DEVICE(&s->timer[i]);
|
||||
qdev_set_parent_bus(dev, sysbus_get_default());
|
||||
snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i);
|
||||
object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL);
|
||||
}
|
||||
|
||||
object_initialize(&s->uart, sizeof(s->uart), TYPE_DIGIC_UART);
|
||||
dev = DEVICE(&s->uart);
|
||||
qdev_set_parent_bus(dev, sysbus_get_default());
|
||||
object_property_add_child(obj, "uart", OBJECT(&s->uart), NULL);
|
||||
}
|
||||
|
||||
static void digic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
DigicState *s = DIGIC(dev);
|
||||
Error *err = NULL;
|
||||
SysBusDevice *sbd;
|
||||
int i;
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpu), true, "reset-hivecs", &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);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < DIGIC4_NB_TIMERS; i++) {
|
||||
object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
sbd = SYS_BUS_DEVICE(&s->timer[i]);
|
||||
sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i));
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->uart), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
sbd = SYS_BUS_DEVICE(&s->uart);
|
||||
sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE);
|
||||
}
|
||||
|
||||
static void digic_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = digic_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo digic_type_info = {
|
||||
.name = TYPE_DIGIC,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(DigicState),
|
||||
.instance_init = digic_init,
|
||||
.class_init = digic_class_init,
|
||||
};
|
||||
|
||||
static void digic_register_types(void)
|
||||
{
|
||||
type_register_static(&digic_type_info);
|
||||
}
|
||||
|
||||
type_init(digic_register_types)
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* QEMU model of the Canon DIGIC boards (cameras indeed :).
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This model is based on reverse engineering efforts
|
||||
* made by CHDK (http://chdk.wikia.com) and
|
||||
* Magic Lantern (http://www.magiclantern.fm) projects
|
||||
* contributors.
|
||||
*
|
||||
* See docs here:
|
||||
* http://magiclantern.wikia.com/wiki/Register_Map
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/digic.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "hw/loader.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
#define DIGIC4_ROM0_BASE 0xf0000000
|
||||
#define DIGIC4_ROM1_BASE 0xf8000000
|
||||
#define DIGIC4_ROM_MAX_SIZE 0x08000000
|
||||
|
||||
typedef struct DigicBoardState {
|
||||
DigicState *digic;
|
||||
MemoryRegion ram;
|
||||
} DigicBoardState;
|
||||
|
||||
typedef struct DigicBoard {
|
||||
hwaddr ram_size;
|
||||
void (*add_rom0)(DigicBoardState *, hwaddr, const char *);
|
||||
const char *rom0_def_filename;
|
||||
void (*add_rom1)(DigicBoardState *, hwaddr, const char *);
|
||||
const char *rom1_def_filename;
|
||||
} DigicBoard;
|
||||
|
||||
static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size)
|
||||
{
|
||||
memory_region_init_ram(&s->ram, NULL, "ram", ram_size);
|
||||
memory_region_add_subregion(get_system_memory(), 0, &s->ram);
|
||||
vmstate_register_ram_global(&s->ram);
|
||||
}
|
||||
|
||||
static void digic4_board_init(DigicBoard *board)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
DigicBoardState *s = g_new(DigicBoardState, 1);
|
||||
|
||||
s->digic = DIGIC(object_new(TYPE_DIGIC));
|
||||
object_property_set_bool(OBJECT(s->digic), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't realize DIGIC SoC: %s\n",
|
||||
error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
digic4_board_setup_ram(s, board->ram_size);
|
||||
|
||||
if (board->add_rom0) {
|
||||
board->add_rom0(s, DIGIC4_ROM0_BASE, board->rom0_def_filename);
|
||||
}
|
||||
|
||||
if (board->add_rom1) {
|
||||
board->add_rom1(s, DIGIC4_ROM1_BASE, board->rom1_def_filename);
|
||||
}
|
||||
}
|
||||
|
||||
static void digic_load_rom(DigicBoardState *s, hwaddr addr,
|
||||
hwaddr max_size, const char *def_filename)
|
||||
{
|
||||
target_long rom_size;
|
||||
const char *filename;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* qtest runs no code so don't attempt a ROM load which
|
||||
* could fail and result in a spurious test failure.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (bios_name) {
|
||||
filename = bios_name;
|
||||
} else {
|
||||
filename = def_filename;
|
||||
}
|
||||
|
||||
if (filename) {
|
||||
char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename);
|
||||
|
||||
if (!fn) {
|
||||
error_report("Couldn't find rom image '%s'.\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rom_size = load_image_targphys(fn, addr, max_size);
|
||||
if (rom_size < 0 || rom_size > max_size) {
|
||||
error_report("Couldn't load rom image '%s'.\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Samsung K8P3215UQB
|
||||
* 64M Bit (4Mx16) Page Mode / Multi-Bank NOR Flash Memory
|
||||
*/
|
||||
static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr,
|
||||
const char *def_filename)
|
||||
{
|
||||
#define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024)
|
||||
#define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024)
|
||||
|
||||
pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE,
|
||||
NULL, FLASH_K8P3215UQB_SECTOR_SIZE,
|
||||
FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE,
|
||||
DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE,
|
||||
4,
|
||||
0x00EC, 0x007E, 0x0003, 0x0001,
|
||||
0x0555, 0x2aa, 0);
|
||||
|
||||
digic_load_rom(s, addr, FLASH_K8P3215UQB_SIZE, def_filename);
|
||||
}
|
||||
|
||||
static DigicBoard digic4_board_canon_a1100 = {
|
||||
.ram_size = 64 * 1024 * 1024,
|
||||
.add_rom1 = digic4_add_k8p3215uqb_rom,
|
||||
.rom1_def_filename = "canon-a1100-rom1.bin",
|
||||
};
|
||||
|
||||
static void canon_a1100_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
digic4_board_init(&digic4_board_canon_a1100);
|
||||
}
|
||||
|
||||
static QEMUMachine canon_a1100 = {
|
||||
.name = "canon-a1100",
|
||||
.desc = "Canon PowerShot A1100 IS",
|
||||
.init = &canon_a1100_init,
|
||||
};
|
||||
|
||||
static void digic_register_machines(void)
|
||||
{
|
||||
qemu_register_machine(&canon_a1100);
|
||||
}
|
||||
|
||||
machine_init(digic_register_machines)
|
|
@ -26,12 +26,13 @@
|
|||
#include "hw/boards.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define SMP_BOOT_ADDR 0x100
|
||||
#define SMP_BOOT_REG 0x40
|
||||
#define GIC_BASE_ADDR 0xfff10000
|
||||
#define SMP_BOOT_ADDR 0x100
|
||||
#define SMP_BOOT_REG 0x40
|
||||
#define MPCORE_PERIPHBASE 0xfff10000
|
||||
|
||||
#define NIRQ_GIC 160
|
||||
#define NIRQ_GIC 160
|
||||
|
||||
/* Board init. */
|
||||
|
||||
|
@ -54,7 +55,7 @@ static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
|
|||
0xe1110001, /* tst r1, r1 */
|
||||
0x0afffffb, /* beq <wfi> */
|
||||
0xe12fff11, /* bx r1 */
|
||||
GIC_BASE_ADDR /* privbase: gic address. */
|
||||
MPCORE_PERIPHBASE /* privbase: MPCore peripheral base address. */
|
||||
};
|
||||
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
|
||||
smpboot[n] = tswap32(smpboot[n]);
|
||||
|
@ -229,15 +230,23 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine)
|
|||
}
|
||||
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
|
||||
ARMCPU *cpu;
|
||||
cpu = cpu_arm_init(cpu_model);
|
||||
if (cpu == NULL) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
Error *err = NULL;
|
||||
|
||||
cpu = ARM_CPU(object_new(object_class_get_name(oc)));
|
||||
|
||||
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar",
|
||||
&err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* This will become a QOM property eventually */
|
||||
cpu->reset_cbar = GIC_BASE_ADDR;
|
||||
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
|
||||
}
|
||||
|
||||
|
@ -279,7 +288,7 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine)
|
|||
qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC);
|
||||
qdev_init_nofail(dev);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_mmio_map(busdev, 0, GIC_BASE_ADDR);
|
||||
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
sysbus_connect_irq(busdev, n, cpu_irq[n]);
|
||||
}
|
||||
|
|
|
@ -480,6 +480,36 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Open code a private version of pflash registration since we
|
||||
* need to set non-default device width for VExpress platform.
|
||||
*/
|
||||
static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name,
|
||||
DriveInfo *di)
|
||||
{
|
||||
DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
|
||||
|
||||
if (di && qdev_prop_set_drive(dev, "drive", di->bdrv)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
qdev_prop_set_uint32(dev, "num-blocks",
|
||||
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE);
|
||||
qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE);
|
||||
qdev_prop_set_uint8(dev, "width", 4);
|
||||
qdev_prop_set_uint8(dev, "device-width", 2);
|
||||
qdev_prop_set_uint8(dev, "big-endian", 0);
|
||||
qdev_prop_set_uint16(dev, "id0", 0x89);
|
||||
qdev_prop_set_uint16(dev, "id1", 0x18);
|
||||
qdev_prop_set_uint16(dev, "id2", 0x00);
|
||||
qdev_prop_set_uint16(dev, "id3", 0x00);
|
||||
qdev_prop_set_string(dev, "name", name);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
|
||||
return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01");
|
||||
}
|
||||
|
||||
static void vexpress_common_init(VEDBoardInfo *daughterboard,
|
||||
QEMUMachineInitArgs *args)
|
||||
{
|
||||
|
@ -561,11 +591,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard,
|
|||
sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
|
||||
|
||||
dinfo = drive_get_next(IF_PFLASH);
|
||||
pflash0 = pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0",
|
||||
VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
|
||||
VEXPRESS_FLASH_SECT_SIZE,
|
||||
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
|
||||
0x00, 0x89, 0x00, 0x18, 0);
|
||||
pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0",
|
||||
dinfo);
|
||||
if (!pflash0) {
|
||||
fprintf(stderr, "vexpress: error registering flash 0.\n");
|
||||
exit(1);
|
||||
|
@ -580,11 +607,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard,
|
|||
}
|
||||
|
||||
dinfo = drive_get_next(IF_PFLASH);
|
||||
if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1",
|
||||
VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
|
||||
VEXPRESS_FLASH_SECT_SIZE,
|
||||
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
|
||||
0x00, 0x89, 0x00, 0x18, 0)) {
|
||||
if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1",
|
||||
dinfo)) {
|
||||
fprintf(stderr, "vexpress: error registering flash 1.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "sysemu/blockdev.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/ssi.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define NUM_SPI_FLASHES 4
|
||||
#define NUM_QSPI_FLASHES 2
|
||||
|
@ -35,6 +36,8 @@
|
|||
|
||||
#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
|
||||
|
||||
#define MPCORE_PERIPHBASE 0xF8F00000
|
||||
|
||||
static const int dma_irqs[8] = {
|
||||
46, 47, 48, 49, 72, 73, 74, 75
|
||||
};
|
||||
|
@ -102,6 +105,7 @@ static void zynq_init(QEMUMachineInitArgs *args)
|
|||
const char *kernel_filename = args->kernel_filename;
|
||||
const char *kernel_cmdline = args->kernel_cmdline;
|
||||
const char *initrd_filename = args->initrd_filename;
|
||||
ObjectClass *cpu_oc;
|
||||
ARMCPU *cpu;
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
|
||||
|
@ -110,15 +114,24 @@ static void zynq_init(QEMUMachineInitArgs *args)
|
|||
SysBusDevice *busdev;
|
||||
qemu_irq pic[64];
|
||||
NICInfo *nd;
|
||||
Error *err = NULL;
|
||||
int n;
|
||||
|
||||
if (!cpu_model) {
|
||||
cpu_model = "cortex-a9";
|
||||
}
|
||||
cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
|
||||
|
||||
cpu = cpu_arm_init(cpu_model);
|
||||
if (!cpu) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc)));
|
||||
|
||||
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
|
||||
if (err) {
|
||||
error_report("%s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -154,7 +167,7 @@ static void zynq_init(QEMUMachineInitArgs *args)
|
|||
qdev_prop_set_uint32(dev, "num-cpu", 1);
|
||||
qdev_init_nofail(dev);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_mmio_map(busdev, 0, 0xF8F00000);
|
||||
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
|
||||
sysbus_connect_irq(busdev, 0,
|
||||
qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "hw/block/flash.h"
|
||||
#include "block/block.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
@ -71,7 +72,9 @@ struct pflash_t {
|
|||
BlockDriverState *bs;
|
||||
uint32_t nb_blocs;
|
||||
uint64_t sector_len;
|
||||
uint8_t width;
|
||||
uint8_t bank_width;
|
||||
uint8_t device_width; /* If 0, device width not specified. */
|
||||
uint8_t max_device_width; /* max device width in bytes */
|
||||
uint8_t be;
|
||||
uint8_t wcycle; /* if 0, the flash is read normally */
|
||||
int ro;
|
||||
|
@ -116,6 +119,119 @@ static void pflash_timer (void *opaque)
|
|||
pfl->cmd = 0;
|
||||
}
|
||||
|
||||
/* Perform a CFI query based on the bank width of the flash.
|
||||
* If this code is called we know we have a device_width set for
|
||||
* this flash.
|
||||
*/
|
||||
static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset)
|
||||
{
|
||||
int i;
|
||||
uint32_t resp = 0;
|
||||
hwaddr boff;
|
||||
|
||||
/* Adjust incoming offset to match expected device-width
|
||||
* addressing. CFI query addresses are always specified in terms of
|
||||
* the maximum supported width of the device. This means that x8
|
||||
* devices and x8/x16 devices in x8 mode behave differently. For
|
||||
* devices that are not used at their max width, we will be
|
||||
* provided with addresses that use higher address bits than
|
||||
* expected (based on the max width), so we will shift them lower
|
||||
* so that they will match the addresses used when
|
||||
* device_width==max_device_width.
|
||||
*/
|
||||
boff = offset >> (ctz32(pfl->bank_width) +
|
||||
ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
|
||||
|
||||
if (boff > pfl->cfi_len) {
|
||||
return 0;
|
||||
}
|
||||
/* Now we will construct the CFI response generated by a single
|
||||
* device, then replicate that for all devices that make up the
|
||||
* bus. For wide parts used in x8 mode, CFI query responses
|
||||
* are different than native byte-wide parts.
|
||||
*/
|
||||
resp = pfl->cfi_table[boff];
|
||||
if (pfl->device_width != pfl->max_device_width) {
|
||||
/* The only case currently supported is x8 mode for a
|
||||
* wider part.
|
||||
*/
|
||||
if (pfl->device_width != 1 || pfl->bank_width > 4) {
|
||||
DPRINTF("%s: Unsupported device configuration: "
|
||||
"device_width=%d, max_device_width=%d\n",
|
||||
__func__, pfl->device_width,
|
||||
pfl->max_device_width);
|
||||
return 0;
|
||||
}
|
||||
/* CFI query data is repeated, rather than zero padded for
|
||||
* wide devices used in x8 mode.
|
||||
*/
|
||||
for (i = 1; i < pfl->max_device_width; i++) {
|
||||
resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]);
|
||||
}
|
||||
}
|
||||
/* Replicate responses for each device in bank. */
|
||||
if (pfl->device_width < pfl->bank_width) {
|
||||
for (i = pfl->device_width;
|
||||
i < pfl->bank_width; i += pfl->device_width) {
|
||||
resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Perform a device id query based on the bank width of the flash. */
|
||||
static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset)
|
||||
{
|
||||
int i;
|
||||
uint32_t resp;
|
||||
hwaddr boff;
|
||||
|
||||
/* Adjust incoming offset to match expected device-width
|
||||
* addressing. Device ID read addresses are always specified in
|
||||
* terms of the maximum supported width of the device. This means
|
||||
* that x8 devices and x8/x16 devices in x8 mode behave
|
||||
* differently. For devices that are not used at their max width,
|
||||
* we will be provided with addresses that use higher address bits
|
||||
* than expected (based on the max width), so we will shift them
|
||||
* lower so that they will match the addresses used when
|
||||
* device_width==max_device_width.
|
||||
*/
|
||||
boff = offset >> (ctz32(pfl->bank_width) +
|
||||
ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
|
||||
|
||||
/* Mask off upper bits which may be used in to query block
|
||||
* or sector lock status at other addresses.
|
||||
* Offsets 2/3 are block lock status, is not emulated.
|
||||
*/
|
||||
switch (boff & 0xFF) {
|
||||
case 0:
|
||||
resp = pfl->ident0;
|
||||
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
|
||||
break;
|
||||
case 1:
|
||||
resp = pfl->ident1;
|
||||
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("%s: Read Device Information offset=%x\n", __func__,
|
||||
(unsigned)offset);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
/* Replicate responses for each device in bank. */
|
||||
if (pfl->device_width < pfl->bank_width) {
|
||||
for (i = pfl->device_width;
|
||||
i < pfl->bank_width; i += pfl->device_width) {
|
||||
resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
|
||||
int width, int be)
|
||||
{
|
||||
|
@ -124,12 +240,6 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
|
|||
uint8_t *p;
|
||||
|
||||
ret = -1;
|
||||
boff = offset & 0xFF; /* why this here ?? */
|
||||
|
||||
if (pfl->width == 2)
|
||||
boff = boff >> 1;
|
||||
else if (pfl->width == 4)
|
||||
boff = boff >> 2;
|
||||
|
||||
#if 0
|
||||
DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
|
||||
|
@ -190,35 +300,88 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
|
|||
case 0x60: /* Block /un)lock */
|
||||
case 0x70: /* Status Register */
|
||||
case 0xe8: /* Write block */
|
||||
/* Status register read */
|
||||
/* Status register read. Return status from each device in
|
||||
* bank.
|
||||
*/
|
||||
ret = pfl->status;
|
||||
if (width > 2) {
|
||||
if (pfl->device_width && width > pfl->device_width) {
|
||||
int shift = pfl->device_width * 8;
|
||||
while (shift + pfl->device_width * 8 <= width * 8) {
|
||||
ret |= pfl->status << shift;
|
||||
shift += pfl->device_width * 8;
|
||||
}
|
||||
} else if (!pfl->device_width && width > 2) {
|
||||
/* Handle 32 bit flash cases where device width is not
|
||||
* set. (Existing behavior before device width added.)
|
||||
*/
|
||||
ret |= pfl->status << 16;
|
||||
}
|
||||
DPRINTF("%s: status %x\n", __func__, ret);
|
||||
break;
|
||||
case 0x90:
|
||||
switch (boff) {
|
||||
case 0:
|
||||
ret = pfl->ident0 << 8 | pfl->ident1;
|
||||
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
|
||||
break;
|
||||
case 1:
|
||||
ret = pfl->ident2 << 8 | pfl->ident3;
|
||||
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("%s: Read Device Information boff=%x\n", __func__,
|
||||
(unsigned)boff);
|
||||
ret = 0;
|
||||
break;
|
||||
if (!pfl->device_width) {
|
||||
/* Preserve old behavior if device width not specified */
|
||||
boff = offset & 0xFF;
|
||||
if (pfl->bank_width == 2) {
|
||||
boff = boff >> 1;
|
||||
} else if (pfl->bank_width == 4) {
|
||||
boff = boff >> 2;
|
||||
}
|
||||
|
||||
switch (boff) {
|
||||
case 0:
|
||||
ret = pfl->ident0 << 8 | pfl->ident1;
|
||||
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
|
||||
break;
|
||||
case 1:
|
||||
ret = pfl->ident2 << 8 | pfl->ident3;
|
||||
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("%s: Read Device Information boff=%x\n", __func__,
|
||||
(unsigned)boff);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* If we have a read larger than the bank_width, combine multiple
|
||||
* manufacturer/device ID queries into a single response.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < width; i += pfl->bank_width) {
|
||||
ret = deposit32(ret, i * 8, pfl->bank_width * 8,
|
||||
pflash_devid_query(pfl,
|
||||
offset + i * pfl->bank_width));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x98: /* Query mode */
|
||||
if (boff > pfl->cfi_len)
|
||||
ret = 0;
|
||||
else
|
||||
ret = pfl->cfi_table[boff];
|
||||
if (!pfl->device_width) {
|
||||
/* Preserve old behavior if device width not specified */
|
||||
boff = offset & 0xFF;
|
||||
if (pfl->bank_width == 2) {
|
||||
boff = boff >> 1;
|
||||
} else if (pfl->bank_width == 4) {
|
||||
boff = boff >> 2;
|
||||
}
|
||||
|
||||
if (boff > pfl->cfi_len) {
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = pfl->cfi_table[boff];
|
||||
}
|
||||
} else {
|
||||
/* If we have a read larger than the bank_width, combine multiple
|
||||
* CFI queries into a single response.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < width; i += pfl->bank_width) {
|
||||
ret = deposit32(ret, i * 8, pfl->bank_width * 8,
|
||||
pflash_cfi_query(pfl,
|
||||
offset + i * pfl->bank_width));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
|
@ -378,6 +541,14 @@ static void pflash_write(pflash_t *pfl, hwaddr offset,
|
|||
|
||||
break;
|
||||
case 0xe8:
|
||||
/* Mask writeblock size based on device width, or bank width if
|
||||
* device width not specified.
|
||||
*/
|
||||
if (pfl->device_width) {
|
||||
value = extract32(value, 0, pfl->device_width * 8);
|
||||
} else {
|
||||
value = extract32(value, 0, pfl->bank_width * 8);
|
||||
}
|
||||
DPRINTF("%s: block write of %x bytes\n", __func__, value);
|
||||
pfl->counter = value;
|
||||
pfl->wcycle++;
|
||||
|
@ -613,6 +784,13 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
|
|||
pfl->ro = 0;
|
||||
}
|
||||
|
||||
/* Default to devices being used at their maximum device width. This was
|
||||
* assumed before the device_width support was added.
|
||||
*/
|
||||
if (!pfl->max_device_width) {
|
||||
pfl->max_device_width = pfl->device_width;
|
||||
}
|
||||
|
||||
pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
|
||||
pfl->wcycle = 0;
|
||||
pfl->cmd = 0;
|
||||
|
@ -665,7 +843,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
|
|||
pfl->cfi_table[0x28] = 0x02;
|
||||
pfl->cfi_table[0x29] = 0x00;
|
||||
/* Max number of bytes in multi-bytes write */
|
||||
if (pfl->width == 1) {
|
||||
if (pfl->bank_width == 1) {
|
||||
pfl->cfi_table[0x2A] = 0x08;
|
||||
} else {
|
||||
pfl->cfi_table[0x2A] = 0x0B;
|
||||
|
@ -706,7 +884,25 @@ static Property pflash_cfi01_properties[] = {
|
|||
DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
|
||||
DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
|
||||
DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0),
|
||||
DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
|
||||
/* width here is the overall width of this QEMU device in bytes.
|
||||
* The QEMU device may be emulating a number of flash devices
|
||||
* wired up in parallel; the width of each individual flash
|
||||
* device should be specified via device-width. If the individual
|
||||
* devices have a maximum width which is greater than the width
|
||||
* they are being used for, this maximum width should be set via
|
||||
* max-device-width (which otherwise defaults to device-width).
|
||||
* So for instance a 32-bit wide QEMU flash device made from four
|
||||
* 16-bit flash devices used in 8-bit wide mode would be configured
|
||||
* with width = 4, device-width = 1, max-device-width = 2.
|
||||
*
|
||||
* If device-width is not specified we default to backwards
|
||||
* compatible behaviour which is a bad emulation of two
|
||||
* 16 bit devices making up a 32 bit wide QEMU device. This
|
||||
* is deprecated for new uses of this device.
|
||||
*/
|
||||
DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0),
|
||||
DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0),
|
||||
DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0),
|
||||
DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
|
||||
DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
|
||||
DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
|
||||
|
@ -745,8 +941,8 @@ pflash_t *pflash_cfi01_register(hwaddr base,
|
|||
DeviceState *qdev, const char *name,
|
||||
hwaddr size,
|
||||
BlockDriverState *bs,
|
||||
uint32_t sector_len, int nb_blocs, int width,
|
||||
uint16_t id0, uint16_t id1,
|
||||
uint32_t sector_len, int nb_blocs,
|
||||
int bank_width, uint16_t id0, uint16_t id1,
|
||||
uint16_t id2, uint16_t id3, int be)
|
||||
{
|
||||
DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01);
|
||||
|
@ -756,7 +952,7 @@ pflash_t *pflash_cfi01_register(hwaddr base,
|
|||
}
|
||||
qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
|
||||
qdev_prop_set_uint64(dev, "sector-length", sector_len);
|
||||
qdev_prop_set_uint8(dev, "width", width);
|
||||
qdev_prop_set_uint8(dev, "width", bank_width);
|
||||
qdev_prop_set_uint8(dev, "big-endian", !!be);
|
||||
qdev_prop_set_uint16(dev, "id0", id0);
|
||||
qdev_prop_set_uint16(dev, "id1", id1);
|
||||
|
|
|
@ -14,6 +14,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_uart.o
|
|||
obj-$(CONFIG_OMAP) += omap_uart.o
|
||||
obj-$(CONFIG_SH4) += sh_serial.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_vty.o
|
||||
obj-$(CONFIG_DIGIC) += digic-uart.o
|
||||
|
||||
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
||||
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* QEMU model of the Canon DIGIC UART block.
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This model is based on reverse engineering efforts
|
||||
* made by CHDK (http://chdk.wikia.com) and
|
||||
* Magic Lantern (http://www.magiclantern.fm) projects
|
||||
* contributors.
|
||||
*
|
||||
* See "Serial terminal" docs here:
|
||||
* http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers
|
||||
*
|
||||
* The QEMU model of the Milkymist UART block by Michael Walle
|
||||
* is used as a template.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#include "hw/char/digic-uart.h"
|
||||
|
||||
enum {
|
||||
ST_RX_RDY = (1 << 0),
|
||||
ST_TX_RDY = (1 << 1),
|
||||
};
|
||||
|
||||
static uint64_t digic_uart_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
DigicUartState *s = opaque;
|
||||
uint64_t ret = 0;
|
||||
|
||||
addr >>= 2;
|
||||
|
||||
switch (addr) {
|
||||
case R_RX:
|
||||
s->reg_st &= ~(ST_RX_RDY);
|
||||
ret = s->reg_rx;
|
||||
break;
|
||||
|
||||
case R_ST:
|
||||
ret = s->reg_st;
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"digic-uart: read access to unknown register 0x"
|
||||
TARGET_FMT_plx, addr << 2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
DigicUartState *s = opaque;
|
||||
unsigned char ch = value;
|
||||
|
||||
addr >>= 2;
|
||||
|
||||
switch (addr) {
|
||||
case R_TX:
|
||||
if (s->chr) {
|
||||
qemu_chr_fe_write_all(s->chr, &ch, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case R_ST:
|
||||
/*
|
||||
* Ignore write to R_ST.
|
||||
*
|
||||
* The point is that this register is actively used
|
||||
* during receiving and transmitting symbols,
|
||||
* but we don't know the function of most of bits.
|
||||
*
|
||||
* Ignoring writes to R_ST is only a simplification
|
||||
* of the model. It has no perceptible side effects
|
||||
* for existing guests.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"digic-uart: write access to unknown register 0x"
|
||||
TARGET_FMT_plx, addr << 2);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps uart_mmio_ops = {
|
||||
.read = digic_uart_read,
|
||||
.write = digic_uart_write,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int uart_can_rx(void *opaque)
|
||||
{
|
||||
DigicUartState *s = opaque;
|
||||
|
||||
return !(s->reg_st & ST_RX_RDY);
|
||||
}
|
||||
|
||||
static void uart_rx(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
DigicUartState *s = opaque;
|
||||
|
||||
assert(uart_can_rx(opaque));
|
||||
|
||||
s->reg_st |= ST_RX_RDY;
|
||||
s->reg_rx = *buf;
|
||||
}
|
||||
|
||||
static void uart_event(void *opaque, int event)
|
||||
{
|
||||
}
|
||||
|
||||
static void digic_uart_reset(DeviceState *d)
|
||||
{
|
||||
DigicUartState *s = DIGIC_UART(d);
|
||||
|
||||
s->reg_rx = 0;
|
||||
s->reg_st = ST_TX_RDY;
|
||||
}
|
||||
|
||||
static void digic_uart_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
DigicUartState *s = DIGIC_UART(dev);
|
||||
|
||||
s->chr = qemu_char_get_next_serial();
|
||||
if (s->chr) {
|
||||
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void digic_uart_init(Object *obj)
|
||||
{
|
||||
DigicUartState *s = DIGIC_UART(obj);
|
||||
|
||||
memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s,
|
||||
TYPE_DIGIC_UART, 0x18);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_digic_uart = {
|
||||
.name = "digic-uart",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(reg_rx, DigicUartState),
|
||||
VMSTATE_UINT32(reg_st, DigicUartState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void digic_uart_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = digic_uart_realize;
|
||||
dc->reset = digic_uart_reset;
|
||||
dc->vmsd = &vmstate_digic_uart;
|
||||
}
|
||||
|
||||
static const TypeInfo digic_uart_info = {
|
||||
.name = TYPE_DIGIC_UART,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(DigicUartState),
|
||||
.instance_init = digic_uart_init,
|
||||
.class_init = digic_uart_class_init,
|
||||
};
|
||||
|
||||
static void digic_uart_register_types(void)
|
||||
{
|
||||
type_register_static(&digic_uart_info);
|
||||
}
|
||||
|
||||
type_init(digic_uart_register_types)
|
|
@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
|
|||
obj-$(CONFIG_SH4) += sh_intc.o
|
||||
obj-$(CONFIG_XICS) += xics.o
|
||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Allwinner A10 interrupt controller device emulation
|
||||
*
|
||||
* Copyright (C) 2013 Li Guang
|
||||
* Written by Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/devices.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/intc/allwinner-a10-pic.h"
|
||||
|
||||
static void aw_a10_pic_update(AwA10PICState *s)
|
||||
{
|
||||
uint8_t i;
|
||||
int irq = 0, fiq = 0;
|
||||
|
||||
for (i = 0; i < AW_A10_PIC_REG_NUM; i++) {
|
||||
irq |= s->irq_pending[i] & ~s->mask[i];
|
||||
fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i];
|
||||
}
|
||||
|
||||
qemu_set_irq(s->parent_irq, !!irq);
|
||||
qemu_set_irq(s->parent_fiq, !!fiq);
|
||||
}
|
||||
|
||||
static void aw_a10_pic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
AwA10PICState *s = opaque;
|
||||
|
||||
if (level) {
|
||||
set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]);
|
||||
}
|
||||
aw_a10_pic_update(s);
|
||||
}
|
||||
|
||||
static uint64_t aw_a10_pic_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
AwA10PICState *s = opaque;
|
||||
uint8_t index = (offset & 0xc) / 4;
|
||||
|
||||
switch (offset) {
|
||||
case AW_A10_PIC_VECTOR:
|
||||
return s->vector;
|
||||
case AW_A10_PIC_BASE_ADDR:
|
||||
return s->base_addr;
|
||||
case AW_A10_PIC_PROTECT:
|
||||
return s->protect;
|
||||
case AW_A10_PIC_NMI:
|
||||
return s->nmi;
|
||||
case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8:
|
||||
return s->irq_pending[index];
|
||||
case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8:
|
||||
return s->fiq_pending[index];
|
||||
case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8:
|
||||
return s->select[index];
|
||||
case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8:
|
||||
return s->enable[index];
|
||||
case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8:
|
||||
return s->mask[index];
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%x\n", __func__, (int)offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
AwA10PICState *s = opaque;
|
||||
uint8_t index = (offset & 0xc) / 4;
|
||||
|
||||
switch (offset) {
|
||||
case AW_A10_PIC_VECTOR:
|
||||
s->vector = value & ~0x3;
|
||||
break;
|
||||
case AW_A10_PIC_BASE_ADDR:
|
||||
s->base_addr = value & ~0x3;
|
||||
case AW_A10_PIC_PROTECT:
|
||||
s->protect = value;
|
||||
break;
|
||||
case AW_A10_PIC_NMI:
|
||||
s->nmi = value;
|
||||
break;
|
||||
case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8:
|
||||
s->irq_pending[index] &= ~value;
|
||||
break;
|
||||
case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8:
|
||||
s->fiq_pending[index] &= ~value;
|
||||
break;
|
||||
case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8:
|
||||
s->select[index] = value;
|
||||
break;
|
||||
case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8:
|
||||
s->enable[index] = value;
|
||||
break;
|
||||
case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8:
|
||||
s->mask[index] = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%x\n", __func__, (int)offset);
|
||||
break;
|
||||
}
|
||||
|
||||
aw_a10_pic_update(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aw_a10_pic_ops = {
|
||||
.read = aw_a10_pic_read,
|
||||
.write = aw_a10_pic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_aw_a10_pic = {
|
||||
.name = "a10.pic",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(vector, AwA10PICState),
|
||||
VMSTATE_UINT32(base_addr, AwA10PICState),
|
||||
VMSTATE_UINT32(protect, AwA10PICState),
|
||||
VMSTATE_UINT32(nmi, AwA10PICState),
|
||||
VMSTATE_UINT32_ARRAY(irq_pending, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_UINT32_ARRAY(fiq_pending, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_UINT32_ARRAY(enable, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_UINT32_ARRAY(select, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_UINT32_ARRAY(mask, AwA10PICState, AW_A10_PIC_REG_NUM),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void aw_a10_pic_init(Object *obj)
|
||||
{
|
||||
AwA10PICState *s = AW_A10_PIC(obj);
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
|
||||
qdev_init_gpio_in(DEVICE(dev), aw_a10_pic_set_irq, AW_A10_PIC_INT_NR);
|
||||
sysbus_init_irq(dev, &s->parent_irq);
|
||||
sysbus_init_irq(dev, &s->parent_fiq);
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &aw_a10_pic_ops, s,
|
||||
TYPE_AW_A10_PIC, 0x400);
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
}
|
||||
|
||||
static void aw_a10_pic_reset(DeviceState *d)
|
||||
{
|
||||
AwA10PICState *s = AW_A10_PIC(d);
|
||||
uint8_t i;
|
||||
|
||||
s->base_addr = 0;
|
||||
s->protect = 0;
|
||||
s->nmi = 0;
|
||||
s->vector = 0;
|
||||
for (i = 0; i < AW_A10_PIC_REG_NUM; i++) {
|
||||
s->irq_pending[i] = 0;
|
||||
s->fiq_pending[i] = 0;
|
||||
s->select[i] = 0;
|
||||
s->enable[i] = 0;
|
||||
s->mask[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void aw_a10_pic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = aw_a10_pic_reset;
|
||||
dc->desc = "allwinner a10 pic";
|
||||
dc->vmsd = &vmstate_aw_a10_pic;
|
||||
}
|
||||
|
||||
static const TypeInfo aw_a10_pic_info = {
|
||||
.name = TYPE_AW_A10_PIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AwA10PICState),
|
||||
.instance_init = aw_a10_pic_init,
|
||||
.class_init = aw_a10_pic_class_init,
|
||||
};
|
||||
|
||||
static void aw_a10_register_types(void)
|
||||
{
|
||||
type_register_static(&aw_a10_pic_info);
|
||||
}
|
||||
|
||||
type_init(aw_a10_register_types);
|
|
@ -26,5 +26,8 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o
|
|||
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
|
||||
obj-$(CONFIG_SH4) += sh_timer.o
|
||||
obj-$(CONFIG_TUSB6010) += tusb6010.o
|
||||
obj-$(CONFIG_DIGIC) += digic-timer.o
|
||||
|
||||
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
|
||||
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Allwinner A10 timer device emulation
|
||||
*
|
||||
* Copyright (C) 2013 Li Guang
|
||||
* Written by Li Guang <lig.fnst@cn.fujitsu.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/timer/allwinner-a10-pit.h"
|
||||
|
||||
static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
AwA10PITState *s = AW_A10_PIT(opaque);
|
||||
uint8_t index;
|
||||
|
||||
switch (offset) {
|
||||
case AW_A10_PIT_TIMER_IRQ_EN:
|
||||
return s->irq_enable;
|
||||
case AW_A10_PIT_TIMER_IRQ_ST:
|
||||
return s->irq_status;
|
||||
case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
|
||||
index = offset & 0xf0;
|
||||
index >>= 4;
|
||||
index -= 1;
|
||||
switch (offset & 0x0f) {
|
||||
case AW_A10_PIT_TIMER_CONTROL:
|
||||
return s->control[index];
|
||||
case AW_A10_PIT_TIMER_INTERVAL:
|
||||
return s->interval[index];
|
||||
case AW_A10_PIT_TIMER_COUNT:
|
||||
s->count[index] = ptimer_get_count(s->timer[index]);
|
||||
return s->count[index];
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%x\n", __func__, (int)offset);
|
||||
break;
|
||||
}
|
||||
case AW_A10_PIT_WDOG_CONTROL:
|
||||
break;
|
||||
case AW_A10_PIT_WDOG_MODE:
|
||||
break;
|
||||
case AW_A10_PIT_COUNT_LO:
|
||||
return s->count_lo;
|
||||
case AW_A10_PIT_COUNT_HI:
|
||||
return s->count_hi;
|
||||
case AW_A10_PIT_COUNT_CTL:
|
||||
return s->count_ctl;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%x\n", __func__, (int)offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
AwA10PITState *s = AW_A10_PIT(opaque);
|
||||
uint8_t index;
|
||||
|
||||
switch (offset) {
|
||||
case AW_A10_PIT_TIMER_IRQ_EN:
|
||||
s->irq_enable = value;
|
||||
break;
|
||||
case AW_A10_PIT_TIMER_IRQ_ST:
|
||||
s->irq_status &= ~value;
|
||||
break;
|
||||
case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
|
||||
index = offset & 0xf0;
|
||||
index >>= 4;
|
||||
index -= 1;
|
||||
switch (offset & 0x0f) {
|
||||
case AW_A10_PIT_TIMER_CONTROL:
|
||||
s->control[index] = value;
|
||||
if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
|
||||
ptimer_set_count(s->timer[index], s->interval[index]);
|
||||
}
|
||||
if (s->control[index] & AW_A10_PIT_TIMER_EN) {
|
||||
int oneshot = 0;
|
||||
if (s->control[index] & AW_A10_PIT_TIMER_MODE) {
|
||||
oneshot = 1;
|
||||
}
|
||||
ptimer_run(s->timer[index], oneshot);
|
||||
} else {
|
||||
ptimer_stop(s->timer[index]);
|
||||
}
|
||||
break;
|
||||
case AW_A10_PIT_TIMER_INTERVAL:
|
||||
s->interval[index] = value;
|
||||
ptimer_set_limit(s->timer[index], s->interval[index], 1);
|
||||
break;
|
||||
case AW_A10_PIT_TIMER_COUNT:
|
||||
s->count[index] = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%x\n", __func__, (int)offset);
|
||||
}
|
||||
break;
|
||||
case AW_A10_PIT_WDOG_CONTROL:
|
||||
s->watch_dog_control = value;
|
||||
break;
|
||||
case AW_A10_PIT_WDOG_MODE:
|
||||
s->watch_dog_mode = value;
|
||||
break;
|
||||
case AW_A10_PIT_COUNT_LO:
|
||||
s->count_lo = value;
|
||||
break;
|
||||
case AW_A10_PIT_COUNT_HI:
|
||||
s->count_hi = value;
|
||||
break;
|
||||
case AW_A10_PIT_COUNT_CTL:
|
||||
s->count_ctl = value;
|
||||
if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) {
|
||||
uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
s->count_lo = tmp_count;
|
||||
s->count_hi = tmp_count >> 32;
|
||||
s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN;
|
||||
}
|
||||
if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) {
|
||||
s->count_lo = 0;
|
||||
s->count_hi = 0;
|
||||
s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%x\n", __func__, (int)offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps a10_pit_ops = {
|
||||
.read = a10_pit_read,
|
||||
.write = a10_pit_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_a10_pit = {
|
||||
.name = "a10.pit",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(irq_enable, AwA10PITState),
|
||||
VMSTATE_UINT32(irq_status, AwA10PITState),
|
||||
VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR),
|
||||
VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR),
|
||||
VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR),
|
||||
VMSTATE_UINT32(watch_dog_mode, AwA10PITState),
|
||||
VMSTATE_UINT32(watch_dog_control, AwA10PITState),
|
||||
VMSTATE_UINT32(count_lo, AwA10PITState),
|
||||
VMSTATE_UINT32(count_hi, AwA10PITState),
|
||||
VMSTATE_UINT32(count_ctl, AwA10PITState),
|
||||
VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void a10_pit_reset(DeviceState *dev)
|
||||
{
|
||||
AwA10PITState *s = AW_A10_PIT(dev);
|
||||
uint8_t i;
|
||||
|
||||
s->irq_enable = 0;
|
||||
s->irq_status = 0;
|
||||
for (i = 0; i < 6; i++) {
|
||||
s->control[i] = AW_A10_PIT_DEFAULT_CLOCK;
|
||||
s->interval[i] = 0;
|
||||
s->count[i] = 0;
|
||||
ptimer_stop(s->timer[i]);
|
||||
}
|
||||
s->watch_dog_mode = 0;
|
||||
s->watch_dog_control = 0;
|
||||
s->count_lo = 0;
|
||||
s->count_hi = 0;
|
||||
s->count_ctl = 0;
|
||||
}
|
||||
|
||||
static void a10_pit_timer_cb(void *opaque)
|
||||
{
|
||||
AwA10PITState *s = AW_A10_PIT(opaque);
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
|
||||
if (s->control[i] & AW_A10_PIT_TIMER_EN) {
|
||||
s->irq_status |= 1 << i;
|
||||
if (s->control[i] & AW_A10_PIT_TIMER_MODE) {
|
||||
ptimer_stop(s->timer[i]);
|
||||
s->control[i] &= ~AW_A10_PIT_TIMER_EN;
|
||||
}
|
||||
qemu_irq_pulse(s->irq[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void a10_pit_init(Object *obj)
|
||||
{
|
||||
AwA10PITState *s = AW_A10_PIT(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
QEMUBH * bh[AW_A10_PIT_TIMER_NR];
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
|
||||
sysbus_init_irq(sbd, &s->irq[i]);
|
||||
}
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s,
|
||||
TYPE_AW_A10_PIT, 0x400);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
|
||||
bh[i] = qemu_bh_new(a10_pit_timer_cb, s);
|
||||
s->timer[i] = ptimer_init(bh[i]);
|
||||
ptimer_set_freq(s->timer[i], 240000);
|
||||
}
|
||||
}
|
||||
|
||||
static void a10_pit_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = a10_pit_reset;
|
||||
dc->desc = "allwinner a10 timer";
|
||||
dc->vmsd = &vmstate_a10_pit;
|
||||
}
|
||||
|
||||
static const TypeInfo a10_pit_info = {
|
||||
.name = TYPE_AW_A10_PIT,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AwA10PITState),
|
||||
.instance_init = a10_pit_init,
|
||||
.class_init = a10_pit_class_init,
|
||||
};
|
||||
|
||||
static void a10_register_types(void)
|
||||
{
|
||||
type_register_static(&a10_pit_info);
|
||||
}
|
||||
|
||||
type_init(a10_register_types);
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* QEMU model of the Canon DIGIC timer block.
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This model is based on reverse engineering efforts
|
||||
* made by CHDK (http://chdk.wikia.com) and
|
||||
* Magic Lantern (http://www.magiclantern.fm) projects
|
||||
* contributors.
|
||||
*
|
||||
* See "Timer/Clock Module" docs here:
|
||||
* http://magiclantern.wikia.com/wiki/Register_Map
|
||||
*
|
||||
* The QEMU model of the OSTimer in PKUnity SoC by Guan Xuetao
|
||||
* is used as a template.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/ptimer.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
#include "hw/timer/digic-timer.h"
|
||||
|
||||
static const VMStateDescription vmstate_digic_timer = {
|
||||
.name = "digic.timer",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PTIMER(ptimer, DigicTimerState),
|
||||
VMSTATE_UINT32(control, DigicTimerState),
|
||||
VMSTATE_UINT32(relvalue, DigicTimerState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void digic_timer_reset(DeviceState *dev)
|
||||
{
|
||||
DigicTimerState *s = DIGIC_TIMER(dev);
|
||||
|
||||
ptimer_stop(s->ptimer);
|
||||
s->control = 0;
|
||||
s->relvalue = 0;
|
||||
}
|
||||
|
||||
static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
DigicTimerState *s = opaque;
|
||||
uint64_t ret = 0;
|
||||
|
||||
switch (offset) {
|
||||
case DIGIC_TIMER_CONTROL:
|
||||
ret = s->control;
|
||||
break;
|
||||
case DIGIC_TIMER_RELVALUE:
|
||||
ret = s->relvalue;
|
||||
break;
|
||||
case DIGIC_TIMER_VALUE:
|
||||
ret = ptimer_get_count(s->ptimer) & 0xffff;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"digic-timer: read access to unknown register 0x"
|
||||
TARGET_FMT_plx, offset);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void digic_timer_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
DigicTimerState *s = opaque;
|
||||
|
||||
switch (offset) {
|
||||
case DIGIC_TIMER_CONTROL:
|
||||
if (value & DIGIC_TIMER_CONTROL_RST) {
|
||||
digic_timer_reset((DeviceState *)s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (value & DIGIC_TIMER_CONTROL_EN) {
|
||||
ptimer_run(s->ptimer, 0);
|
||||
}
|
||||
|
||||
s->control = (uint32_t)value;
|
||||
break;
|
||||
|
||||
case DIGIC_TIMER_RELVALUE:
|
||||
s->relvalue = extract32(value, 0, 16);
|
||||
ptimer_set_limit(s->ptimer, s->relvalue, 1);
|
||||
break;
|
||||
|
||||
case DIGIC_TIMER_VALUE:
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"digic-timer: read access to unknown register 0x"
|
||||
TARGET_FMT_plx, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps digic_timer_ops = {
|
||||
.read = digic_timer_read,
|
||||
.write = digic_timer_write,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void digic_timer_init(Object *obj)
|
||||
{
|
||||
DigicTimerState *s = DIGIC_TIMER(obj);
|
||||
|
||||
s->ptimer = ptimer_init(NULL);
|
||||
|
||||
/*
|
||||
* FIXME: there is no documentation on Digic timer
|
||||
* frequency setup so let it always run at 1 MHz
|
||||
*/
|
||||
ptimer_set_freq(s->ptimer, 1 * 1000 * 1000);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s,
|
||||
TYPE_DIGIC_TIMER, 0x100);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
|
||||
}
|
||||
|
||||
static void digic_timer_class_init(ObjectClass *klass, void *class_data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = digic_timer_reset;
|
||||
dc->vmsd = &vmstate_digic_timer;
|
||||
}
|
||||
|
||||
static const TypeInfo digic_timer_info = {
|
||||
.name = TYPE_DIGIC_TIMER,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(DigicTimerState),
|
||||
.instance_init = digic_timer_init,
|
||||
.class_init = digic_timer_class_init,
|
||||
};
|
||||
|
||||
static void digic_timer_register_type(void)
|
||||
{
|
||||
type_register_static(&digic_timer_info);
|
||||
}
|
||||
|
||||
type_init(digic_timer_register_type)
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef ALLWINNER_H_
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/arm/arm.h"
|
||||
#include "hw/timer/allwinner-a10-pit.h"
|
||||
#include "hw/intc/allwinner-a10-pic.h"
|
||||
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
||||
#define AW_A10_PIC_REG_BASE 0x01c20400
|
||||
#define AW_A10_PIT_REG_BASE 0x01c20c00
|
||||
#define AW_A10_UART0_REG_BASE 0x01c28000
|
||||
|
||||
#define AW_A10_SDRAM_BASE 0x40000000
|
||||
|
||||
#define TYPE_AW_A10 "allwinner-a10"
|
||||
#define AW_A10(obj) OBJECT_CHECK(AwA10State, (obj), TYPE_AW_A10)
|
||||
|
||||
typedef struct AwA10State {
|
||||
/*< private >*/
|
||||
DeviceState parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
ARMCPU cpu;
|
||||
qemu_irq irq[AW_A10_PIC_INT_NR];
|
||||
AwA10PITState timer;
|
||||
AwA10PICState intc;
|
||||
} AwA10State;
|
||||
|
||||
#define ALLWINNER_H_
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Misc Canon DIGIC declarations.
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_ARM_DIGIC_H
|
||||
#define HW_ARM_DIGIC_H
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#include "hw/timer/digic-timer.h"
|
||||
#include "hw/char/digic-uart.h"
|
||||
|
||||
#define TYPE_DIGIC "digic"
|
||||
|
||||
#define DIGIC(obj) OBJECT_CHECK(DigicState, (obj), TYPE_DIGIC)
|
||||
|
||||
#define DIGIC4_NB_TIMERS 3
|
||||
|
||||
typedef struct DigicState {
|
||||
/*< private >*/
|
||||
DeviceState parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
ARMCPU cpu;
|
||||
|
||||
DigicTimerState timer[DIGIC4_NB_TIMERS];
|
||||
DigicUartState uart;
|
||||
} DigicState;
|
||||
|
||||
#endif /* HW_ARM_DIGIC_H */
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Canon DIGIC UART block declarations.
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_CHAR_DIGIC_UART_H
|
||||
#define HW_CHAR_DIGIC_UART_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/typedefs.h"
|
||||
|
||||
#define TYPE_DIGIC_UART "digic-uart"
|
||||
#define DIGIC_UART(obj) \
|
||||
OBJECT_CHECK(DigicUartState, (obj), TYPE_DIGIC_UART)
|
||||
|
||||
enum {
|
||||
R_TX = 0x00,
|
||||
R_RX,
|
||||
R_ST = (0x14 >> 2),
|
||||
R_MAX
|
||||
};
|
||||
|
||||
typedef struct DigicUartState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion regs_region;
|
||||
CharDriverState *chr;
|
||||
|
||||
uint32_t reg_rx;
|
||||
uint32_t reg_st;
|
||||
} DigicUartState;
|
||||
|
||||
#endif /* HW_CHAR_DIGIC_UART_H */
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef AW_A10_PIC_H
|
||||
#define AW_A10_PIC_H
|
||||
|
||||
#define TYPE_AW_A10_PIC "allwinner-a10-pic"
|
||||
#define AW_A10_PIC(obj) OBJECT_CHECK(AwA10PICState, (obj), TYPE_AW_A10_PIC)
|
||||
|
||||
#define AW_A10_PIC_VECTOR 0
|
||||
#define AW_A10_PIC_BASE_ADDR 4
|
||||
#define AW_A10_PIC_PROTECT 8
|
||||
#define AW_A10_PIC_NMI 0xc
|
||||
#define AW_A10_PIC_IRQ_PENDING 0x10
|
||||
#define AW_A10_PIC_FIQ_PENDING 0x20
|
||||
#define AW_A10_PIC_SELECT 0x30
|
||||
#define AW_A10_PIC_ENABLE 0x40
|
||||
#define AW_A10_PIC_MASK 0x50
|
||||
|
||||
#define AW_A10_PIC_INT_NR 95
|
||||
#define AW_A10_PIC_REG_NUM DIV_ROUND_UP(AW_A10_PIC_INT_NR, 32)
|
||||
|
||||
typedef struct AwA10PICState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
MemoryRegion iomem;
|
||||
qemu_irq parent_fiq;
|
||||
qemu_irq parent_irq;
|
||||
|
||||
uint32_t vector;
|
||||
uint32_t base_addr;
|
||||
uint32_t protect;
|
||||
uint32_t nmi;
|
||||
uint32_t irq_pending[AW_A10_PIC_REG_NUM];
|
||||
uint32_t fiq_pending[AW_A10_PIC_REG_NUM];
|
||||
uint32_t select[AW_A10_PIC_REG_NUM];
|
||||
uint32_t enable[AW_A10_PIC_REG_NUM];
|
||||
uint32_t mask[AW_A10_PIC_REG_NUM];
|
||||
/*priority setting here*/
|
||||
} AwA10PICState;
|
||||
|
||||
#endif
|
|
@ -36,4 +36,8 @@ extern const VMStateDescription vmstate_ptimer;
|
|||
.offset = vmstate_offset_pointer(_state, _field, ptimer_state), \
|
||||
}
|
||||
|
||||
#define VMSTATE_PTIMER_ARRAY(_f, _s, _n) \
|
||||
VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(_f, _s, _n, 0, \
|
||||
vmstate_ptimer, ptimer_state)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef AW_A10_PIT_H
|
||||
#define AW_A10_PIT_H
|
||||
|
||||
#include "hw/ptimer.h"
|
||||
|
||||
#define TYPE_AW_A10_PIT "allwinner-A10-timer"
|
||||
#define AW_A10_PIT(obj) OBJECT_CHECK(AwA10PITState, (obj), TYPE_AW_A10_PIT)
|
||||
|
||||
#define AW_A10_PIT_TIMER_NR 6
|
||||
#define AW_A10_PIT_TIMER_IRQ 0x1
|
||||
#define AW_A10_PIT_WDOG_IRQ 0x100
|
||||
|
||||
#define AW_A10_PIT_TIMER_IRQ_EN 0
|
||||
#define AW_A10_PIT_TIMER_IRQ_ST 0x4
|
||||
|
||||
#define AW_A10_PIT_TIMER_CONTROL 0x0
|
||||
#define AW_A10_PIT_TIMER_EN 0x1
|
||||
#define AW_A10_PIT_TIMER_RELOAD 0x2
|
||||
#define AW_A10_PIT_TIMER_MODE 0x80
|
||||
|
||||
#define AW_A10_PIT_TIMER_INTERVAL 0x4
|
||||
#define AW_A10_PIT_TIMER_COUNT 0x8
|
||||
#define AW_A10_PIT_WDOG_CONTROL 0x90
|
||||
#define AW_A10_PIT_WDOG_MODE 0x94
|
||||
|
||||
#define AW_A10_PIT_COUNT_CTL 0xa0
|
||||
#define AW_A10_PIT_COUNT_RL_EN 0x2
|
||||
#define AW_A10_PIT_COUNT_CLR_EN 0x1
|
||||
#define AW_A10_PIT_COUNT_LO 0xa4
|
||||
#define AW_A10_PIT_COUNT_HI 0xa8
|
||||
|
||||
#define AW_A10_PIT_TIMER_BASE 0x10
|
||||
#define AW_A10_PIT_TIMER_BASE_END \
|
||||
(AW_A10_PIT_TIMER_BASE * 6 + AW_A10_PIT_TIMER_COUNT)
|
||||
|
||||
#define AW_A10_PIT_DEFAULT_CLOCK 0x4
|
||||
|
||||
typedef struct AwA10PITState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
qemu_irq irq[AW_A10_PIT_TIMER_NR];
|
||||
ptimer_state * timer[AW_A10_PIT_TIMER_NR];
|
||||
MemoryRegion iomem;
|
||||
|
||||
uint32_t irq_enable;
|
||||
uint32_t irq_status;
|
||||
uint32_t control[AW_A10_PIT_TIMER_NR];
|
||||
uint32_t interval[AW_A10_PIT_TIMER_NR];
|
||||
uint32_t count[AW_A10_PIT_TIMER_NR];
|
||||
uint32_t watch_dog_mode;
|
||||
uint32_t watch_dog_control;
|
||||
uint32_t count_lo;
|
||||
uint32_t count_hi;
|
||||
uint32_t count_ctl;
|
||||
} AwA10PITState;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Canon DIGIC timer block declarations.
|
||||
*
|
||||
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_TIMER_DIGIC_TIMER_H
|
||||
#define HW_TIMER_DIGIC_TIMER_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/typedefs.h"
|
||||
#include "hw/ptimer.h"
|
||||
|
||||
#define TYPE_DIGIC_TIMER "digic-timer"
|
||||
#define DIGIC_TIMER(obj) OBJECT_CHECK(DigicTimerState, (obj), TYPE_DIGIC_TIMER)
|
||||
|
||||
#define DIGIC_TIMER_CONTROL 0x00
|
||||
#define DIGIC_TIMER_CONTROL_RST 0x80000000
|
||||
#define DIGIC_TIMER_CONTROL_EN 0x00000001
|
||||
#define DIGIC_TIMER_RELVALUE 0x08
|
||||
#define DIGIC_TIMER_VALUE 0x0c
|
||||
|
||||
typedef struct DigicTimerState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion iomem;
|
||||
ptimer_state *ptimer;
|
||||
|
||||
uint32_t control;
|
||||
uint32_t relvalue;
|
||||
} DigicTimerState;
|
||||
|
||||
#endif /* HW_TIMER_DIGIC_TIMER_H */
|
|
@ -339,6 +339,16 @@ extern const VMStateInfo vmstate_info_bitmap;
|
|||
.offset = vmstate_offset_array(_state, _field, _type, _num), \
|
||||
}
|
||||
|
||||
#define VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(_f, _s, _n, _v, _vmsd, _type) { \
|
||||
.name = (stringify(_f)), \
|
||||
.version_id = (_v), \
|
||||
.num = (_n), \
|
||||
.vmsd = &(_vmsd), \
|
||||
.size = sizeof(_type *), \
|
||||
.flags = VMS_ARRAY|VMS_STRUCT|VMS_ARRAY_OF_POINTER, \
|
||||
.offset = vmstate_offset_array(_s, _f, _type*, _n), \
|
||||
}
|
||||
|
||||
#define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, _vmsd, _type) { \
|
||||
.name = (stringify(_field)), \
|
||||
.num = (_num), \
|
||||
|
|
|
@ -227,6 +227,38 @@ static inline int cto64(uint64_t val)
|
|||
return ctz64(~val);
|
||||
}
|
||||
|
||||
/**
|
||||
* clrsb32 - count leading redundant sign bits in a 32-bit value.
|
||||
* @val: The value to search
|
||||
*
|
||||
* Returns the number of bits following the sign bit that are equal to it.
|
||||
* No special cases; output range is [0-31].
|
||||
*/
|
||||
static inline int clrsb32(uint32_t val)
|
||||
{
|
||||
#if QEMU_GNUC_PREREQ(4, 7)
|
||||
return __builtin_clrsb(val);
|
||||
#else
|
||||
return clz32(val ^ ((int32_t)val >> 1)) - 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* clrsb64 - count leading redundant sign bits in a 64-bit value.
|
||||
* @val: The value to search
|
||||
*
|
||||
* Returns the number of bits following the sign bit that are equal to it.
|
||||
* No special cases; output range is [0-63].
|
||||
*/
|
||||
static inline int clrsb64(uint64_t val)
|
||||
{
|
||||
#if QEMU_GNUC_PREREQ(4, 7)
|
||||
return __builtin_clrsbll(val);
|
||||
#else
|
||||
return clz64(val ^ ((int64_t)val >> 1)) - 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* ctpop8 - count the population of one bits in an 8-bit value.
|
||||
* @val: The value to search
|
||||
|
|
|
@ -1171,7 +1171,7 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf,
|
|||
}
|
||||
__put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
|
||||
__put_user(env->pc, &sf->uc.tuc_mcontext.pc);
|
||||
__put_user(env->pstate, &sf->uc.tuc_mcontext.pstate);
|
||||
__put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate);
|
||||
|
||||
__put_user(/*current->thread.fault_address*/ 0,
|
||||
&sf->uc.tuc_mcontext.fault_address);
|
||||
|
@ -1210,6 +1210,7 @@ static int target_restore_sigframe(CPUARMState *env,
|
|||
struct target_aux_context *aux =
|
||||
(struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
|
||||
uint32_t magic, size;
|
||||
uint64_t pstate;
|
||||
|
||||
target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
|
@ -1220,7 +1221,8 @@ static int target_restore_sigframe(CPUARMState *env,
|
|||
|
||||
__get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
|
||||
__get_user(env->pc, &sf->uc.tuc_mcontext.pc);
|
||||
__get_user(env->pstate, &sf->uc.tuc_mcontext.pstate);
|
||||
__get_user(pstate, &sf->uc.tuc_mcontext.pstate);
|
||||
pstate_write(env, pstate);
|
||||
|
||||
__get_user(magic, &aux->fpsimd.head.magic);
|
||||
__get_user(size, &aux->fpsimd.head.size);
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
obj-y += arm-semi.o
|
||||
obj-$(CONFIG_SOFTMMU) += machine.o
|
||||
obj-$(CONFIG_KVM) += kvm.o
|
||||
obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
|
||||
obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
obj-y += translate.o op_helper.o helper.o cpu.o
|
||||
obj-y += neon_helper.o iwmmxt_helper.o
|
||||
obj-y += gdbstub.o
|
||||
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o
|
||||
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
|
||||
obj-y += crypto_helper.o
|
||||
|
|
|
@ -139,6 +139,7 @@ typedef struct ARMCPU {
|
|||
uint32_t ccsidr[16];
|
||||
uint32_t reset_cbar;
|
||||
uint32_t reset_auxcr;
|
||||
bool reset_hivecs;
|
||||
} ARMCPU;
|
||||
|
||||
#define TYPE_AARCH64_CPU "aarch64-cpu"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "cpu.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#include "hw/loader.h"
|
||||
#endif
|
||||
|
@ -88,6 +89,12 @@ static void arm_cpu_reset(CPUState *s)
|
|||
if (arm_feature(env, ARM_FEATURE_AARCH64)) {
|
||||
/* 64 bit CPUs always start in 64 bit mode */
|
||||
env->aarch64 = 1;
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
env->pstate = PSTATE_MODE_EL0t;
|
||||
#else
|
||||
env->pstate = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F
|
||||
| PSTATE_MODE_EL1h;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
@ -120,6 +127,11 @@ static void arm_cpu_reset(CPUState *s)
|
|||
env->regs[15] = pc & ~1;
|
||||
}
|
||||
}
|
||||
|
||||
if (env->cp15.c1_sys & (1 << 13)) {
|
||||
env->regs[15] = 0xFFFF0000;
|
||||
}
|
||||
|
||||
env->vfp.xregs[ARM_VFP_FPEXC] = 0;
|
||||
#endif
|
||||
set_flush_to_zero(1, &env->vfp.standard_fp_status);
|
||||
|
@ -231,6 +243,30 @@ static void arm_cpu_initfn(Object *obj)
|
|||
}
|
||||
}
|
||||
|
||||
static Property arm_cpu_reset_cbar_property =
|
||||
DEFINE_PROP_UINT32("reset-cbar", ARMCPU, reset_cbar, 0);
|
||||
|
||||
static Property arm_cpu_reset_hivecs_property =
|
||||
DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false);
|
||||
|
||||
static void arm_cpu_post_init(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
Error *err = NULL;
|
||||
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_CBAR)) {
|
||||
qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property,
|
||||
&err);
|
||||
assert_no_error(err);
|
||||
}
|
||||
|
||||
if (!arm_feature(&cpu->env, ARM_FEATURE_M)) {
|
||||
qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property,
|
||||
&err);
|
||||
assert_no_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_cpu_finalizefn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
@ -249,6 +285,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
|||
set_feature(env, ARM_FEATURE_V7);
|
||||
set_feature(env, ARM_FEATURE_ARM_DIV);
|
||||
set_feature(env, ARM_FEATURE_LPAE);
|
||||
set_feature(env, ARM_FEATURE_V8_AES);
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_V7)) {
|
||||
set_feature(env, ARM_FEATURE_VAPA);
|
||||
|
@ -290,6 +327,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
|||
set_feature(env, ARM_FEATURE_PXN);
|
||||
}
|
||||
|
||||
if (cpu->reset_hivecs) {
|
||||
cpu->reset_sctlr |= (1 << 13);
|
||||
}
|
||||
|
||||
register_cp_regs_for_features(cpu);
|
||||
arm_cpu_register_gdb_regs_for_features(cpu);
|
||||
|
||||
|
@ -616,6 +657,7 @@ static void cortex_a9_initfn(Object *obj)
|
|||
* and valid configurations; we don't model A9UP).
|
||||
*/
|
||||
set_feature(&cpu->env, ARM_FEATURE_V7MP);
|
||||
set_feature(&cpu->env, ARM_FEATURE_CBAR);
|
||||
cpu->midr = 0x410fc090;
|
||||
cpu->reset_fpsid = 0x41033090;
|
||||
cpu->mvfr0 = 0x11110222;
|
||||
|
@ -638,15 +680,7 @@ static void cortex_a9_initfn(Object *obj)
|
|||
cpu->clidr = (1 << 27) | (1 << 24) | 3;
|
||||
cpu->ccsidr[0] = 0xe00fe015; /* 16k L1 dcache. */
|
||||
cpu->ccsidr[1] = 0x200fe015; /* 16k L1 icache. */
|
||||
{
|
||||
ARMCPRegInfo cbar = {
|
||||
.name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4,
|
||||
.opc2 = 0, .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address)
|
||||
};
|
||||
define_one_arm_cp_reg(cpu, &cbar);
|
||||
define_arm_cp_regs(cpu, cortexa9_cp_reginfo);
|
||||
}
|
||||
define_arm_cp_regs(cpu, cortexa9_cp_reginfo);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
@ -685,6 +719,7 @@ static void cortex_a15_initfn(Object *obj)
|
|||
set_feature(&cpu->env, ARM_FEATURE_ARM_DIV);
|
||||
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
|
||||
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
|
||||
set_feature(&cpu->env, ARM_FEATURE_CBAR);
|
||||
set_feature(&cpu->env, ARM_FEATURE_LPAE);
|
||||
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15;
|
||||
cpu->midr = 0x412fc0f1;
|
||||
|
@ -999,6 +1034,7 @@ static const TypeInfo arm_cpu_type_info = {
|
|||
.parent = TYPE_CPU,
|
||||
.instance_size = sizeof(ARMCPU),
|
||||
.instance_init = arm_cpu_initfn,
|
||||
.instance_post_init = arm_cpu_post_init,
|
||||
.instance_finalize = arm_cpu_finalizefn,
|
||||
.abstract = true,
|
||||
.class_size = sizeof(ARMCPUClass),
|
||||
|
|
100
target-arm/cpu.h
100
target-arm/cpu.h
|
@ -113,8 +113,15 @@ typedef struct CPUARMState {
|
|||
/* Regs for A64 mode. */
|
||||
uint64_t xregs[32];
|
||||
uint64_t pc;
|
||||
/* TODO: pstate doesn't correspond to an architectural register;
|
||||
* it would be better modelled as the underlying fields.
|
||||
/* PSTATE isn't an architectural register for ARMv8. However, it is
|
||||
* convenient for us to assemble the underlying state into a 32 bit format
|
||||
* identical to the architectural format used for the SPSR. (This is also
|
||||
* what the Linux kernel's 'pstate' field in signal handlers and KVM's
|
||||
* 'pstate' register are.) Of the PSTATE bits:
|
||||
* NZCV are kept in the split out env->CF/VF/NF/ZF, (which have the same
|
||||
* semantics as for AArch32, as described in the comments on each field)
|
||||
* nRW (also known as M[4]) is kept, inverted, in env->aarch64
|
||||
* all other bits are stored in their correct places in env->pstate
|
||||
*/
|
||||
uint32_t pstate;
|
||||
uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */
|
||||
|
@ -309,15 +316,6 @@ static inline bool is_a64(CPUARMState *env)
|
|||
return env->aarch64;
|
||||
}
|
||||
|
||||
#define PSTATE_N_SHIFT 3
|
||||
#define PSTATE_N (1 << PSTATE_N_SHIFT)
|
||||
#define PSTATE_Z_SHIFT 2
|
||||
#define PSTATE_Z (1 << PSTATE_Z_SHIFT)
|
||||
#define PSTATE_C_SHIFT 1
|
||||
#define PSTATE_C (1 << PSTATE_C_SHIFT)
|
||||
#define PSTATE_V_SHIFT 0
|
||||
#define PSTATE_V (1 << PSTATE_V_SHIFT)
|
||||
|
||||
/* you can call this signal handler from your SIGBUS and SIGSEGV
|
||||
signal handlers to inform the virtual CPU of exceptions. non zero
|
||||
is returned if the signal was handled by the virtual CPU. */
|
||||
|
@ -352,6 +350,56 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
|
|||
/* Execution state bits. MRS read as zero, MSR writes ignored. */
|
||||
#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
|
||||
|
||||
/* Bit definitions for ARMv8 SPSR (PSTATE) format.
|
||||
* Only these are valid when in AArch64 mode; in
|
||||
* AArch32 mode SPSRs are basically CPSR-format.
|
||||
*/
|
||||
#define PSTATE_M (0xFU)
|
||||
#define PSTATE_nRW (1U << 4)
|
||||
#define PSTATE_F (1U << 6)
|
||||
#define PSTATE_I (1U << 7)
|
||||
#define PSTATE_A (1U << 8)
|
||||
#define PSTATE_D (1U << 9)
|
||||
#define PSTATE_IL (1U << 20)
|
||||
#define PSTATE_SS (1U << 21)
|
||||
#define PSTATE_V (1U << 28)
|
||||
#define PSTATE_C (1U << 29)
|
||||
#define PSTATE_Z (1U << 30)
|
||||
#define PSTATE_N (1U << 31)
|
||||
#define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
|
||||
#define CACHED_PSTATE_BITS (PSTATE_NZCV)
|
||||
/* Mode values for AArch64 */
|
||||
#define PSTATE_MODE_EL3h 13
|
||||
#define PSTATE_MODE_EL3t 12
|
||||
#define PSTATE_MODE_EL2h 9
|
||||
#define PSTATE_MODE_EL2t 8
|
||||
#define PSTATE_MODE_EL1h 5
|
||||
#define PSTATE_MODE_EL1t 4
|
||||
#define PSTATE_MODE_EL0t 0
|
||||
|
||||
/* Return the current PSTATE value. For the moment we don't support 32<->64 bit
|
||||
* interprocessing, so we don't attempt to sync with the cpsr state used by
|
||||
* the 32 bit decoder.
|
||||
*/
|
||||
static inline uint32_t pstate_read(CPUARMState *env)
|
||||
{
|
||||
int ZF;
|
||||
|
||||
ZF = (env->ZF == 0);
|
||||
return (env->NF & 0x80000000) | (ZF << 30)
|
||||
| (env->CF << 29) | ((env->VF & 0x80000000) >> 3)
|
||||
| env->pstate;
|
||||
}
|
||||
|
||||
static inline void pstate_write(CPUARMState *env, uint32_t val)
|
||||
{
|
||||
env->ZF = (~val) & PSTATE_Z;
|
||||
env->NF = val;
|
||||
env->CF = (val >> 29) & 1;
|
||||
env->VF = (val << 3) & 0x80000000;
|
||||
env->pstate = val & ~CACHED_PSTATE_BITS;
|
||||
}
|
||||
|
||||
/* Return the current CPSR value. */
|
||||
uint32_t cpsr_read(CPUARMState *env);
|
||||
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
|
||||
|
@ -399,6 +447,34 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
|||
uint32_t vfp_get_fpscr(CPUARMState *env);
|
||||
void vfp_set_fpscr(CPUARMState *env, uint32_t val);
|
||||
|
||||
/* For A64 the FPSCR is split into two logically distinct registers,
|
||||
* FPCR and FPSR. However since they still use non-overlapping bits
|
||||
* we store the underlying state in fpscr and just mask on read/write.
|
||||
*/
|
||||
#define FPSR_MASK 0xf800009f
|
||||
#define FPCR_MASK 0x07f79f00
|
||||
static inline uint32_t vfp_get_fpsr(CPUARMState *env)
|
||||
{
|
||||
return vfp_get_fpscr(env) & FPSR_MASK;
|
||||
}
|
||||
|
||||
static inline void vfp_set_fpsr(CPUARMState *env, uint32_t val)
|
||||
{
|
||||
uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPSR_MASK) | (val & FPSR_MASK);
|
||||
vfp_set_fpscr(env, new_fpscr);
|
||||
}
|
||||
|
||||
static inline uint32_t vfp_get_fpcr(CPUARMState *env)
|
||||
{
|
||||
return vfp_get_fpscr(env) & FPCR_MASK;
|
||||
}
|
||||
|
||||
static inline void vfp_set_fpcr(CPUARMState *env, uint32_t val)
|
||||
{
|
||||
uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPCR_MASK) | (val & FPCR_MASK);
|
||||
vfp_set_fpscr(env, new_fpscr);
|
||||
}
|
||||
|
||||
enum arm_cpu_mode {
|
||||
ARM_CPU_MODE_USR = 0x10,
|
||||
ARM_CPU_MODE_FIQ = 0x11,
|
||||
|
@ -467,6 +543,8 @@ enum arm_features {
|
|||
ARM_FEATURE_LPAE, /* has Large Physical Address Extension */
|
||||
ARM_FEATURE_V8,
|
||||
ARM_FEATURE_AARCH64, /* supports 64 bit mode */
|
||||
ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */
|
||||
ARM_FEATURE_CBAR, /* has cp15 CBAR */
|
||||
};
|
||||
|
||||
static inline int arm_feature(CPUARMState *env, int feature)
|
||||
|
|
|
@ -68,11 +68,22 @@ static void aarch64_cpu_finalizefn(Object *obj)
|
|||
{
|
||||
}
|
||||
|
||||
static void aarch64_cpu_set_pc(CPUState *cs, vaddr value)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
/*
|
||||
* TODO: this will need updating for system emulation,
|
||||
* when the core may be in AArch32 mode.
|
||||
*/
|
||||
cpu->env.pc = value;
|
||||
}
|
||||
|
||||
static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
cc->dump_state = aarch64_cpu_dump_state;
|
||||
cc->set_pc = aarch64_cpu_set_pc;
|
||||
cc->gdb_read_register = aarch64_cpu_gdb_read_register;
|
||||
cc->gdb_write_register = aarch64_cpu_gdb_write_register;
|
||||
cc->gdb_num_core_regs = 34;
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* crypto_helper.c - emulate v8 Crypto Extensions instructions
|
||||
*
|
||||
* Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "helper.h"
|
||||
|
||||
union AES_STATE {
|
||||
uint8_t bytes[16];
|
||||
uint32_t cols[4];
|
||||
uint64_t l[2];
|
||||
};
|
||||
|
||||
void HELPER(crypto_aese)(CPUARMState *env, uint32_t rd, uint32_t rm,
|
||||
uint32_t decrypt)
|
||||
{
|
||||
static uint8_t const sbox[][256] = { {
|
||||
/* S-box for encryption */
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
|
||||
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
||||
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
|
||||
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
|
||||
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
||||
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
|
||||
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
||||
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
|
||||
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
|
||||
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
||||
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
|
||||
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
|
||||
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
||||
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
|
||||
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
|
||||
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
|
||||
}, {
|
||||
/* S-box for decryption */
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
|
||||
0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
|
||||
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
|
||||
0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
|
||||
0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
||||
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
|
||||
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
||||
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
|
||||
0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
|
||||
0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
|
||||
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
|
||||
0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
|
||||
0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
||||
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
|
||||
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
||||
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
|
||||
0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
|
||||
0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
|
||||
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
|
||||
0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
|
||||
0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
|
||||
} };
|
||||
static uint8_t const shift[][16] = {
|
||||
/* ShiftRows permutation vector for encryption */
|
||||
{ 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 },
|
||||
/* ShiftRows permutation vector for decryption */
|
||||
{ 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 },
|
||||
};
|
||||
union AES_STATE rk = { .l = {
|
||||
float64_val(env->vfp.regs[rm]),
|
||||
float64_val(env->vfp.regs[rm + 1])
|
||||
} };
|
||||
union AES_STATE st = { .l = {
|
||||
float64_val(env->vfp.regs[rd]),
|
||||
float64_val(env->vfp.regs[rd + 1])
|
||||
} };
|
||||
int i;
|
||||
|
||||
assert(decrypt < 2);
|
||||
|
||||
/* xor state vector with round key */
|
||||
rk.l[0] ^= st.l[0];
|
||||
rk.l[1] ^= st.l[1];
|
||||
|
||||
/* combine ShiftRows operation and sbox substitution */
|
||||
for (i = 0; i < 16; i++) {
|
||||
st.bytes[i] = sbox[decrypt][rk.bytes[shift[decrypt][i]]];
|
||||
}
|
||||
|
||||
env->vfp.regs[rd] = make_float64(st.l[0]);
|
||||
env->vfp.regs[rd + 1] = make_float64(st.l[1]);
|
||||
}
|
||||
|
||||
void HELPER(crypto_aesmc)(CPUARMState *env, uint32_t rd, uint32_t rm,
|
||||
uint32_t decrypt)
|
||||
{
|
||||
static uint32_t const mc[][256] = { {
|
||||
/* MixColumns lookup table */
|
||||
0x00000000, 0x03010102, 0x06020204, 0x05030306,
|
||||
0x0c040408, 0x0f05050a, 0x0a06060c, 0x0907070e,
|
||||
0x18080810, 0x1b090912, 0x1e0a0a14, 0x1d0b0b16,
|
||||
0x140c0c18, 0x170d0d1a, 0x120e0e1c, 0x110f0f1e,
|
||||
0x30101020, 0x33111122, 0x36121224, 0x35131326,
|
||||
0x3c141428, 0x3f15152a, 0x3a16162c, 0x3917172e,
|
||||
0x28181830, 0x2b191932, 0x2e1a1a34, 0x2d1b1b36,
|
||||
0x241c1c38, 0x271d1d3a, 0x221e1e3c, 0x211f1f3e,
|
||||
0x60202040, 0x63212142, 0x66222244, 0x65232346,
|
||||
0x6c242448, 0x6f25254a, 0x6a26264c, 0x6927274e,
|
||||
0x78282850, 0x7b292952, 0x7e2a2a54, 0x7d2b2b56,
|
||||
0x742c2c58, 0x772d2d5a, 0x722e2e5c, 0x712f2f5e,
|
||||
0x50303060, 0x53313162, 0x56323264, 0x55333366,
|
||||
0x5c343468, 0x5f35356a, 0x5a36366c, 0x5937376e,
|
||||
0x48383870, 0x4b393972, 0x4e3a3a74, 0x4d3b3b76,
|
||||
0x443c3c78, 0x473d3d7a, 0x423e3e7c, 0x413f3f7e,
|
||||
0xc0404080, 0xc3414182, 0xc6424284, 0xc5434386,
|
||||
0xcc444488, 0xcf45458a, 0xca46468c, 0xc947478e,
|
||||
0xd8484890, 0xdb494992, 0xde4a4a94, 0xdd4b4b96,
|
||||
0xd44c4c98, 0xd74d4d9a, 0xd24e4e9c, 0xd14f4f9e,
|
||||
0xf05050a0, 0xf35151a2, 0xf65252a4, 0xf55353a6,
|
||||
0xfc5454a8, 0xff5555aa, 0xfa5656ac, 0xf95757ae,
|
||||
0xe85858b0, 0xeb5959b2, 0xee5a5ab4, 0xed5b5bb6,
|
||||
0xe45c5cb8, 0xe75d5dba, 0xe25e5ebc, 0xe15f5fbe,
|
||||
0xa06060c0, 0xa36161c2, 0xa66262c4, 0xa56363c6,
|
||||
0xac6464c8, 0xaf6565ca, 0xaa6666cc, 0xa96767ce,
|
||||
0xb86868d0, 0xbb6969d2, 0xbe6a6ad4, 0xbd6b6bd6,
|
||||
0xb46c6cd8, 0xb76d6dda, 0xb26e6edc, 0xb16f6fde,
|
||||
0x907070e0, 0x937171e2, 0x967272e4, 0x957373e6,
|
||||
0x9c7474e8, 0x9f7575ea, 0x9a7676ec, 0x997777ee,
|
||||
0x887878f0, 0x8b7979f2, 0x8e7a7af4, 0x8d7b7bf6,
|
||||
0x847c7cf8, 0x877d7dfa, 0x827e7efc, 0x817f7ffe,
|
||||
0x9b80801b, 0x98818119, 0x9d82821f, 0x9e83831d,
|
||||
0x97848413, 0x94858511, 0x91868617, 0x92878715,
|
||||
0x8388880b, 0x80898909, 0x858a8a0f, 0x868b8b0d,
|
||||
0x8f8c8c03, 0x8c8d8d01, 0x898e8e07, 0x8a8f8f05,
|
||||
0xab90903b, 0xa8919139, 0xad92923f, 0xae93933d,
|
||||
0xa7949433, 0xa4959531, 0xa1969637, 0xa2979735,
|
||||
0xb398982b, 0xb0999929, 0xb59a9a2f, 0xb69b9b2d,
|
||||
0xbf9c9c23, 0xbc9d9d21, 0xb99e9e27, 0xba9f9f25,
|
||||
0xfba0a05b, 0xf8a1a159, 0xfda2a25f, 0xfea3a35d,
|
||||
0xf7a4a453, 0xf4a5a551, 0xf1a6a657, 0xf2a7a755,
|
||||
0xe3a8a84b, 0xe0a9a949, 0xe5aaaa4f, 0xe6abab4d,
|
||||
0xefacac43, 0xecadad41, 0xe9aeae47, 0xeaafaf45,
|
||||
0xcbb0b07b, 0xc8b1b179, 0xcdb2b27f, 0xceb3b37d,
|
||||
0xc7b4b473, 0xc4b5b571, 0xc1b6b677, 0xc2b7b775,
|
||||
0xd3b8b86b, 0xd0b9b969, 0xd5baba6f, 0xd6bbbb6d,
|
||||
0xdfbcbc63, 0xdcbdbd61, 0xd9bebe67, 0xdabfbf65,
|
||||
0x5bc0c09b, 0x58c1c199, 0x5dc2c29f, 0x5ec3c39d,
|
||||
0x57c4c493, 0x54c5c591, 0x51c6c697, 0x52c7c795,
|
||||
0x43c8c88b, 0x40c9c989, 0x45caca8f, 0x46cbcb8d,
|
||||
0x4fcccc83, 0x4ccdcd81, 0x49cece87, 0x4acfcf85,
|
||||
0x6bd0d0bb, 0x68d1d1b9, 0x6dd2d2bf, 0x6ed3d3bd,
|
||||
0x67d4d4b3, 0x64d5d5b1, 0x61d6d6b7, 0x62d7d7b5,
|
||||
0x73d8d8ab, 0x70d9d9a9, 0x75dadaaf, 0x76dbdbad,
|
||||
0x7fdcdca3, 0x7cdddda1, 0x79dedea7, 0x7adfdfa5,
|
||||
0x3be0e0db, 0x38e1e1d9, 0x3de2e2df, 0x3ee3e3dd,
|
||||
0x37e4e4d3, 0x34e5e5d1, 0x31e6e6d7, 0x32e7e7d5,
|
||||
0x23e8e8cb, 0x20e9e9c9, 0x25eaeacf, 0x26ebebcd,
|
||||
0x2fececc3, 0x2cededc1, 0x29eeeec7, 0x2aefefc5,
|
||||
0x0bf0f0fb, 0x08f1f1f9, 0x0df2f2ff, 0x0ef3f3fd,
|
||||
0x07f4f4f3, 0x04f5f5f1, 0x01f6f6f7, 0x02f7f7f5,
|
||||
0x13f8f8eb, 0x10f9f9e9, 0x15fafaef, 0x16fbfbed,
|
||||
0x1ffcfce3, 0x1cfdfde1, 0x19fefee7, 0x1affffe5,
|
||||
}, {
|
||||
/* Inverse MixColumns lookup table */
|
||||
0x00000000, 0x0b0d090e, 0x161a121c, 0x1d171b12,
|
||||
0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a,
|
||||
0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362,
|
||||
0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a,
|
||||
0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2,
|
||||
0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca,
|
||||
0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382,
|
||||
0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba,
|
||||
0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9,
|
||||
0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1,
|
||||
0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9,
|
||||
0x0fe75793, 0x04ea5e9d, 0x19fd458f, 0x12f04c81,
|
||||
0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029,
|
||||
0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411,
|
||||
0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859,
|
||||
0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61,
|
||||
0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf,
|
||||
0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987,
|
||||
0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf,
|
||||
0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7,
|
||||
0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f,
|
||||
0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967,
|
||||
0x1ed5ae3d, 0x15d8a733, 0x08cfbc21, 0x03c2b52f,
|
||||
0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117,
|
||||
0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664,
|
||||
0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c,
|
||||
0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14,
|
||||
0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c,
|
||||
0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684,
|
||||
0x1132f9ae, 0x1a3ff0a0, 0x0728ebb2, 0x0c25e2bc,
|
||||
0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4,
|
||||
0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc,
|
||||
0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753,
|
||||
0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b,
|
||||
0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23,
|
||||
0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b,
|
||||
0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3,
|
||||
0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b,
|
||||
0x1f6234d1, 0x146f3ddf, 0x097826cd, 0x02752fc3,
|
||||
0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb,
|
||||
0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88,
|
||||
0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0,
|
||||
0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8,
|
||||
0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0,
|
||||
0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68,
|
||||
0x10856342, 0x1b886a4c, 0x069f715e, 0x0d927850,
|
||||
0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418,
|
||||
0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020,
|
||||
0x01b79aec, 0x0aba93e2, 0x17ad88f0, 0x1ca081fe,
|
||||
0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6,
|
||||
0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e,
|
||||
0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6,
|
||||
0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e,
|
||||
0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526,
|
||||
0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e,
|
||||
0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56,
|
||||
0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25,
|
||||
0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d,
|
||||
0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255,
|
||||
0x0e50cd7f, 0x055dc471, 0x184adf63, 0x1347d66d,
|
||||
0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5,
|
||||
0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd,
|
||||
0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5,
|
||||
0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d,
|
||||
} };
|
||||
union AES_STATE st = { .l = {
|
||||
float64_val(env->vfp.regs[rm]),
|
||||
float64_val(env->vfp.regs[rm + 1])
|
||||
} };
|
||||
int i;
|
||||
|
||||
assert(decrypt < 2);
|
||||
|
||||
for (i = 0; i < 16; i += 4) {
|
||||
st.cols[i >> 2] = cpu_to_le32(
|
||||
mc[decrypt][st.bytes[i]] ^
|
||||
rol32(mc[decrypt][st.bytes[i + 1]], 8) ^
|
||||
rol32(mc[decrypt][st.bytes[i + 2]], 16) ^
|
||||
rol32(mc[decrypt][st.bytes[i + 3]], 24));
|
||||
}
|
||||
|
||||
env->vfp.regs[rd] = make_float64(st.l[0]);
|
||||
env->vfp.regs[rd + 1] = make_float64(st.l[1]);
|
||||
}
|
|
@ -37,7 +37,7 @@ int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
|
|||
return gdb_get_reg64(mem_buf, env->pc);
|
||||
break;
|
||||
case 33:
|
||||
return gdb_get_reg32(mem_buf, env->pstate);
|
||||
return gdb_get_reg32(mem_buf, pstate_read(env));
|
||||
}
|
||||
/* Unknown register. */
|
||||
return 0;
|
||||
|
@ -65,7 +65,7 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
|||
return 8;
|
||||
case 33:
|
||||
/* CPSR */
|
||||
env->pstate = tmp;
|
||||
pstate_write(env, tmp);
|
||||
return 4;
|
||||
}
|
||||
/* Unknown register. */
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* AArch64 specific helpers
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "helper.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
/* C2.4.7 Multiply and divide */
|
||||
/* special cases for 0 and LLONG_MIN are mandated by the standard */
|
||||
uint64_t HELPER(udiv64)(uint64_t num, uint64_t den)
|
||||
{
|
||||
if (den == 0) {
|
||||
return 0;
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
|
||||
int64_t HELPER(sdiv64)(int64_t num, int64_t den)
|
||||
{
|
||||
if (den == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (num == LLONG_MIN && den == -1) {
|
||||
return LLONG_MIN;
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
|
||||
uint64_t HELPER(clz64)(uint64_t x)
|
||||
{
|
||||
return clz64(x);
|
||||
}
|
||||
|
||||
uint64_t HELPER(cls64)(uint64_t x)
|
||||
{
|
||||
return clrsb64(x);
|
||||
}
|
||||
|
||||
uint32_t HELPER(cls32)(uint32_t x)
|
||||
{
|
||||
return clrsb32(x);
|
||||
}
|
||||
|
||||
uint64_t HELPER(rbit64)(uint64_t x)
|
||||
{
|
||||
/* assign the correct byte position */
|
||||
x = bswap64(x);
|
||||
|
||||
/* assign the correct nibble position */
|
||||
x = ((x & 0xf0f0f0f0f0f0f0f0ULL) >> 4)
|
||||
| ((x & 0x0f0f0f0f0f0f0f0fULL) << 4);
|
||||
|
||||
/* assign the correct bit position */
|
||||
x = ((x & 0x8888888888888888ULL) >> 3)
|
||||
| ((x & 0x4444444444444444ULL) >> 1)
|
||||
| ((x & 0x2222222222222222ULL) << 1)
|
||||
| ((x & 0x1111111111111111ULL) << 3);
|
||||
|
||||
return x;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* AArch64 specific helper definitions
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
|
||||
DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
|
||||
DEF_HELPER_FLAGS_1(clz64, TCG_CALL_NO_RWG_SE, i64, i64)
|
||||
DEF_HELPER_FLAGS_1(cls64, TCG_CALL_NO_RWG_SE, i64, i64)
|
||||
DEF_HELPER_FLAGS_1(cls32, TCG_CALL_NO_RWG_SE, i32, i32)
|
||||
DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64)
|
|
@ -65,6 +65,48 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int aarch64_fpu_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0 ... 31:
|
||||
/* 128 bit FP register */
|
||||
stfq_le_p(buf, env->vfp.regs[reg * 2]);
|
||||
stfq_le_p(buf + 8, env->vfp.regs[reg * 2 + 1]);
|
||||
return 16;
|
||||
case 32:
|
||||
/* FPSR */
|
||||
stl_p(buf, vfp_get_fpsr(env));
|
||||
return 4;
|
||||
case 33:
|
||||
/* FPCR */
|
||||
stl_p(buf, vfp_get_fpcr(env));
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0 ... 31:
|
||||
/* 128 bit FP register */
|
||||
env->vfp.regs[reg * 2] = ldfq_le_p(buf);
|
||||
env->vfp.regs[reg * 2 + 1] = ldfq_le_p(buf + 8);
|
||||
return 16;
|
||||
case 32:
|
||||
/* FPSR */
|
||||
vfp_set_fpsr(env, ldl_p(buf));
|
||||
return 4;
|
||||
case 33:
|
||||
/* FPCR */
|
||||
vfp_set_fpcr(env, ldl_p(buf));
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int raw_read(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t *value)
|
||||
{
|
||||
|
@ -1338,7 +1380,8 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = {
|
|||
*/
|
||||
{ .name = "C15_IMPDEF", .cp = 15, .crn = 15,
|
||||
.crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE,
|
||||
.access = PL1_RW,
|
||||
.type = ARM_CP_CONST | ARM_CP_NO_MIGRATE | ARM_CP_OVERRIDE,
|
||||
.resetvalue = 0 },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
@ -1744,6 +1787,15 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
|||
define_one_arm_cp_reg(cpu, &auxcr);
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_CBAR)) {
|
||||
ARMCPRegInfo cbar = {
|
||||
.name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, .opc2 = 0,
|
||||
.access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address)
|
||||
};
|
||||
define_one_arm_cp_reg(cpu, &cbar);
|
||||
}
|
||||
|
||||
/* Generic registers whose values depend on the implementation */
|
||||
{
|
||||
ARMCPRegInfo sctlr = {
|
||||
|
@ -1785,7 +1837,11 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
|
|||
CPUState *cs = CPU(cpu);
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_NEON)) {
|
||||
if (arm_feature(env, ARM_FEATURE_AARCH64)) {
|
||||
gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg,
|
||||
aarch64_fpu_gdb_set_reg,
|
||||
34, "aarch64-fpu.xml", 0);
|
||||
} else if (arm_feature(env, ARM_FEATURE_NEON)) {
|
||||
gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg,
|
||||
51, "arm-neon.xml", 0);
|
||||
} else if (arm_feature(env, ARM_FEATURE_VFP3)) {
|
||||
|
|
|
@ -463,4 +463,11 @@ DEF_HELPER_3(neon_qzip8, void, env, i32, i32)
|
|||
DEF_HELPER_3(neon_qzip16, void, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
|
||||
|
||||
DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32)
|
||||
DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32)
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
#include "helper-a64.h"
|
||||
#endif
|
||||
|
||||
#include "exec/def-helper.h"
|
||||
|
|
495
target-arm/kvm.c
495
target-arm/kvm.c
|
@ -100,120 +100,6 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void set_feature(uint64_t *features, int feature)
|
||||
{
|
||||
*features |= 1ULL << feature;
|
||||
}
|
||||
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
{
|
||||
/* Identify the feature bits corresponding to the host CPU, and
|
||||
* fill out the ARMHostCPUClass fields accordingly. To do this
|
||||
* we have to create a scratch VM, create a single CPU inside it,
|
||||
* and then query that CPU for the relevant ID registers.
|
||||
*/
|
||||
int i, ret, fdarray[3];
|
||||
uint32_t midr, id_pfr0, id_isar0, mvfr1;
|
||||
uint64_t features = 0;
|
||||
/* Old kernels may not know about the PREFERRED_TARGET ioctl: however
|
||||
* we know these will only support creating one kind of guest CPU,
|
||||
* which is its preferred CPU type.
|
||||
*/
|
||||
static const uint32_t cpus_to_try[] = {
|
||||
QEMU_KVM_ARM_TARGET_CORTEX_A15,
|
||||
QEMU_KVM_ARM_TARGET_NONE
|
||||
};
|
||||
struct kvm_vcpu_init init;
|
||||
struct kvm_one_reg idregs[] = {
|
||||
{
|
||||
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
|
||||
| ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
|
||||
.addr = (uintptr_t)&midr,
|
||||
},
|
||||
{
|
||||
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
|
||||
| ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
|
||||
.addr = (uintptr_t)&id_pfr0,
|
||||
},
|
||||
{
|
||||
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
|
||||
| ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
|
||||
.addr = (uintptr_t)&id_isar0,
|
||||
},
|
||||
{
|
||||
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
|
||||
| KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
|
||||
.addr = (uintptr_t)&mvfr1,
|
||||
},
|
||||
};
|
||||
|
||||
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ahcc->target = init.target;
|
||||
|
||||
/* This is not strictly blessed by the device tree binding docs yet,
|
||||
* but in practice the kernel does not care about this string so
|
||||
* there is no point maintaining an KVM_ARM_TARGET_* -> string table.
|
||||
*/
|
||||
ahcc->dtb_compatible = "arm,arm-v7";
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(idregs); i++) {
|
||||
ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kvm_arm_destroy_scratch_host_vcpu(fdarray);
|
||||
|
||||
if (ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now we've retrieved all the register information we can
|
||||
* set the feature bits based on the ID register fields.
|
||||
* We can assume any KVM supporting CPU is at least a v7
|
||||
* with VFPv3, LPAE and the generic timers; this in turn implies
|
||||
* most of the other feature bits, but a few must be tested.
|
||||
*/
|
||||
set_feature(&features, ARM_FEATURE_V7);
|
||||
set_feature(&features, ARM_FEATURE_VFP3);
|
||||
set_feature(&features, ARM_FEATURE_LPAE);
|
||||
set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
|
||||
|
||||
switch (extract32(id_isar0, 24, 4)) {
|
||||
case 1:
|
||||
set_feature(&features, ARM_FEATURE_THUMB_DIV);
|
||||
break;
|
||||
case 2:
|
||||
set_feature(&features, ARM_FEATURE_ARM_DIV);
|
||||
set_feature(&features, ARM_FEATURE_THUMB_DIV);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (extract32(id_pfr0, 12, 4) == 1) {
|
||||
set_feature(&features, ARM_FEATURE_THUMB2EE);
|
||||
}
|
||||
if (extract32(mvfr1, 20, 4) == 1) {
|
||||
set_feature(&features, ARM_FEATURE_VFP_FP16);
|
||||
}
|
||||
if (extract32(mvfr1, 12, 4) == 1) {
|
||||
set_feature(&features, ARM_FEATURE_NEON);
|
||||
}
|
||||
if (extract32(mvfr1, 28, 4) == 1) {
|
||||
/* FMAC support implies VFPv4 */
|
||||
set_feature(&features, ARM_FEATURE_VFP4);
|
||||
}
|
||||
|
||||
ahcc->features = features;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc);
|
||||
|
@ -242,7 +128,11 @@ static void kvm_arm_host_cpu_initfn(Object *obj)
|
|||
|
||||
static const TypeInfo host_arm_cpu_type_info = {
|
||||
.name = TYPE_ARM_HOST_CPU,
|
||||
#ifdef TARGET_AARCH64
|
||||
.parent = TYPE_AARCH64_CPU,
|
||||
#else
|
||||
.parent = TYPE_ARM_CPU,
|
||||
#endif
|
||||
.instance_init = kvm_arm_host_cpu_initfn,
|
||||
.class_init = kvm_arm_host_cpu_class_init,
|
||||
.class_size = sizeof(ARMHostCPUClass),
|
||||
|
@ -265,144 +155,6 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
|
|||
return cpu->cpu_index;
|
||||
}
|
||||
|
||||
static bool reg_syncs_via_tuple_list(uint64_t regidx)
|
||||
{
|
||||
/* Return true if the regidx is a register we should synchronize
|
||||
* via the cpreg_tuples array (ie is not a core reg we sync by
|
||||
* hand in kvm_arch_get/put_registers())
|
||||
*/
|
||||
switch (regidx & KVM_REG_ARM_COPROC_MASK) {
|
||||
case KVM_REG_ARM_CORE:
|
||||
case KVM_REG_ARM_VFP:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static int compare_u64(const void *a, const void *b)
|
||||
{
|
||||
if (*(uint64_t *)a > *(uint64_t *)b) {
|
||||
return 1;
|
||||
}
|
||||
if (*(uint64_t *)a < *(uint64_t *)b) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_init_vcpu(CPUState *cs)
|
||||
{
|
||||
struct kvm_vcpu_init init;
|
||||
int i, ret, arraylen;
|
||||
uint64_t v;
|
||||
struct kvm_one_reg r;
|
||||
struct kvm_reg_list rl;
|
||||
struct kvm_reg_list *rlp;
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
|
||||
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
|
||||
fprintf(stderr, "KVM is not supported for this guest CPU type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
init.target = cpu->kvm_target;
|
||||
memset(init.features, 0, sizeof(init.features));
|
||||
if (cpu->start_powered_off) {
|
||||
init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
|
||||
}
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
/* Query the kernel to make sure it supports 32 VFP
|
||||
* registers: QEMU's "cortex-a15" CPU is always a
|
||||
* VFP-D32 core. The simplest way to do this is just
|
||||
* to attempt to read register d31.
|
||||
*/
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
|
||||
r.addr = (uintptr_t)(&v);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret == -ENOENT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Populate the cpreg list based on the kernel's idea
|
||||
* of what registers exist (and throw away the TCG-created list).
|
||||
*/
|
||||
rl.n = 0;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
|
||||
if (ret != -E2BIG) {
|
||||
return ret;
|
||||
}
|
||||
rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
|
||||
rlp->n = rl.n;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
/* Sort the list we get back from the kernel, since cpreg_tuples
|
||||
* must be in strictly ascending order.
|
||||
*/
|
||||
qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
|
||||
|
||||
for (i = 0, arraylen = 0; i < rlp->n; i++) {
|
||||
if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
|
||||
continue;
|
||||
}
|
||||
switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
|
||||
case KVM_REG_SIZE_U32:
|
||||
case KVM_REG_SIZE_U64:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Can't handle size of register in kernel list\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
arraylen++;
|
||||
}
|
||||
|
||||
cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
|
||||
cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
|
||||
cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
|
||||
arraylen);
|
||||
cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
|
||||
arraylen);
|
||||
cpu->cpreg_array_len = arraylen;
|
||||
cpu->cpreg_vmstate_array_len = arraylen;
|
||||
|
||||
for (i = 0, arraylen = 0; i < rlp->n; i++) {
|
||||
uint64_t regidx = rlp->reg[i];
|
||||
if (!reg_syncs_via_tuple_list(regidx)) {
|
||||
continue;
|
||||
}
|
||||
cpu->cpreg_indexes[arraylen] = regidx;
|
||||
arraylen++;
|
||||
}
|
||||
assert(cpu->cpreg_array_len == arraylen);
|
||||
|
||||
if (!write_kvmstate_to_list(cpu)) {
|
||||
/* Shouldn't happen unless kernel is inconsistent about
|
||||
* what registers exist.
|
||||
*/
|
||||
fprintf(stderr, "Initial read of kernel register state failed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Save a copy of the initial register values so that we can
|
||||
* feed it back to the kernel on VCPU reset.
|
||||
*/
|
||||
cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
|
||||
cpu->cpreg_array_len *
|
||||
sizeof(cpu->cpreg_values[0]));
|
||||
|
||||
out:
|
||||
g_free(rlp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We track all the KVM devices which need their memory addresses
|
||||
* passing to the kernel in a list of these structures.
|
||||
* When board init is complete we run through the list and
|
||||
|
@ -563,232 +315,6 @@ bool write_list_to_kvmstate(ARMCPU *cpu)
|
|||
return ok;
|
||||
}
|
||||
|
||||
typedef struct Reg {
|
||||
uint64_t id;
|
||||
int offset;
|
||||
} Reg;
|
||||
|
||||
#define COREREG(KERNELNAME, QEMUFIELD) \
|
||||
{ \
|
||||
KVM_REG_ARM | KVM_REG_SIZE_U32 | \
|
||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
|
||||
offsetof(CPUARMState, QEMUFIELD) \
|
||||
}
|
||||
|
||||
#define VFPSYSREG(R) \
|
||||
{ \
|
||||
KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
|
||||
KVM_REG_ARM_VFP_##R, \
|
||||
offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
|
||||
}
|
||||
|
||||
static const Reg regs[] = {
|
||||
/* R0_usr .. R14_usr */
|
||||
COREREG(usr_regs.uregs[0], regs[0]),
|
||||
COREREG(usr_regs.uregs[1], regs[1]),
|
||||
COREREG(usr_regs.uregs[2], regs[2]),
|
||||
COREREG(usr_regs.uregs[3], regs[3]),
|
||||
COREREG(usr_regs.uregs[4], regs[4]),
|
||||
COREREG(usr_regs.uregs[5], regs[5]),
|
||||
COREREG(usr_regs.uregs[6], regs[6]),
|
||||
COREREG(usr_regs.uregs[7], regs[7]),
|
||||
COREREG(usr_regs.uregs[8], usr_regs[0]),
|
||||
COREREG(usr_regs.uregs[9], usr_regs[1]),
|
||||
COREREG(usr_regs.uregs[10], usr_regs[2]),
|
||||
COREREG(usr_regs.uregs[11], usr_regs[3]),
|
||||
COREREG(usr_regs.uregs[12], usr_regs[4]),
|
||||
COREREG(usr_regs.uregs[13], banked_r13[0]),
|
||||
COREREG(usr_regs.uregs[14], banked_r14[0]),
|
||||
/* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
|
||||
COREREG(svc_regs[0], banked_r13[1]),
|
||||
COREREG(svc_regs[1], banked_r14[1]),
|
||||
COREREG(svc_regs[2], banked_spsr[1]),
|
||||
COREREG(abt_regs[0], banked_r13[2]),
|
||||
COREREG(abt_regs[1], banked_r14[2]),
|
||||
COREREG(abt_regs[2], banked_spsr[2]),
|
||||
COREREG(und_regs[0], banked_r13[3]),
|
||||
COREREG(und_regs[1], banked_r14[3]),
|
||||
COREREG(und_regs[2], banked_spsr[3]),
|
||||
COREREG(irq_regs[0], banked_r13[4]),
|
||||
COREREG(irq_regs[1], banked_r14[4]),
|
||||
COREREG(irq_regs[2], banked_spsr[4]),
|
||||
/* R8_fiq .. R14_fiq and SPSR_fiq */
|
||||
COREREG(fiq_regs[0], fiq_regs[0]),
|
||||
COREREG(fiq_regs[1], fiq_regs[1]),
|
||||
COREREG(fiq_regs[2], fiq_regs[2]),
|
||||
COREREG(fiq_regs[3], fiq_regs[3]),
|
||||
COREREG(fiq_regs[4], fiq_regs[4]),
|
||||
COREREG(fiq_regs[5], banked_r13[5]),
|
||||
COREREG(fiq_regs[6], banked_r14[5]),
|
||||
COREREG(fiq_regs[7], banked_spsr[5]),
|
||||
/* R15 */
|
||||
COREREG(usr_regs.uregs[15], regs[15]),
|
||||
/* VFP system registers */
|
||||
VFPSYSREG(FPSID),
|
||||
VFPSYSREG(MVFR1),
|
||||
VFPSYSREG(MVFR0),
|
||||
VFPSYSREG(FPEXC),
|
||||
VFPSYSREG(FPINST),
|
||||
VFPSYSREG(FPINST2),
|
||||
};
|
||||
|
||||
int kvm_arch_put_registers(CPUState *cs, int level)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
struct kvm_one_reg r;
|
||||
int mode, bn;
|
||||
int ret, i;
|
||||
uint32_t cpsr, fpscr;
|
||||
|
||||
/* Make sure the banked regs are properly set */
|
||||
mode = env->uncached_cpsr & CPSR_M;
|
||||
bn = bank_number(mode);
|
||||
if (mode == ARM_CPU_MODE_FIQ) {
|
||||
memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
|
||||
} else {
|
||||
memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
|
||||
}
|
||||
env->banked_r13[bn] = env->regs[13];
|
||||
env->banked_r14[bn] = env->regs[14];
|
||||
env->banked_spsr[bn] = env->spsr;
|
||||
|
||||
/* Now we can safely copy stuff down to the kernel */
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
r.id = regs[i].id;
|
||||
r.addr = (uintptr_t)(env) + regs[i].offset;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Special cases which aren't a single CPUARMState field */
|
||||
cpsr = cpsr_read(env);
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
|
||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
|
||||
r.addr = (uintptr_t)(&cpsr);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* VFP registers */
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
|
||||
for (i = 0; i < 32; i++) {
|
||||
r.addr = (uintptr_t)(&env->vfp.regs[i]);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
r.id++;
|
||||
}
|
||||
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
|
||||
KVM_REG_ARM_VFP_FPSCR;
|
||||
fpscr = vfp_get_fpscr(env);
|
||||
r.addr = (uintptr_t)&fpscr;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Note that we do not call write_cpustate_to_list()
|
||||
* here, so we are only writing the tuple list back to
|
||||
* KVM. This is safe because nothing can change the
|
||||
* CPUARMState cp15 fields (in particular gdb accesses cannot)
|
||||
* and so there are no changes to sync. In fact syncing would
|
||||
* be wrong at this point: for a constant register where TCG and
|
||||
* KVM disagree about its value, the preceding write_list_to_cpustate()
|
||||
* would not have had any effect on the CPUARMState value (since the
|
||||
* register is read-only), and a write_cpustate_to_list() here would
|
||||
* then try to write the TCG value back into KVM -- this would either
|
||||
* fail or incorrectly change the value the guest sees.
|
||||
*
|
||||
* If we ever want to allow the user to modify cp15 registers via
|
||||
* the gdb stub, we would need to be more clever here (for instance
|
||||
* tracking the set of registers kvm_arch_get_registers() successfully
|
||||
* managed to update the CPUARMState with, and only allowing those
|
||||
* to be written back up into the kernel).
|
||||
*/
|
||||
if (!write_list_to_kvmstate(cpu)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_arch_get_registers(CPUState *cs)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
struct kvm_one_reg r;
|
||||
int mode, bn;
|
||||
int ret, i;
|
||||
uint32_t cpsr, fpscr;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
r.id = regs[i].id;
|
||||
r.addr = (uintptr_t)(env) + regs[i].offset;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Special cases which aren't a single CPUARMState field */
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
|
||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
|
||||
r.addr = (uintptr_t)(&cpsr);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
cpsr_write(env, cpsr, 0xffffffff);
|
||||
|
||||
/* Make sure the current mode regs are properly set */
|
||||
mode = env->uncached_cpsr & CPSR_M;
|
||||
bn = bank_number(mode);
|
||||
if (mode == ARM_CPU_MODE_FIQ) {
|
||||
memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
|
||||
} else {
|
||||
memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
|
||||
}
|
||||
env->regs[13] = env->banked_r13[bn];
|
||||
env->regs[14] = env->banked_r14[bn];
|
||||
env->spsr = env->banked_spsr[bn];
|
||||
|
||||
/* VFP registers */
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
|
||||
for (i = 0; i < 32; i++) {
|
||||
r.addr = (uintptr_t)(&env->vfp.regs[i]);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
r.id++;
|
||||
}
|
||||
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
|
||||
KVM_REG_ARM_VFP_FPSCR;
|
||||
r.addr = (uintptr_t)&fpscr;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
vfp_set_fpscr(env, fpscr);
|
||||
|
||||
if (!write_kvmstate_to_list(cpu)) {
|
||||
return EINVAL;
|
||||
}
|
||||
/* Note that it's OK to have registers which aren't in CPUState,
|
||||
* so we can ignore a failure return here.
|
||||
*/
|
||||
write_list_to_cpustate(cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
|
||||
{
|
||||
}
|
||||
|
@ -802,19 +328,6 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_reset_vcpu(CPUState *cs)
|
||||
{
|
||||
/* Feed the kernel back its initial register state */
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
|
||||
memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
|
||||
cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
|
||||
|
||||
if (!write_list_to_kvmstate(cpu)) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool kvm_arch_stop_on_emulation_error(CPUState *cs)
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,515 @@
|
|||
/*
|
||||
* ARM implementation of KVM hooks, 32 bit specific code.
|
||||
*
|
||||
* Copyright Christoffer Dall 2009-2010
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <linux/kvm.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_arm.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/arm/arm.h"
|
||||
|
||||
static inline void set_feature(uint64_t *features, int feature)
|
||||
{
|
||||
*features |= 1ULL << feature;
|
||||
}
|
||||
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
{
|
||||
/* Identify the feature bits corresponding to the host CPU, and
|
||||
* fill out the ARMHostCPUClass fields accordingly. To do this
|
||||
* we have to create a scratch VM, create a single CPU inside it,
|
||||
* and then query that CPU for the relevant ID registers.
|
||||
*/
|
||||
int i, ret, fdarray[3];
|
||||
uint32_t midr, id_pfr0, id_isar0, mvfr1;
|
||||
uint64_t features = 0;
|
||||
/* Old kernels may not know about the PREFERRED_TARGET ioctl: however
|
||||
* we know these will only support creating one kind of guest CPU,
|
||||
* which is its preferred CPU type.
|
||||
*/
|
||||
static const uint32_t cpus_to_try[] = {
|
||||
QEMU_KVM_ARM_TARGET_CORTEX_A15,
|
||||
QEMU_KVM_ARM_TARGET_NONE
|
||||
};
|
||||
struct kvm_vcpu_init init;
|
||||
struct kvm_one_reg idregs[] = {
|
||||
{
|
||||
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
|
||||
| ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
|
||||
.addr = (uintptr_t)&midr,
|
||||
},
|
||||
{
|
||||
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
|
||||
| ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
|
||||
.addr = (uintptr_t)&id_pfr0,
|
||||
},
|
||||
{
|
||||
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
|
||||
| ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
|
||||
.addr = (uintptr_t)&id_isar0,
|
||||
},
|
||||
{
|
||||
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
|
||||
| KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
|
||||
.addr = (uintptr_t)&mvfr1,
|
||||
},
|
||||
};
|
||||
|
||||
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ahcc->target = init.target;
|
||||
|
||||
/* This is not strictly blessed by the device tree binding docs yet,
|
||||
* but in practice the kernel does not care about this string so
|
||||
* there is no point maintaining an KVM_ARM_TARGET_* -> string table.
|
||||
*/
|
||||
ahcc->dtb_compatible = "arm,arm-v7";
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(idregs); i++) {
|
||||
ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kvm_arm_destroy_scratch_host_vcpu(fdarray);
|
||||
|
||||
if (ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now we've retrieved all the register information we can
|
||||
* set the feature bits based on the ID register fields.
|
||||
* We can assume any KVM supporting CPU is at least a v7
|
||||
* with VFPv3, LPAE and the generic timers; this in turn implies
|
||||
* most of the other feature bits, but a few must be tested.
|
||||
*/
|
||||
set_feature(&features, ARM_FEATURE_V7);
|
||||
set_feature(&features, ARM_FEATURE_VFP3);
|
||||
set_feature(&features, ARM_FEATURE_LPAE);
|
||||
set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
|
||||
|
||||
switch (extract32(id_isar0, 24, 4)) {
|
||||
case 1:
|
||||
set_feature(&features, ARM_FEATURE_THUMB_DIV);
|
||||
break;
|
||||
case 2:
|
||||
set_feature(&features, ARM_FEATURE_ARM_DIV);
|
||||
set_feature(&features, ARM_FEATURE_THUMB_DIV);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (extract32(id_pfr0, 12, 4) == 1) {
|
||||
set_feature(&features, ARM_FEATURE_THUMB2EE);
|
||||
}
|
||||
if (extract32(mvfr1, 20, 4) == 1) {
|
||||
set_feature(&features, ARM_FEATURE_VFP_FP16);
|
||||
}
|
||||
if (extract32(mvfr1, 12, 4) == 1) {
|
||||
set_feature(&features, ARM_FEATURE_NEON);
|
||||
}
|
||||
if (extract32(mvfr1, 28, 4) == 1) {
|
||||
/* FMAC support implies VFPv4 */
|
||||
set_feature(&features, ARM_FEATURE_VFP4);
|
||||
}
|
||||
|
||||
ahcc->features = features;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool reg_syncs_via_tuple_list(uint64_t regidx)
|
||||
{
|
||||
/* Return true if the regidx is a register we should synchronize
|
||||
* via the cpreg_tuples array (ie is not a core reg we sync by
|
||||
* hand in kvm_arch_get/put_registers())
|
||||
*/
|
||||
switch (regidx & KVM_REG_ARM_COPROC_MASK) {
|
||||
case KVM_REG_ARM_CORE:
|
||||
case KVM_REG_ARM_VFP:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static int compare_u64(const void *a, const void *b)
|
||||
{
|
||||
if (*(uint64_t *)a > *(uint64_t *)b) {
|
||||
return 1;
|
||||
}
|
||||
if (*(uint64_t *)a < *(uint64_t *)b) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_init_vcpu(CPUState *cs)
|
||||
{
|
||||
struct kvm_vcpu_init init;
|
||||
int i, ret, arraylen;
|
||||
uint64_t v;
|
||||
struct kvm_one_reg r;
|
||||
struct kvm_reg_list rl;
|
||||
struct kvm_reg_list *rlp;
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
|
||||
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
|
||||
fprintf(stderr, "KVM is not supported for this guest CPU type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
init.target = cpu->kvm_target;
|
||||
memset(init.features, 0, sizeof(init.features));
|
||||
if (cpu->start_powered_off) {
|
||||
init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
|
||||
}
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
/* Query the kernel to make sure it supports 32 VFP
|
||||
* registers: QEMU's "cortex-a15" CPU is always a
|
||||
* VFP-D32 core. The simplest way to do this is just
|
||||
* to attempt to read register d31.
|
||||
*/
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
|
||||
r.addr = (uintptr_t)(&v);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret == -ENOENT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Populate the cpreg list based on the kernel's idea
|
||||
* of what registers exist (and throw away the TCG-created list).
|
||||
*/
|
||||
rl.n = 0;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
|
||||
if (ret != -E2BIG) {
|
||||
return ret;
|
||||
}
|
||||
rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
|
||||
rlp->n = rl.n;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
/* Sort the list we get back from the kernel, since cpreg_tuples
|
||||
* must be in strictly ascending order.
|
||||
*/
|
||||
qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
|
||||
|
||||
for (i = 0, arraylen = 0; i < rlp->n; i++) {
|
||||
if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
|
||||
continue;
|
||||
}
|
||||
switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
|
||||
case KVM_REG_SIZE_U32:
|
||||
case KVM_REG_SIZE_U64:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Can't handle size of register in kernel list\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
arraylen++;
|
||||
}
|
||||
|
||||
cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
|
||||
cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
|
||||
cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
|
||||
arraylen);
|
||||
cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
|
||||
arraylen);
|
||||
cpu->cpreg_array_len = arraylen;
|
||||
cpu->cpreg_vmstate_array_len = arraylen;
|
||||
|
||||
for (i = 0, arraylen = 0; i < rlp->n; i++) {
|
||||
uint64_t regidx = rlp->reg[i];
|
||||
if (!reg_syncs_via_tuple_list(regidx)) {
|
||||
continue;
|
||||
}
|
||||
cpu->cpreg_indexes[arraylen] = regidx;
|
||||
arraylen++;
|
||||
}
|
||||
assert(cpu->cpreg_array_len == arraylen);
|
||||
|
||||
if (!write_kvmstate_to_list(cpu)) {
|
||||
/* Shouldn't happen unless kernel is inconsistent about
|
||||
* what registers exist.
|
||||
*/
|
||||
fprintf(stderr, "Initial read of kernel register state failed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Save a copy of the initial register values so that we can
|
||||
* feed it back to the kernel on VCPU reset.
|
||||
*/
|
||||
cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
|
||||
cpu->cpreg_array_len *
|
||||
sizeof(cpu->cpreg_values[0]));
|
||||
|
||||
out:
|
||||
g_free(rlp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct Reg {
|
||||
uint64_t id;
|
||||
int offset;
|
||||
} Reg;
|
||||
|
||||
#define COREREG(KERNELNAME, QEMUFIELD) \
|
||||
{ \
|
||||
KVM_REG_ARM | KVM_REG_SIZE_U32 | \
|
||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
|
||||
offsetof(CPUARMState, QEMUFIELD) \
|
||||
}
|
||||
|
||||
#define VFPSYSREG(R) \
|
||||
{ \
|
||||
KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
|
||||
KVM_REG_ARM_VFP_##R, \
|
||||
offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
|
||||
}
|
||||
|
||||
static const Reg regs[] = {
|
||||
/* R0_usr .. R14_usr */
|
||||
COREREG(usr_regs.uregs[0], regs[0]),
|
||||
COREREG(usr_regs.uregs[1], regs[1]),
|
||||
COREREG(usr_regs.uregs[2], regs[2]),
|
||||
COREREG(usr_regs.uregs[3], regs[3]),
|
||||
COREREG(usr_regs.uregs[4], regs[4]),
|
||||
COREREG(usr_regs.uregs[5], regs[5]),
|
||||
COREREG(usr_regs.uregs[6], regs[6]),
|
||||
COREREG(usr_regs.uregs[7], regs[7]),
|
||||
COREREG(usr_regs.uregs[8], usr_regs[0]),
|
||||
COREREG(usr_regs.uregs[9], usr_regs[1]),
|
||||
COREREG(usr_regs.uregs[10], usr_regs[2]),
|
||||
COREREG(usr_regs.uregs[11], usr_regs[3]),
|
||||
COREREG(usr_regs.uregs[12], usr_regs[4]),
|
||||
COREREG(usr_regs.uregs[13], banked_r13[0]),
|
||||
COREREG(usr_regs.uregs[14], banked_r14[0]),
|
||||
/* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
|
||||
COREREG(svc_regs[0], banked_r13[1]),
|
||||
COREREG(svc_regs[1], banked_r14[1]),
|
||||
COREREG(svc_regs[2], banked_spsr[1]),
|
||||
COREREG(abt_regs[0], banked_r13[2]),
|
||||
COREREG(abt_regs[1], banked_r14[2]),
|
||||
COREREG(abt_regs[2], banked_spsr[2]),
|
||||
COREREG(und_regs[0], banked_r13[3]),
|
||||
COREREG(und_regs[1], banked_r14[3]),
|
||||
COREREG(und_regs[2], banked_spsr[3]),
|
||||
COREREG(irq_regs[0], banked_r13[4]),
|
||||
COREREG(irq_regs[1], banked_r14[4]),
|
||||
COREREG(irq_regs[2], banked_spsr[4]),
|
||||
/* R8_fiq .. R14_fiq and SPSR_fiq */
|
||||
COREREG(fiq_regs[0], fiq_regs[0]),
|
||||
COREREG(fiq_regs[1], fiq_regs[1]),
|
||||
COREREG(fiq_regs[2], fiq_regs[2]),
|
||||
COREREG(fiq_regs[3], fiq_regs[3]),
|
||||
COREREG(fiq_regs[4], fiq_regs[4]),
|
||||
COREREG(fiq_regs[5], banked_r13[5]),
|
||||
COREREG(fiq_regs[6], banked_r14[5]),
|
||||
COREREG(fiq_regs[7], banked_spsr[5]),
|
||||
/* R15 */
|
||||
COREREG(usr_regs.uregs[15], regs[15]),
|
||||
/* VFP system registers */
|
||||
VFPSYSREG(FPSID),
|
||||
VFPSYSREG(MVFR1),
|
||||
VFPSYSREG(MVFR0),
|
||||
VFPSYSREG(FPEXC),
|
||||
VFPSYSREG(FPINST),
|
||||
VFPSYSREG(FPINST2),
|
||||
};
|
||||
|
||||
int kvm_arch_put_registers(CPUState *cs, int level)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
struct kvm_one_reg r;
|
||||
int mode, bn;
|
||||
int ret, i;
|
||||
uint32_t cpsr, fpscr;
|
||||
|
||||
/* Make sure the banked regs are properly set */
|
||||
mode = env->uncached_cpsr & CPSR_M;
|
||||
bn = bank_number(mode);
|
||||
if (mode == ARM_CPU_MODE_FIQ) {
|
||||
memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
|
||||
} else {
|
||||
memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
|
||||
}
|
||||
env->banked_r13[bn] = env->regs[13];
|
||||
env->banked_r14[bn] = env->regs[14];
|
||||
env->banked_spsr[bn] = env->spsr;
|
||||
|
||||
/* Now we can safely copy stuff down to the kernel */
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
r.id = regs[i].id;
|
||||
r.addr = (uintptr_t)(env) + regs[i].offset;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Special cases which aren't a single CPUARMState field */
|
||||
cpsr = cpsr_read(env);
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
|
||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
|
||||
r.addr = (uintptr_t)(&cpsr);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* VFP registers */
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
|
||||
for (i = 0; i < 32; i++) {
|
||||
r.addr = (uintptr_t)(&env->vfp.regs[i]);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
r.id++;
|
||||
}
|
||||
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
|
||||
KVM_REG_ARM_VFP_FPSCR;
|
||||
fpscr = vfp_get_fpscr(env);
|
||||
r.addr = (uintptr_t)&fpscr;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Note that we do not call write_cpustate_to_list()
|
||||
* here, so we are only writing the tuple list back to
|
||||
* KVM. This is safe because nothing can change the
|
||||
* CPUARMState cp15 fields (in particular gdb accesses cannot)
|
||||
* and so there are no changes to sync. In fact syncing would
|
||||
* be wrong at this point: for a constant register where TCG and
|
||||
* KVM disagree about its value, the preceding write_list_to_cpustate()
|
||||
* would not have had any effect on the CPUARMState value (since the
|
||||
* register is read-only), and a write_cpustate_to_list() here would
|
||||
* then try to write the TCG value back into KVM -- this would either
|
||||
* fail or incorrectly change the value the guest sees.
|
||||
*
|
||||
* If we ever want to allow the user to modify cp15 registers via
|
||||
* the gdb stub, we would need to be more clever here (for instance
|
||||
* tracking the set of registers kvm_arch_get_registers() successfully
|
||||
* managed to update the CPUARMState with, and only allowing those
|
||||
* to be written back up into the kernel).
|
||||
*/
|
||||
if (!write_list_to_kvmstate(cpu)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_arch_get_registers(CPUState *cs)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
struct kvm_one_reg r;
|
||||
int mode, bn;
|
||||
int ret, i;
|
||||
uint32_t cpsr, fpscr;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
r.id = regs[i].id;
|
||||
r.addr = (uintptr_t)(env) + regs[i].offset;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Special cases which aren't a single CPUARMState field */
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
|
||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
|
||||
r.addr = (uintptr_t)(&cpsr);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
cpsr_write(env, cpsr, 0xffffffff);
|
||||
|
||||
/* Make sure the current mode regs are properly set */
|
||||
mode = env->uncached_cpsr & CPSR_M;
|
||||
bn = bank_number(mode);
|
||||
if (mode == ARM_CPU_MODE_FIQ) {
|
||||
memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
|
||||
} else {
|
||||
memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
|
||||
}
|
||||
env->regs[13] = env->banked_r13[bn];
|
||||
env->regs[14] = env->banked_r14[bn];
|
||||
env->spsr = env->banked_spsr[bn];
|
||||
|
||||
/* VFP registers */
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
|
||||
for (i = 0; i < 32; i++) {
|
||||
r.addr = (uintptr_t)(&env->vfp.regs[i]);
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
r.id++;
|
||||
}
|
||||
|
||||
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
|
||||
KVM_REG_ARM_VFP_FPSCR;
|
||||
r.addr = (uintptr_t)&fpscr;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
vfp_set_fpscr(env, fpscr);
|
||||
|
||||
if (!write_kvmstate_to_list(cpu)) {
|
||||
return EINVAL;
|
||||
}
|
||||
/* Note that it's OK to have registers which aren't in CPUState,
|
||||
* so we can ignore a failure return here.
|
||||
*/
|
||||
write_list_to_cpustate(cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_reset_vcpu(CPUState *cs)
|
||||
{
|
||||
/* Feed the kernel back its initial register state */
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
|
||||
memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
|
||||
cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
|
||||
|
||||
if (!write_list_to_kvmstate(cpu)) {
|
||||
abort();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* ARM implementation of KVM hooks, 64 bit specific code
|
||||
*
|
||||
* Copyright Mian-M. Hamayun 2013, Virtual Open Systems
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <linux/kvm.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_arm.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/arm/arm.h"
|
||||
|
||||
static inline void set_feature(uint64_t *features, int feature)
|
||||
{
|
||||
*features |= 1ULL << feature;
|
||||
}
|
||||
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
|
||||
{
|
||||
/* Identify the feature bits corresponding to the host CPU, and
|
||||
* fill out the ARMHostCPUClass fields accordingly. To do this
|
||||
* we have to create a scratch VM, create a single CPU inside it,
|
||||
* and then query that CPU for the relevant ID registers.
|
||||
* For AArch64 we currently don't care about ID registers at
|
||||
* all; we just want to know the CPU type.
|
||||
*/
|
||||
int fdarray[3];
|
||||
uint64_t features = 0;
|
||||
/* Old kernels may not know about the PREFERRED_TARGET ioctl: however
|
||||
* we know these will only support creating one kind of guest CPU,
|
||||
* which is its preferred CPU type. Fortunately these old kernels
|
||||
* support only a very limited number of CPUs.
|
||||
*/
|
||||
static const uint32_t cpus_to_try[] = {
|
||||
KVM_ARM_TARGET_AEM_V8,
|
||||
KVM_ARM_TARGET_FOUNDATION_V8,
|
||||
KVM_ARM_TARGET_CORTEX_A57,
|
||||
QEMU_KVM_ARM_TARGET_NONE
|
||||
};
|
||||
struct kvm_vcpu_init init;
|
||||
|
||||
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ahcc->target = init.target;
|
||||
ahcc->dtb_compatible = "arm,arm-v8";
|
||||
|
||||
kvm_arm_destroy_scratch_host_vcpu(fdarray);
|
||||
|
||||
/* We can assume any KVM supporting CPU is at least a v8
|
||||
* with VFPv4+Neon; this in turn implies most of the other
|
||||
* feature bits.
|
||||
*/
|
||||
set_feature(&features, ARM_FEATURE_V8);
|
||||
set_feature(&features, ARM_FEATURE_VFP4);
|
||||
set_feature(&features, ARM_FEATURE_NEON);
|
||||
set_feature(&features, ARM_FEATURE_AARCH64);
|
||||
|
||||
ahcc->features = features;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int kvm_arch_init_vcpu(CPUState *cs)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
struct kvm_vcpu_init init;
|
||||
int ret;
|
||||
|
||||
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
|
||||
!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||
fprintf(stderr, "KVM is not supported for this guest CPU type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
init.target = cpu->kvm_target;
|
||||
memset(init.features, 0, sizeof(init.features));
|
||||
if (cpu->start_powered_off) {
|
||||
init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
|
||||
}
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
|
||||
|
||||
/* TODO : support for save/restore/reset of system regs via tuple list */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
|
||||
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x))
|
||||
|
||||
int kvm_arch_put_registers(CPUState *cs, int level)
|
||||
{
|
||||
struct kvm_one_reg reg;
|
||||
uint64_t val;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
for (i = 0; i < 31; i++) {
|
||||
reg.id = AARCH64_CORE_REG(regs.regs[i]);
|
||||
reg.addr = (uintptr_t) &env->xregs[i];
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
reg.id = AARCH64_CORE_REG(regs.sp);
|
||||
reg.addr = (uintptr_t) &env->xregs[31];
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Note that KVM thinks pstate is 64 bit but we use a uint32_t */
|
||||
val = pstate_read(env);
|
||||
reg.id = AARCH64_CORE_REG(regs.pstate);
|
||||
reg.addr = (uintptr_t) &val;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg.id = AARCH64_CORE_REG(regs.pc);
|
||||
reg.addr = (uintptr_t) &env->pc;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
* SP_EL1
|
||||
* ELR_EL1
|
||||
* SPSR[]
|
||||
* FP state
|
||||
* system registers
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_arch_get_registers(CPUState *cs)
|
||||
{
|
||||
struct kvm_one_reg reg;
|
||||
uint64_t val;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
for (i = 0; i < 31; i++) {
|
||||
reg.id = AARCH64_CORE_REG(regs.regs[i]);
|
||||
reg.addr = (uintptr_t) &env->xregs[i];
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
reg.id = AARCH64_CORE_REG(regs.sp);
|
||||
reg.addr = (uintptr_t) &env->xregs[31];
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg.id = AARCH64_CORE_REG(regs.pstate);
|
||||
reg.addr = (uintptr_t) &val;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
pstate_write(env, val);
|
||||
|
||||
reg.id = AARCH64_CORE_REG(regs.pc);
|
||||
reg.addr = (uintptr_t) &env->pc;
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: other registers */
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvm_arch_reset_vcpu(CPUState *cs)
|
||||
{
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -56,11 +56,6 @@ static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE];
|
|||
#define IS_USER(s) (s->user)
|
||||
#endif
|
||||
|
||||
/* These instructions trap after executing, so defer them until after the
|
||||
conditional execution state has been updated. */
|
||||
#define DISAS_WFI 4
|
||||
#define DISAS_SWI 5
|
||||
|
||||
TCGv_ptr cpu_env;
|
||||
/* We reuse the same 64-bit temporaries for efficiency. */
|
||||
static TCGv_i64 cpu_V0, cpu_V1, cpu_M0;
|
||||
|
@ -676,7 +671,11 @@ static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b)
|
|||
}
|
||||
#undef PAS_OP
|
||||
|
||||
static void gen_test_cc(int cc, int label)
|
||||
/*
|
||||
* generate a conditional branch based on ARM condition code cc.
|
||||
* This is common between ARM and Aarch64 targets.
|
||||
*/
|
||||
void arm_gen_test_cc(int cc, int label)
|
||||
{
|
||||
TCGv_i32 tmp;
|
||||
int inv;
|
||||
|
@ -900,11 +899,7 @@ DO_GEN_ST(32, MO_TEUL)
|
|||
|
||||
static inline void gen_set_pc_im(DisasContext *s, target_ulong val)
|
||||
{
|
||||
if (s->aarch64) {
|
||||
gen_a64_set_pc_im(val);
|
||||
} else {
|
||||
tcg_gen_movi_i32(cpu_R[15], val);
|
||||
}
|
||||
tcg_gen_movi_i32(cpu_R[15], val);
|
||||
}
|
||||
|
||||
/* Force a TB lookup after an instruction that changes the CPU state. */
|
||||
|
@ -4592,6 +4587,8 @@ static const uint8_t neon_3r_sizes[] = {
|
|||
#define NEON_2RM_VREV16 2
|
||||
#define NEON_2RM_VPADDL 4
|
||||
#define NEON_2RM_VPADDL_U 5
|
||||
#define NEON_2RM_AESE 6 /* Includes AESD */
|
||||
#define NEON_2RM_AESMC 7 /* Includes AESIMC */
|
||||
#define NEON_2RM_VCLS 8
|
||||
#define NEON_2RM_VCLZ 9
|
||||
#define NEON_2RM_VCNT 10
|
||||
|
@ -4649,6 +4646,8 @@ static const uint8_t neon_2rm_sizes[] = {
|
|||
[NEON_2RM_VREV16] = 0x1,
|
||||
[NEON_2RM_VPADDL] = 0x7,
|
||||
[NEON_2RM_VPADDL_U] = 0x7,
|
||||
[NEON_2RM_AESE] = 0x1,
|
||||
[NEON_2RM_AESMC] = 0x1,
|
||||
[NEON_2RM_VCLS] = 0x7,
|
||||
[NEON_2RM_VCLZ] = 0x7,
|
||||
[NEON_2RM_VCNT] = 0x1,
|
||||
|
@ -6184,6 +6183,28 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins
|
|||
tcg_temp_free_i32(tmp2);
|
||||
tcg_temp_free_i32(tmp3);
|
||||
break;
|
||||
case NEON_2RM_AESE: case NEON_2RM_AESMC:
|
||||
if (!arm_feature(env, ARM_FEATURE_V8_AES)
|
||||
|| ((rm | rd) & 1)) {
|
||||
return 1;
|
||||
}
|
||||
tmp = tcg_const_i32(rd);
|
||||
tmp2 = tcg_const_i32(rm);
|
||||
|
||||
/* Bit 6 is the lowest opcode bit; it distinguishes between
|
||||
* encryption (AESE/AESMC) and decryption (AESD/AESIMC)
|
||||
*/
|
||||
tmp3 = tcg_const_i32(extract32(insn, 6, 1));
|
||||
|
||||
if (op == NEON_2RM_AESE) {
|
||||
gen_helper_crypto_aese(cpu_env, tmp, tmp2, tmp3);
|
||||
} else {
|
||||
gen_helper_crypto_aesmc(cpu_env, tmp, tmp2, tmp3);
|
||||
}
|
||||
tcg_temp_free_i32(tmp);
|
||||
tcg_temp_free_i32(tmp2);
|
||||
tcg_temp_free_i32(tmp3);
|
||||
break;
|
||||
default:
|
||||
elementwise:
|
||||
for (pass = 0; pass < (q ? 4 : 2); pass++) {
|
||||
|
@ -7114,7 +7135,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s)
|
|||
/* if not always execute, we generate a conditional jump to
|
||||
next instruction */
|
||||
s->condlabel = gen_new_label();
|
||||
gen_test_cc(cond ^ 1, s->condlabel);
|
||||
arm_gen_test_cc(cond ^ 1, s->condlabel);
|
||||
s->condjmp = 1;
|
||||
}
|
||||
if ((insn & 0x0f900000) == 0x03000000) {
|
||||
|
@ -9131,7 +9152,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
|
|||
op = (insn >> 22) & 0xf;
|
||||
/* Generate a conditional jump to next instruction. */
|
||||
s->condlabel = gen_new_label();
|
||||
gen_test_cc(op ^ 1, s->condlabel);
|
||||
arm_gen_test_cc(op ^ 1, s->condlabel);
|
||||
s->condjmp = 1;
|
||||
|
||||
/* offset[11:1] = insn[10:0] */
|
||||
|
@ -9488,7 +9509,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
|
|||
cond = s->condexec_cond;
|
||||
if (cond != 0x0e) { /* Skip conditional when condition is AL. */
|
||||
s->condlabel = gen_new_label();
|
||||
gen_test_cc(cond ^ 1, s->condlabel);
|
||||
arm_gen_test_cc(cond ^ 1, s->condlabel);
|
||||
s->condjmp = 1;
|
||||
}
|
||||
}
|
||||
|
@ -10161,7 +10182,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
|
|||
}
|
||||
/* generate a conditional jump to next instruction */
|
||||
s->condlabel = gen_new_label();
|
||||
gen_test_cc(cond ^ 1, s->condlabel);
|
||||
arm_gen_test_cc(cond ^ 1, s->condlabel);
|
||||
s->condjmp = 1;
|
||||
|
||||
/* jump to the offset */
|
||||
|
@ -10217,6 +10238,15 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
|||
int max_insns;
|
||||
|
||||
/* generate intermediate code */
|
||||
|
||||
/* The A64 decoder has its own top level loop, because it doesn't need
|
||||
* the A32/T32 complexity to do with conditional execution/IT blocks/etc.
|
||||
*/
|
||||
if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) {
|
||||
gen_intermediate_code_internal_a64(cpu, tb, search_pc);
|
||||
return;
|
||||
}
|
||||
|
||||
pc_start = tb->pc;
|
||||
|
||||
dc->tb = tb;
|
||||
|
@ -10228,31 +10258,18 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
|||
dc->singlestep_enabled = cs->singlestep_enabled;
|
||||
dc->condjmp = 0;
|
||||
|
||||
if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) {
|
||||
dc->aarch64 = 1;
|
||||
dc->thumb = 0;
|
||||
dc->bswap_code = 0;
|
||||
dc->condexec_mask = 0;
|
||||
dc->condexec_cond = 0;
|
||||
dc->aarch64 = 0;
|
||||
dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
|
||||
dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
|
||||
dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
|
||||
dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
dc->user = 0;
|
||||
dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
|
||||
#endif
|
||||
dc->vfp_enabled = 0;
|
||||
dc->vec_len = 0;
|
||||
dc->vec_stride = 0;
|
||||
} else {
|
||||
dc->aarch64 = 0;
|
||||
dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
|
||||
dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
|
||||
dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
|
||||
dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
|
||||
#endif
|
||||
dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
|
||||
dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
|
||||
dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
|
||||
}
|
||||
dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
|
||||
dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
|
||||
dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
|
||||
|
||||
cpu_F0s = tcg_temp_new_i32();
|
||||
cpu_F1s = tcg_temp_new_i32();
|
||||
cpu_F0d = tcg_temp_new_i64();
|
||||
|
@ -10314,7 +10331,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
|||
do {
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* Intercept jump to the magic kernel page. */
|
||||
if (!dc->aarch64 && dc->pc >= 0xffff0000) {
|
||||
if (dc->pc >= 0xffff0000) {
|
||||
/* We always get here via a jump, so know we are not in a
|
||||
conditional execution block. */
|
||||
gen_exception(EXCP_KERNEL_TRAP);
|
||||
|
@ -10362,9 +10379,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
|
|||
tcg_gen_debug_insn_start(dc->pc);
|
||||
}
|
||||
|
||||
if (dc->aarch64) {
|
||||
disas_a64_insn(env, dc);
|
||||
} else if (dc->thumb) {
|
||||
if (dc->thumb) {
|
||||
disas_thumb_insn(env, dc);
|
||||
if (dc->condexec_mask) {
|
||||
dc->condexec_cond = (dc->condexec_cond & 0xe)
|
||||
|
@ -10559,8 +10574,9 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, int pc_pos)
|
|||
{
|
||||
if (is_a64(env)) {
|
||||
env->pc = tcg_ctx.gen_opc_pc[pc_pos];
|
||||
env->condexec_bits = 0;
|
||||
} else {
|
||||
env->regs[15] = tcg_ctx.gen_opc_pc[pc_pos];
|
||||
env->condexec_bits = gen_opc_condexec_bits[pc_pos];
|
||||
}
|
||||
env->condexec_bits = gen_opc_condexec_bits[pc_pos];
|
||||
}
|
||||
|
|
|
@ -24,20 +24,39 @@ typedef struct DisasContext {
|
|||
int vec_len;
|
||||
int vec_stride;
|
||||
int aarch64;
|
||||
#define TMP_A64_MAX 16
|
||||
int tmp_a64_count;
|
||||
TCGv_i64 tmp_a64[TMP_A64_MAX];
|
||||
} DisasContext;
|
||||
|
||||
extern TCGv_ptr cpu_env;
|
||||
|
||||
/* target-specific extra values for is_jmp */
|
||||
/* These instructions trap after executing, so the A32/T32 decoder must
|
||||
* defer them until after the conditional execution state has been updated.
|
||||
* WFI also needs special handling when single-stepping.
|
||||
*/
|
||||
#define DISAS_WFI 4
|
||||
#define DISAS_SWI 5
|
||||
/* For instructions which unconditionally cause an exception we can skip
|
||||
* emitting unreachable code at the end of the TB in the A64 decoder
|
||||
*/
|
||||
#define DISAS_EXC 6
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
void a64_translate_init(void);
|
||||
void disas_a64_insn(CPUARMState *env, DisasContext *s);
|
||||
void gen_intermediate_code_internal_a64(ARMCPU *cpu,
|
||||
TranslationBlock *tb,
|
||||
bool search_pc);
|
||||
void gen_a64_set_pc_im(uint64_t val);
|
||||
#else
|
||||
static inline void a64_translate_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void disas_a64_insn(CPUARMState *env, DisasContext *s)
|
||||
static inline void gen_intermediate_code_internal_a64(ARMCPU *cpu,
|
||||
TranslationBlock *tb,
|
||||
bool search_pc)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -46,4 +65,6 @@ static inline void gen_a64_set_pc_im(uint64_t val)
|
|||
}
|
||||
#endif
|
||||
|
||||
void arm_gen_test_cc(int cc, int label);
|
||||
|
||||
#endif /* TARGET_ARM_TRANSLATE_H */
|
||||
|
|
|
@ -70,6 +70,8 @@ static const char *arm_machines[] = {
|
|||
"xilinx-zynq-a9",
|
||||
"highbank",
|
||||
"midway",
|
||||
"canon-a1100",
|
||||
"cubieboard",
|
||||
};
|
||||
|
||||
static const char *cris_machines[] = {
|
||||
|
|
Loading…
Reference in New Issue