target-arm queue:
* raspi2: implement RNG module * raspi2: implement new SD card controller (but don't wire it up) * sdhci: bugfixes for block transfers * virt: fix cpu object reference leak * Add missing fp_access_check() to aarch64 crypto instructions * cputlb: Don't assume do_unassigned_access() never returns * virt: Add a user option to disallow ITS instantiation * i.MX timers: fix reset handling * ARMv7M NVIC: rewrite to fix broken priority handling and masking * exynos: Fix proper mapping of CPUs by providing real cluster ID * exynos: Fix Linux kernel division by zero for PLLs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJYtW/TAAoJEDwlJe0UNgzezv4P/3j+WOVgVlNL8AQ3RFEzzzz4 IszdrQIFcZ5ICT3MDgH/JMjkpj/C13eGo9eiIFlOvVjtsLlneW10frEB6SGP4ype KpFDHji0cm9MT7gdbgbWbextGU8w7xWV43JmSmEuOxkF/r64u/Ap3CXudB58A+Rv NvbJMHkkR5Q0MIDA4EkOCLn/Ihh78sd99p8+EV3Gu89KiiB4xRf9D3k/O+Sdh58L yvPNat0tjJolzZkAUf6RieFN1F7oBXazR13+E8fDy5OTr25K+S7mehBwSJtQ7dGo VjhR7eMJdyyzi+l+OezQFCUmZI9pENcDdhspSl2mOkPRrQi4gwjEszPcmcNhCNGQ mguQjk7f5KHtLDDzL1HFr+4sKZdoptXZC18JupjN9oCHJvMq4MDHJaUH0bwrHals GhE7cM3aNg8ItJu694ruMLY13Z0+B+TmSLFktRYrjJe3qJEfOQE4EKWXXUZaEe5j L13HPP4nInAUU7kvpuepiYHiR4zBTTgEqRBVdQ/qCkLSuO/EH2TbT9u6pifAtI1S OkBidnbatWflUwLMMa6jt7ZUx+yDsH7y7C1WxmytnPzKudMMOZ5MxI54yLgEEFTs SoelwzfSZb2PlOw3h3UwyRDz3CehkDMUMqzIoqF7Wn/UVb6GHvldq/eVpKOOxtG7 nVTTYBFuSil0LV/LST4X =3qLp -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170228' into staging target-arm queue: * raspi2: implement RNG module * raspi2: implement new SD card controller (but don't wire it up) * sdhci: bugfixes for block transfers * virt: fix cpu object reference leak * Add missing fp_access_check() to aarch64 crypto instructions * cputlb: Don't assume do_unassigned_access() never returns * virt: Add a user option to disallow ITS instantiation * i.MX timers: fix reset handling * ARMv7M NVIC: rewrite to fix broken priority handling and masking * exynos: Fix proper mapping of CPUs by providing real cluster ID * exynos: Fix Linux kernel division by zero for PLLs # gpg: Signature made Tue 28 Feb 2017 12:40:51 GMT # gpg: using RSA key 0x3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20170228: (27 commits) hw/arm/exynos: Fix proper mapping of CPUs by providing real cluster ID hw/arm/exynos: Fix Linux kernel division by zero for PLLs bcm2835_sdhost: add bcm2835 sdhost controller armv7m: Allow SHCSR writes to change pending and active bits armv7m: Raise correct kind of UsageFault for attempts to execute ARM code armv7m: Check exception return consistency armv7m: Extract "exception taken" code into functions armv7m: VECTCLRACTIVE and VECTRESET are UNPREDICTABLE armv7m: Simpler and faster exception start armv7m: Remove unused armv7m_nvic_acknowledge_irq() return value armv7m: Escalate exceptions to HardFault if necessary arm: gic: Remove references to NVIC armv7m: Fix condition check for taking exceptions armv7m: Rewrite NVIC to not use any GIC code armv7m: Implement reading and writing of PRIGROUP armv7m: Rename nvic_state to NVICState ARM i.MX timers: fix reset handling hw/arm/virt: Add a user option to disallow ITS instantiation cputlb: Don't assume do_unassigned_access() never returns Add missing fp_access_check() to aarch64 crypto instructions ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1bbe5dc66b
15
cputlb.c
15
cputlb.c
@ -769,14 +769,13 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
|||||||
pd = iotlbentry->addr & ~TARGET_PAGE_MASK;
|
pd = iotlbentry->addr & ~TARGET_PAGE_MASK;
|
||||||
mr = iotlb_to_region(cpu, pd, iotlbentry->attrs);
|
mr = iotlb_to_region(cpu, pd, iotlbentry->attrs);
|
||||||
if (memory_region_is_unassigned(mr)) {
|
if (memory_region_is_unassigned(mr)) {
|
||||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
cpu_unassigned_access(cpu, addr, false, true, 0, 4);
|
||||||
|
/* The CPU's unassigned access hook might have longjumped out
|
||||||
if (cc->do_unassigned_access) {
|
* with an exception. If it didn't (or there was no hook) then
|
||||||
cc->do_unassigned_access(cpu, addr, false, true, 0, 4);
|
* we can't proceed further.
|
||||||
} else {
|
*/
|
||||||
report_bad_exec(cpu, addr);
|
report_bad_exec(cpu, addr);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend);
|
p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend);
|
||||||
return qemu_ram_addr_from_host_nofail(p);
|
return qemu_ram_addr_from_host_nofail(p);
|
||||||
|
@ -86,6 +86,11 @@ static void bcm2835_peripherals_init(Object *obj)
|
|||||||
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
|
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
|
||||||
OBJECT(&s->gpu_bus_mr), &error_abort);
|
OBJECT(&s->gpu_bus_mr), &error_abort);
|
||||||
|
|
||||||
|
/* Random Number Generator */
|
||||||
|
object_initialize(&s->rng, sizeof(s->rng), TYPE_BCM2835_RNG);
|
||||||
|
object_property_add_child(obj, "rng", OBJECT(&s->rng), NULL);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->rng), sysbus_get_default());
|
||||||
|
|
||||||
/* Extended Mass Media Controller */
|
/* Extended Mass Media Controller */
|
||||||
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
|
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
|
||||||
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
|
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
|
||||||
@ -226,6 +231,16 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0,
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0,
|
||||||
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY));
|
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY));
|
||||||
|
|
||||||
|
/* Random Number Generator */
|
||||||
|
object_property_set_bool(OBJECT(&s->rng), true, "realized", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
|
||||||
|
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
|
||||||
|
|
||||||
/* Extended Mass Media Controller */
|
/* Extended Mass Media Controller */
|
||||||
object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
|
object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
|
||||||
&err);
|
&err);
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
@ -74,6 +75,9 @@
|
|||||||
/* PMU SFR base address */
|
/* PMU SFR base address */
|
||||||
#define EXYNOS4210_PMU_BASE_ADDR 0x10020000
|
#define EXYNOS4210_PMU_BASE_ADDR 0x10020000
|
||||||
|
|
||||||
|
/* Clock controller SFR base address */
|
||||||
|
#define EXYNOS4210_CLK_BASE_ADDR 0x10030000
|
||||||
|
|
||||||
/* Display controllers (FIMD) */
|
/* Display controllers (FIMD) */
|
||||||
#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000
|
#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000
|
||||||
|
|
||||||
@ -138,6 +142,16 @@ void exynos4210_write_secondary(ARMCPU *cpu,
|
|||||||
info->smp_loader_start);
|
info->smp_loader_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t exynos4210_calc_affinity(int cpu)
|
||||||
|
{
|
||||||
|
uint64_t mp_affinity;
|
||||||
|
|
||||||
|
/* Exynos4210 has 0x9 as cluster ID */
|
||||||
|
mp_affinity = (0x9 << ARM_AFF1_SHIFT) | cpu;
|
||||||
|
|
||||||
|
return mp_affinity;
|
||||||
|
}
|
||||||
|
|
||||||
Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
||||||
unsigned long ram_size)
|
unsigned long ram_size)
|
||||||
{
|
{
|
||||||
@ -163,6 +177,8 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
|||||||
}
|
}
|
||||||
|
|
||||||
s->cpu[n] = ARM_CPU(cpuobj);
|
s->cpu[n] = ARM_CPU(cpuobj);
|
||||||
|
object_property_set_int(cpuobj, exynos4210_calc_affinity(n),
|
||||||
|
"mp-affinity", &error_abort);
|
||||||
object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR,
|
object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR,
|
||||||
"reset-cbar", &error_abort);
|
"reset-cbar", &error_abort);
|
||||||
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
|
||||||
@ -297,6 +313,8 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
|
|||||||
*/
|
*/
|
||||||
sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
|
sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
|
||||||
|
|
||||||
|
sysbus_create_simple("exynos4210.clk", EXYNOS4210_CLK_BASE_ADDR, NULL);
|
||||||
|
|
||||||
/* PWM */
|
/* PWM */
|
||||||
sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
|
sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
|
||||||
s->irq_table[exynos4210_get_irq(22, 0)],
|
s->irq_table[exynos4210_get_irq(22, 0)],
|
||||||
|
@ -535,7 +535,6 @@ static void create_v2m(VirtMachineState *vms, qemu_irq *pic)
|
|||||||
static void create_gic(VirtMachineState *vms, qemu_irq *pic)
|
static void create_gic(VirtMachineState *vms, qemu_irq *pic)
|
||||||
{
|
{
|
||||||
/* We create a standalone GIC */
|
/* We create a standalone GIC */
|
||||||
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
|
||||||
DeviceState *gicdev;
|
DeviceState *gicdev;
|
||||||
SysBusDevice *gicbusdev;
|
SysBusDevice *gicbusdev;
|
||||||
const char *gictype;
|
const char *gictype;
|
||||||
@ -605,7 +604,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
|
|||||||
|
|
||||||
fdt_add_gic_node(vms);
|
fdt_add_gic_node(vms);
|
||||||
|
|
||||||
if (type == 3 && !vmc->no_its) {
|
if (type == 3 && vms->its) {
|
||||||
create_its(vms, gicdev);
|
create_its(vms, gicdev);
|
||||||
} else if (type == 2) {
|
} else if (type == 2) {
|
||||||
create_v2m(vms, pic);
|
create_v2m(vms, pic);
|
||||||
@ -1378,6 +1377,7 @@ static void machvirt_init(MachineState *machine)
|
|||||||
}
|
}
|
||||||
|
|
||||||
object_property_set_bool(cpuobj, true, "realized", NULL);
|
object_property_set_bool(cpuobj, true, "realized", NULL);
|
||||||
|
object_unref(cpuobj);
|
||||||
}
|
}
|
||||||
fdt_add_timer_nodes(vms);
|
fdt_add_timer_nodes(vms);
|
||||||
fdt_add_cpu_nodes(vms);
|
fdt_add_cpu_nodes(vms);
|
||||||
@ -1480,6 +1480,20 @@ static void virt_set_highmem(Object *obj, bool value, Error **errp)
|
|||||||
vms->highmem = value;
|
vms->highmem = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool virt_get_its(Object *obj, Error **errp)
|
||||||
|
{
|
||||||
|
VirtMachineState *vms = VIRT_MACHINE(obj);
|
||||||
|
|
||||||
|
return vms->its;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virt_set_its(Object *obj, bool value, Error **errp)
|
||||||
|
{
|
||||||
|
VirtMachineState *vms = VIRT_MACHINE(obj);
|
||||||
|
|
||||||
|
vms->its = value;
|
||||||
|
}
|
||||||
|
|
||||||
static char *virt_get_gic_version(Object *obj, Error **errp)
|
static char *virt_get_gic_version(Object *obj, Error **errp)
|
||||||
{
|
{
|
||||||
VirtMachineState *vms = VIRT_MACHINE(obj);
|
VirtMachineState *vms = VIRT_MACHINE(obj);
|
||||||
@ -1540,6 +1554,7 @@ type_init(machvirt_machine_init);
|
|||||||
static void virt_2_9_instance_init(Object *obj)
|
static void virt_2_9_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
VirtMachineState *vms = VIRT_MACHINE(obj);
|
VirtMachineState *vms = VIRT_MACHINE(obj);
|
||||||
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
||||||
|
|
||||||
/* EL3 is disabled by default on virt: this makes us consistent
|
/* EL3 is disabled by default on virt: this makes us consistent
|
||||||
* between KVM and TCG for this board, and it also allows us to
|
* between KVM and TCG for this board, and it also allows us to
|
||||||
@ -1579,6 +1594,19 @@ static void virt_2_9_instance_init(Object *obj)
|
|||||||
"Set GIC version. "
|
"Set GIC version. "
|
||||||
"Valid values are 2, 3 and host", NULL);
|
"Valid values are 2, 3 and host", NULL);
|
||||||
|
|
||||||
|
if (vmc->no_its) {
|
||||||
|
vms->its = false;
|
||||||
|
} else {
|
||||||
|
/* Default allows ITS instantiation */
|
||||||
|
vms->its = true;
|
||||||
|
object_property_add_bool(obj, "its", virt_get_its,
|
||||||
|
virt_set_its, NULL);
|
||||||
|
object_property_set_description(obj, "its",
|
||||||
|
"Set on/off to enable/disable "
|
||||||
|
"ITS instantiation",
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
vms->memmap = a15memmap;
|
vms->memmap = a15memmap;
|
||||||
vms->irqmap = a15irqmap;
|
vms->irqmap = a15irqmap;
|
||||||
}
|
}
|
||||||
|
@ -156,17 +156,6 @@ static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gic_set_irq_nvic(GICState *s, int irq, int level,
|
|
||||||
int cm, int target)
|
|
||||||
{
|
|
||||||
if (level) {
|
|
||||||
GIC_SET_LEVEL(irq, cm);
|
|
||||||
GIC_SET_PENDING(irq, target);
|
|
||||||
} else {
|
|
||||||
GIC_CLEAR_LEVEL(irq, cm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gic_set_irq_generic(GICState *s, int irq, int level,
|
static void gic_set_irq_generic(GICState *s, int irq, int level,
|
||||||
int cm, int target)
|
int cm, int target)
|
||||||
{
|
{
|
||||||
@ -214,8 +203,6 @@ static void gic_set_irq(void *opaque, int irq, int level)
|
|||||||
|
|
||||||
if (s->revision == REV_11MPCORE) {
|
if (s->revision == REV_11MPCORE) {
|
||||||
gic_set_irq_11mpcore(s, irq, level, cm, target);
|
gic_set_irq_11mpcore(s, irq, level, cm, target);
|
||||||
} else if (s->revision == REV_NVIC) {
|
|
||||||
gic_set_irq_nvic(s, irq, level, cm, target);
|
|
||||||
} else {
|
} else {
|
||||||
gic_set_irq_generic(s, irq, level, cm, target);
|
gic_set_irq_generic(s, irq, level, cm, target);
|
||||||
}
|
}
|
||||||
@ -367,7 +354,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
|
|||||||
return 1023;
|
return 1023;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
if (s->revision == REV_11MPCORE) {
|
||||||
/* Clear pending flags for both level and edge triggered interrupts.
|
/* Clear pending flags for both level and edge triggered interrupts.
|
||||||
* Level triggered IRQs will be reasserted once they become inactive.
|
* Level triggered IRQs will be reasserted once they become inactive.
|
||||||
*/
|
*/
|
||||||
@ -589,11 +576,6 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
|
|||||||
DPRINTF("Set %d pending mask %x\n", irq, cm);
|
DPRINTF("Set %d pending mask %x\n", irq, cm);
|
||||||
GIC_SET_PENDING(irq, cm);
|
GIC_SET_PENDING(irq, cm);
|
||||||
}
|
}
|
||||||
} else if (s->revision == REV_NVIC) {
|
|
||||||
if (GIC_TEST_LEVEL(irq, cm)) {
|
|
||||||
DPRINTF("Set nvic %d pending mask %x\n", irq, cm);
|
|
||||||
GIC_SET_PENDING(irq, cm);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
|
group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
|
||||||
@ -768,7 +750,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
|
|||||||
} else if (offset < 0xf10) {
|
} else if (offset < 0xf10) {
|
||||||
goto bad_reg;
|
goto bad_reg;
|
||||||
} else if (offset < 0xf30) {
|
} else if (offset < 0xf30) {
|
||||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
if (s->revision == REV_11MPCORE) {
|
||||||
goto bad_reg;
|
goto bad_reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,9 +784,6 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
|
|||||||
case 2:
|
case 2:
|
||||||
res = gic_id_gicv2[(offset - 0xfd0) >> 2];
|
res = gic_id_gicv2[(offset - 0xfd0) >> 2];
|
||||||
break;
|
break;
|
||||||
case REV_NVIC:
|
|
||||||
/* Shouldn't be able to get here */
|
|
||||||
abort();
|
|
||||||
default:
|
default:
|
||||||
res = 0;
|
res = 0;
|
||||||
}
|
}
|
||||||
@ -1028,7 +1007,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
|||||||
continue; /* Ignore Non-secure access of Group0 IRQ */
|
continue; /* Ignore Non-secure access of Group0 IRQ */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
if (s->revision == REV_11MPCORE) {
|
||||||
if (value & (1 << (i * 2))) {
|
if (value & (1 << (i * 2))) {
|
||||||
GIC_SET_MODEL(irq + i);
|
GIC_SET_MODEL(irq + i);
|
||||||
} else {
|
} else {
|
||||||
@ -1046,7 +1025,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
|||||||
goto bad_reg;
|
goto bad_reg;
|
||||||
} else if (offset < 0xf20) {
|
} else if (offset < 0xf20) {
|
||||||
/* GICD_CPENDSGIRn */
|
/* GICD_CPENDSGIRn */
|
||||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
if (s->revision == REV_11MPCORE) {
|
||||||
goto bad_reg;
|
goto bad_reg;
|
||||||
}
|
}
|
||||||
irq = (offset - 0xf10);
|
irq = (offset - 0xf10);
|
||||||
@ -1060,7 +1039,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
|||||||
}
|
}
|
||||||
} else if (offset < 0xf30) {
|
} else if (offset < 0xf30) {
|
||||||
/* GICD_SPENDSGIRn */
|
/* GICD_SPENDSGIRn */
|
||||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
if (s->revision == REV_11MPCORE) {
|
||||||
goto bad_reg;
|
goto bad_reg;
|
||||||
}
|
}
|
||||||
irq = (offset - 0xf20);
|
irq = (offset - 0xf20);
|
||||||
|
@ -99,9 +99,7 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
|
|||||||
* [N+32..N+63] PPIs for CPU 1
|
* [N+32..N+63] PPIs for CPU 1
|
||||||
* ...
|
* ...
|
||||||
*/
|
*/
|
||||||
if (s->revision != REV_NVIC) {
|
i += (GIC_INTERNAL * s->num_cpu);
|
||||||
i += (GIC_INTERNAL * s->num_cpu);
|
|
||||||
}
|
|
||||||
qdev_init_gpio_in(DEVICE(s), handler, i);
|
qdev_init_gpio_in(DEVICE(s), handler, i);
|
||||||
|
|
||||||
for (i = 0; i < s->num_cpu; i++) {
|
for (i = 0; i < s->num_cpu; i++) {
|
||||||
@ -121,16 +119,12 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
|
|||||||
memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000);
|
memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000);
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
|
|
||||||
if (s->revision != REV_NVIC) {
|
/* This is the main CPU interface "for this core". It is always
|
||||||
/* This is the main CPU interface "for this core". It is always
|
* present because it is required by both software emulation and KVM.
|
||||||
* present because it is required by both software emulation and KVM.
|
*/
|
||||||
* NVIC is not handled here because its CPU interface is different,
|
memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
|
||||||
* neither it can use KVM.
|
s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100);
|
||||||
*/
|
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
|
||||||
memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
|
|
||||||
s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100);
|
|
||||||
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void arm_gic_common_realize(DeviceState *dev, Error **errp)
|
static void arm_gic_common_realize(DeviceState *dev, Error **errp)
|
||||||
@ -162,7 +156,7 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (s->security_extn &&
|
if (s->security_extn &&
|
||||||
(s->revision == REV_11MPCORE || s->revision == REV_NVIC)) {
|
(s->revision == REV_11MPCORE)) {
|
||||||
error_setg(errp, "this GIC revision does not implement "
|
error_setg(errp, "this GIC revision does not implement "
|
||||||
"the security extensions");
|
"the security extensions");
|
||||||
return;
|
return;
|
||||||
@ -255,7 +249,6 @@ static Property arm_gic_common_properties[] = {
|
|||||||
DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32),
|
DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32),
|
||||||
/* Revision can be 1 or 2 for GIC architecture specification
|
/* Revision can be 1 or 2 for GIC architecture specification
|
||||||
* versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC.
|
* versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC.
|
||||||
* (Internally, 0xffffffff also indicates "not a GIC but an NVIC".)
|
|
||||||
*/
|
*/
|
||||||
DEFINE_PROP_UINT32("revision", GICState, revision, 1),
|
DEFINE_PROP_UINT32("revision", GICState, revision, 1),
|
||||||
/* True if the GIC should implement the security extensions */
|
/* True if the GIC should implement the security extensions */
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -25,9 +25,7 @@
|
|||||||
|
|
||||||
#define ALL_CPU_MASK ((unsigned)(((1 << GIC_NCPU) - 1)))
|
#define ALL_CPU_MASK ((unsigned)(((1 << GIC_NCPU) - 1)))
|
||||||
|
|
||||||
/* The NVIC has 16 internal vectors. However these are not exposed
|
#define GIC_BASE_IRQ 0
|
||||||
through the normal GIC interface. */
|
|
||||||
#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0)
|
|
||||||
|
|
||||||
#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm)
|
#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm)
|
||||||
#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm)
|
#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm)
|
||||||
@ -75,7 +73,6 @@
|
|||||||
|
|
||||||
/* The special cases for the revision property: */
|
/* The special cases for the revision property: */
|
||||||
#define REV_11MPCORE 0
|
#define REV_11MPCORE 0
|
||||||
#define REV_NVIC 0xffffffff
|
|
||||||
|
|
||||||
void gic_set_pending_private(GICState *s, int cpu, int irq);
|
void gic_set_pending_private(GICState *s, int cpu, int irq);
|
||||||
uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs);
|
uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs);
|
||||||
@ -87,7 +84,7 @@ void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val,
|
|||||||
|
|
||||||
static inline bool gic_test_pending(GICState *s, int irq, int cm)
|
static inline bool gic_test_pending(GICState *s, int irq, int cm)
|
||||||
{
|
{
|
||||||
if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) {
|
if (s->revision == REV_11MPCORE) {
|
||||||
return s->irq_state[irq].pending & cm;
|
return s->irq_state[irq].pending & cm;
|
||||||
} else {
|
} else {
|
||||||
/* Edge-triggered interrupts are marked pending on a rising edge, but
|
/* Edge-triggered interrupts are marked pending on a rising edge, but
|
||||||
|
@ -161,3 +161,18 @@ gicv3_redist_write(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size,
|
|||||||
gicv3_redist_badwrite(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor %x write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d: error"
|
gicv3_redist_badwrite(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor %x write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d: error"
|
||||||
gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor %x interrupt %d level changed to %d"
|
gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor %x interrupt %d level changed to %d"
|
||||||
gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor %x pending SGI %d"
|
gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor %x pending SGI %d"
|
||||||
|
|
||||||
|
# hw/intc/armv7m_nvic.c
|
||||||
|
nvic_recompute_state(int vectpending, int exception_prio) "NVIC state recomputed: vectpending %d exception_prio %d"
|
||||||
|
nvic_set_prio(int irq, uint8_t prio) "NVIC set irq %d priority %d"
|
||||||
|
nvic_irq_update(int vectpending, int pendprio, int exception_prio, int level) "NVIC vectpending %d pending prio %d exception_prio %d: setting irq line to %d"
|
||||||
|
nvic_escalate_prio(int irq, int irqprio, int runprio) "NVIC escalating irq %d to HardFault: insufficient priority %d >= %d"
|
||||||
|
nvic_escalate_disabled(int irq) "NVIC escalating irq %d to HardFault: disabled"
|
||||||
|
nvic_set_pending(int irq, int en, int prio) "NVIC set pending irq %d (enabled: %d priority %d)"
|
||||||
|
nvic_clear_pending(int irq, int en, int prio) "NVIC clear pending irq %d (enabled: %d priority %d)"
|
||||||
|
nvic_set_pending_level(int irq) "NVIC set pending: irq %d higher prio than vectpending: setting irq line to 1"
|
||||||
|
nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (prio %d)"
|
||||||
|
nvic_complete_irq(int irq) "NVIC complete IRQ %d"
|
||||||
|
nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d"
|
||||||
|
nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
|
||||||
|
nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
|
||||||
|
@ -26,7 +26,7 @@ obj-$(CONFIG_IVSHMEM) += ivshmem.o
|
|||||||
obj-$(CONFIG_REALVIEW) += arm_sysctl.o
|
obj-$(CONFIG_REALVIEW) += arm_sysctl.o
|
||||||
obj-$(CONFIG_NSERIES) += cbus.o
|
obj-$(CONFIG_NSERIES) += cbus.o
|
||||||
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
|
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
|
||||||
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
|
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o exynos4210_clk.o
|
||||||
obj-$(CONFIG_IMX) += imx_ccm.o
|
obj-$(CONFIG_IMX) += imx_ccm.o
|
||||||
obj-$(CONFIG_IMX) += imx31_ccm.o
|
obj-$(CONFIG_IMX) += imx31_ccm.o
|
||||||
obj-$(CONFIG_IMX) += imx25_ccm.o
|
obj-$(CONFIG_IMX) += imx25_ccm.o
|
||||||
@ -42,6 +42,7 @@ obj-$(CONFIG_OMAP) += omap_sdrc.o
|
|||||||
obj-$(CONFIG_OMAP) += omap_tap.o
|
obj-$(CONFIG_OMAP) += omap_tap.o
|
||||||
obj-$(CONFIG_RASPI) += bcm2835_mbox.o
|
obj-$(CONFIG_RASPI) += bcm2835_mbox.o
|
||||||
obj-$(CONFIG_RASPI) += bcm2835_property.o
|
obj-$(CONFIG_RASPI) += bcm2835_property.o
|
||||||
|
obj-$(CONFIG_RASPI) += bcm2835_rng.o
|
||||||
obj-$(CONFIG_SLAVIO) += slavio_misc.o
|
obj-$(CONFIG_SLAVIO) += slavio_misc.o
|
||||||
obj-$(CONFIG_ZYNQ) += zynq_slcr.o
|
obj-$(CONFIG_ZYNQ) += zynq_slcr.o
|
||||||
obj-$(CONFIG_ZYNQ) += zynq-xadc.o
|
obj-$(CONFIG_ZYNQ) += zynq-xadc.o
|
||||||
|
149
hw/misc/bcm2835_rng.c
Normal file
149
hw/misc/bcm2835_rng.c
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* BCM2835 Random Number Generator emulation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Marcin Chojnacki <marcinch7@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "qemu/osdep.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "crypto/random.h"
|
||||||
|
#include "hw/misc/bcm2835_rng.h"
|
||||||
|
|
||||||
|
static uint32_t get_random_bytes(void)
|
||||||
|
{
|
||||||
|
uint32_t res;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
if (qcrypto_random_bytes((uint8_t *)&res, sizeof(res), &err) < 0) {
|
||||||
|
/* On failure we don't want to return the guest a non-random
|
||||||
|
* value in case they're really using it for cryptographic
|
||||||
|
* purposes, so the best we can do is die here.
|
||||||
|
* This shouldn't happen unless something's broken.
|
||||||
|
* In theory we could implement this device's full FIFO
|
||||||
|
* and interrupt semantics and then just stop filling the
|
||||||
|
* FIFO. That's a lot of work, though, so we assume any
|
||||||
|
* errors are systematic problems and trust that if we didn't
|
||||||
|
* fail as the guest inited then we won't fail later on
|
||||||
|
* mid-run.
|
||||||
|
*/
|
||||||
|
error_report_err(err);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t bcm2835_rng_read(void *opaque, hwaddr offset,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835RngState *s = (BCM2835RngState *)opaque;
|
||||||
|
uint32_t res = 0;
|
||||||
|
|
||||||
|
assert(size == 4);
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case 0x0: /* rng_ctrl */
|
||||||
|
res = s->rng_ctrl;
|
||||||
|
break;
|
||||||
|
case 0x4: /* rng_status */
|
||||||
|
res = s->rng_status | (1 << 24);
|
||||||
|
break;
|
||||||
|
case 0x8: /* rng_data */
|
||||||
|
res = get_random_bytes();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"bcm2835_rng_read: Bad offset %x\n",
|
||||||
|
(int)offset);
|
||||||
|
res = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_rng_write(void *opaque, hwaddr offset,
|
||||||
|
uint64_t value, unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835RngState *s = (BCM2835RngState *)opaque;
|
||||||
|
|
||||||
|
assert(size == 4);
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case 0x0: /* rng_ctrl */
|
||||||
|
s->rng_ctrl = value;
|
||||||
|
break;
|
||||||
|
case 0x4: /* rng_status */
|
||||||
|
/* we shouldn't let the guest write to bits [31..20] */
|
||||||
|
s->rng_status &= ~0xFFFFF; /* clear 20 lower bits */
|
||||||
|
s->rng_status |= value & 0xFFFFF; /* set them to new value */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"bcm2835_rng_write: Bad offset %x\n",
|
||||||
|
(int)offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps bcm2835_rng_ops = {
|
||||||
|
.read = bcm2835_rng_read,
|
||||||
|
.write = bcm2835_rng_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_bcm2835_rng = {
|
||||||
|
.name = TYPE_BCM2835_RNG,
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(rng_ctrl, BCM2835RngState),
|
||||||
|
VMSTATE_UINT32(rng_status, BCM2835RngState),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_rng_init(Object *obj)
|
||||||
|
{
|
||||||
|
BCM2835RngState *s = BCM2835_RNG(obj);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, obj, &bcm2835_rng_ops, s,
|
||||||
|
TYPE_BCM2835_RNG, 0x10);
|
||||||
|
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_rng_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
BCM2835RngState *s = BCM2835_RNG(dev);
|
||||||
|
|
||||||
|
s->rng_ctrl = 0;
|
||||||
|
s->rng_status = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_rng_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->reset = bcm2835_rng_reset;
|
||||||
|
dc->vmsd = &vmstate_bcm2835_rng;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeInfo bcm2835_rng_info = {
|
||||||
|
.name = TYPE_BCM2835_RNG,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(BCM2835RngState),
|
||||||
|
.class_init = bcm2835_rng_class_init,
|
||||||
|
.instance_init = bcm2835_rng_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_rng_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&bcm2835_rng_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(bcm2835_rng_register_types)
|
164
hw/misc/exynos4210_clk.c
Normal file
164
hw/misc/exynos4210_clk.c
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* Exynos4210 Clock Controller Emulation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
|
||||||
|
#define TYPE_EXYNOS4210_CLK "exynos4210.clk"
|
||||||
|
#define EXYNOS4210_CLK(obj) \
|
||||||
|
OBJECT_CHECK(Exynos4210ClkState, (obj), TYPE_EXYNOS4210_CLK)
|
||||||
|
|
||||||
|
#define CLK_PLL_LOCKED BIT(29)
|
||||||
|
|
||||||
|
#define EXYNOS4210_CLK_REGS_MEM_SIZE 0x15104
|
||||||
|
|
||||||
|
typedef struct Exynos4210Reg {
|
||||||
|
const char *name; /* for debug only */
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t reset_value;
|
||||||
|
} Exynos4210Reg;
|
||||||
|
|
||||||
|
/* Clock controller register base: 0x10030000 */
|
||||||
|
static const Exynos4210Reg exynos4210_clk_regs[] = {
|
||||||
|
{"EPLL_LOCK", 0xc010, 0x00000fff},
|
||||||
|
{"VPLL_LOCK", 0xc020, 0x00000fff},
|
||||||
|
{"EPLL_CON0", 0xc110, 0x00300301 | CLK_PLL_LOCKED},
|
||||||
|
{"EPLL_CON1", 0xc114, 0x00000000},
|
||||||
|
{"VPLL_CON0", 0xc120, 0x00240201 | CLK_PLL_LOCKED},
|
||||||
|
{"VPLL_CON1", 0xc124, 0x66010464},
|
||||||
|
{"APLL_LOCK", 0x14000, 0x00000fff},
|
||||||
|
{"MPLL_LOCK", 0x14004, 0x00000fff},
|
||||||
|
{"APLL_CON0", 0x14100, 0x00c80601 | CLK_PLL_LOCKED},
|
||||||
|
{"APLL_CON1", 0x14104, 0x0000001c},
|
||||||
|
{"MPLL_CON0", 0x14108, 0x00c80601 | CLK_PLL_LOCKED},
|
||||||
|
{"MPLL_CON1", 0x1410c, 0x0000001c},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EXYNOS4210_REGS_NUM ARRAY_SIZE(exynos4210_clk_regs)
|
||||||
|
|
||||||
|
typedef struct Exynos4210ClkState {
|
||||||
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
|
MemoryRegion iomem;
|
||||||
|
uint32_t reg[EXYNOS4210_REGS_NUM];
|
||||||
|
} Exynos4210ClkState;
|
||||||
|
|
||||||
|
static uint64_t exynos4210_clk_read(void *opaque, hwaddr offset,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
const Exynos4210ClkState *s = (Exynos4210ClkState *)opaque;
|
||||||
|
const Exynos4210Reg *regs = exynos4210_clk_regs;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
|
||||||
|
if (regs->offset == offset) {
|
||||||
|
return s->reg[i];
|
||||||
|
}
|
||||||
|
regs++;
|
||||||
|
}
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n",
|
||||||
|
__func__, (uint32_t)offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exynos4210_clk_write(void *opaque, hwaddr offset,
|
||||||
|
uint64_t val, unsigned size)
|
||||||
|
{
|
||||||
|
Exynos4210ClkState *s = (Exynos4210ClkState *)opaque;
|
||||||
|
const Exynos4210Reg *regs = exynos4210_clk_regs;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
|
||||||
|
if (regs->offset == offset) {
|
||||||
|
s->reg[i] = val;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
regs++;
|
||||||
|
}
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n",
|
||||||
|
__func__, (uint32_t)offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps exynos4210_clk_ops = {
|
||||||
|
.read = exynos4210_clk_read,
|
||||||
|
.write = exynos4210_clk_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void exynos4210_clk_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
Exynos4210ClkState *s = EXYNOS4210_CLK(dev);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* Set default values for registers */
|
||||||
|
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
|
||||||
|
s->reg[i] = exynos4210_clk_regs[i].reset_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exynos4210_clk_init(Object *obj)
|
||||||
|
{
|
||||||
|
Exynos4210ClkState *s = EXYNOS4210_CLK(obj);
|
||||||
|
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||||
|
|
||||||
|
/* memory mapping */
|
||||||
|
memory_region_init_io(&s->iomem, obj, &exynos4210_clk_ops, s,
|
||||||
|
TYPE_EXYNOS4210_CLK, EXYNOS4210_CLK_REGS_MEM_SIZE);
|
||||||
|
sysbus_init_mmio(dev, &s->iomem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription exynos4210_clk_vmstate = {
|
||||||
|
.name = TYPE_EXYNOS4210_CLK,
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32_ARRAY(reg, Exynos4210ClkState, EXYNOS4210_REGS_NUM),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void exynos4210_clk_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->reset = exynos4210_clk_reset;
|
||||||
|
dc->vmsd = &exynos4210_clk_vmstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo exynos4210_clk_info = {
|
||||||
|
.name = TYPE_EXYNOS4210_CLK,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(Exynos4210ClkState),
|
||||||
|
.instance_init = exynos4210_clk_init,
|
||||||
|
.class_init = exynos4210_clk_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void exynos4210_clk_register(void)
|
||||||
|
{
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "Clock init\n");
|
||||||
|
type_register_static(&exynos4210_clk_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(exynos4210_clk_register)
|
@ -6,3 +6,4 @@ common-obj-$(CONFIG_SDHCI) += sdhci.o
|
|||||||
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
|
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
|
||||||
obj-$(CONFIG_OMAP) += omap_mmc.o
|
obj-$(CONFIG_OMAP) += omap_mmc.o
|
||||||
obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o
|
obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o
|
||||||
|
obj-$(CONFIG_RASPI) += bcm2835_sdhost.o
|
||||||
|
429
hw/sd/bcm2835_sdhost.c
Normal file
429
hw/sd/bcm2835_sdhost.c
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
/*
|
||||||
|
* Raspberry Pi (BCM2835) SD Host Controller
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Antfield SAS
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Clement Deschamps <clement.deschamps@antfield.fr>
|
||||||
|
* Luc Michel <luc.michel@antfield.fr>
|
||||||
|
*
|
||||||
|
* 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 "qemu/osdep.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "sysemu/blockdev.h"
|
||||||
|
#include "hw/sd/bcm2835_sdhost.h"
|
||||||
|
|
||||||
|
#define TYPE_BCM2835_SDHOST_BUS "bcm2835-sdhost-bus"
|
||||||
|
#define BCM2835_SDHOST_BUS(obj) \
|
||||||
|
OBJECT_CHECK(SDBus, (obj), TYPE_BCM2835_SDHOST_BUS)
|
||||||
|
|
||||||
|
#define SDCMD 0x00 /* Command to SD card - 16 R/W */
|
||||||
|
#define SDARG 0x04 /* Argument to SD card - 32 R/W */
|
||||||
|
#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */
|
||||||
|
#define SDCDIV 0x0c /* Start value for clock divider - 11 R/W */
|
||||||
|
#define SDRSP0 0x10 /* SD card rsp (31:0) - 32 R */
|
||||||
|
#define SDRSP1 0x14 /* SD card rsp (63:32) - 32 R */
|
||||||
|
#define SDRSP2 0x18 /* SD card rsp (95:64) - 32 R */
|
||||||
|
#define SDRSP3 0x1c /* SD card rsp (127:96) - 32 R */
|
||||||
|
#define SDHSTS 0x20 /* SD host status - 11 R */
|
||||||
|
#define SDVDD 0x30 /* SD card power control - 1 R/W */
|
||||||
|
#define SDEDM 0x34 /* Emergency Debug Mode - 13 R/W */
|
||||||
|
#define SDHCFG 0x38 /* Host configuration - 2 R/W */
|
||||||
|
#define SDHBCT 0x3c /* Host byte count (debug) - 32 R/W */
|
||||||
|
#define SDDATA 0x40 /* Data to/from SD card - 32 R/W */
|
||||||
|
#define SDHBLC 0x50 /* Host block count (SDIO/SDHC) - 9 R/W */
|
||||||
|
|
||||||
|
#define SDCMD_NEW_FLAG 0x8000
|
||||||
|
#define SDCMD_FAIL_FLAG 0x4000
|
||||||
|
#define SDCMD_BUSYWAIT 0x800
|
||||||
|
#define SDCMD_NO_RESPONSE 0x400
|
||||||
|
#define SDCMD_LONG_RESPONSE 0x200
|
||||||
|
#define SDCMD_WRITE_CMD 0x80
|
||||||
|
#define SDCMD_READ_CMD 0x40
|
||||||
|
#define SDCMD_CMD_MASK 0x3f
|
||||||
|
|
||||||
|
#define SDCDIV_MAX_CDIV 0x7ff
|
||||||
|
|
||||||
|
#define SDHSTS_BUSY_IRPT 0x400
|
||||||
|
#define SDHSTS_BLOCK_IRPT 0x200
|
||||||
|
#define SDHSTS_SDIO_IRPT 0x100
|
||||||
|
#define SDHSTS_REW_TIME_OUT 0x80
|
||||||
|
#define SDHSTS_CMD_TIME_OUT 0x40
|
||||||
|
#define SDHSTS_CRC16_ERROR 0x20
|
||||||
|
#define SDHSTS_CRC7_ERROR 0x10
|
||||||
|
#define SDHSTS_FIFO_ERROR 0x08
|
||||||
|
/* Reserved */
|
||||||
|
/* Reserved */
|
||||||
|
#define SDHSTS_DATA_FLAG 0x01
|
||||||
|
|
||||||
|
#define SDHCFG_BUSY_IRPT_EN (1 << 10)
|
||||||
|
#define SDHCFG_BLOCK_IRPT_EN (1 << 8)
|
||||||
|
#define SDHCFG_SDIO_IRPT_EN (1 << 5)
|
||||||
|
#define SDHCFG_DATA_IRPT_EN (1 << 4)
|
||||||
|
#define SDHCFG_SLOW_CARD (1 << 3)
|
||||||
|
#define SDHCFG_WIDE_EXT_BUS (1 << 2)
|
||||||
|
#define SDHCFG_WIDE_INT_BUS (1 << 1)
|
||||||
|
#define SDHCFG_REL_CMD_LINE (1 << 0)
|
||||||
|
|
||||||
|
#define SDEDM_FORCE_DATA_MODE (1 << 19)
|
||||||
|
#define SDEDM_CLOCK_PULSE (1 << 20)
|
||||||
|
#define SDEDM_BYPASS (1 << 21)
|
||||||
|
|
||||||
|
#define SDEDM_WRITE_THRESHOLD_SHIFT 9
|
||||||
|
#define SDEDM_READ_THRESHOLD_SHIFT 14
|
||||||
|
#define SDEDM_THRESHOLD_MASK 0x1f
|
||||||
|
|
||||||
|
#define SDEDM_FSM_MASK 0xf
|
||||||
|
#define SDEDM_FSM_IDENTMODE 0x0
|
||||||
|
#define SDEDM_FSM_DATAMODE 0x1
|
||||||
|
#define SDEDM_FSM_READDATA 0x2
|
||||||
|
#define SDEDM_FSM_WRITEDATA 0x3
|
||||||
|
#define SDEDM_FSM_READWAIT 0x4
|
||||||
|
#define SDEDM_FSM_READCRC 0x5
|
||||||
|
#define SDEDM_FSM_WRITECRC 0x6
|
||||||
|
#define SDEDM_FSM_WRITEWAIT1 0x7
|
||||||
|
#define SDEDM_FSM_POWERDOWN 0x8
|
||||||
|
#define SDEDM_FSM_POWERUP 0x9
|
||||||
|
#define SDEDM_FSM_WRITESTART1 0xa
|
||||||
|
#define SDEDM_FSM_WRITESTART2 0xb
|
||||||
|
#define SDEDM_FSM_GENPULSES 0xc
|
||||||
|
#define SDEDM_FSM_WRITEWAIT2 0xd
|
||||||
|
#define SDEDM_FSM_STARTPOWDOWN 0xf
|
||||||
|
|
||||||
|
#define SDDATA_FIFO_WORDS 16
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_update_irq(BCM2835SDHostState *s)
|
||||||
|
{
|
||||||
|
uint32_t irq = s->status &
|
||||||
|
(SDHSTS_BUSY_IRPT | SDHSTS_BLOCK_IRPT | SDHSTS_SDIO_IRPT);
|
||||||
|
qemu_set_irq(s->irq, !!irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_send_command(BCM2835SDHostState *s)
|
||||||
|
{
|
||||||
|
SDRequest request;
|
||||||
|
uint8_t rsp[16];
|
||||||
|
int rlen;
|
||||||
|
|
||||||
|
request.cmd = s->cmd & SDCMD_CMD_MASK;
|
||||||
|
request.arg = s->cmdarg;
|
||||||
|
|
||||||
|
rlen = sdbus_do_command(&s->sdbus, &request, rsp);
|
||||||
|
if (rlen < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (!(s->cmd & SDCMD_NO_RESPONSE)) {
|
||||||
|
#define RWORD(n) (((uint32_t)rsp[n] << 24) | (rsp[n + 1] << 16) \
|
||||||
|
| (rsp[n + 2] << 8) | rsp[n + 3])
|
||||||
|
if (rlen == 0 || (rlen == 4 && (s->cmd & SDCMD_LONG_RESPONSE))) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (rlen != 4 && rlen != 16) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (rlen == 4) {
|
||||||
|
s->rsp[0] = RWORD(0);
|
||||||
|
s->rsp[1] = s->rsp[2] = s->rsp[3] = 0;
|
||||||
|
} else {
|
||||||
|
s->rsp[0] = RWORD(12);
|
||||||
|
s->rsp[1] = RWORD(8);
|
||||||
|
s->rsp[2] = RWORD(4);
|
||||||
|
s->rsp[3] = RWORD(0);
|
||||||
|
}
|
||||||
|
#undef RWORD
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
s->cmd |= SDCMD_FAIL_FLAG;
|
||||||
|
s->status |= SDHSTS_CMD_TIME_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_fifo_push(BCM2835SDHostState *s, uint32_t value)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (s->fifo_len == BCM2835_SDHOST_FIFO_LEN) {
|
||||||
|
/* FIFO overflow */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
n = (s->fifo_pos + s->fifo_len) & (BCM2835_SDHOST_FIFO_LEN - 1);
|
||||||
|
s->fifo_len++;
|
||||||
|
s->fifo[n] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t bcm2835_sdhost_fifo_pop(BCM2835SDHostState *s)
|
||||||
|
{
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
if (s->fifo_len == 0) {
|
||||||
|
/* FIFO underflow */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
value = s->fifo[s->fifo_pos];
|
||||||
|
s->fifo_len--;
|
||||||
|
s->fifo_pos = (s->fifo_pos + 1) & (BCM2835_SDHOST_FIFO_LEN - 1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s)
|
||||||
|
{
|
||||||
|
uint32_t value = 0;
|
||||||
|
int n;
|
||||||
|
int is_read;
|
||||||
|
|
||||||
|
is_read = (s->cmd & SDCMD_READ_CMD) != 0;
|
||||||
|
if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))) {
|
||||||
|
if (is_read) {
|
||||||
|
n = 0;
|
||||||
|
while (s->datacnt && s->fifo_len < BCM2835_SDHOST_FIFO_LEN) {
|
||||||
|
value |= (uint32_t)sdbus_read_data(&s->sdbus) << (n * 8);
|
||||||
|
s->datacnt--;
|
||||||
|
n++;
|
||||||
|
if (n == 4) {
|
||||||
|
bcm2835_sdhost_fifo_push(s, value);
|
||||||
|
n = 0;
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n != 0) {
|
||||||
|
bcm2835_sdhost_fifo_push(s, value);
|
||||||
|
}
|
||||||
|
} else { /* write */
|
||||||
|
n = 0;
|
||||||
|
while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
|
||||||
|
if (n == 0) {
|
||||||
|
value = bcm2835_sdhost_fifo_pop(s);
|
||||||
|
n = 4;
|
||||||
|
}
|
||||||
|
n--;
|
||||||
|
s->datacnt--;
|
||||||
|
sdbus_write_data(&s->sdbus, value & 0xff);
|
||||||
|
value >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s->datacnt == 0) {
|
||||||
|
s->status |= SDHSTS_DATA_FLAG;
|
||||||
|
|
||||||
|
s->edm &= ~0xf;
|
||||||
|
s->edm |= SDEDM_FSM_DATAMODE;
|
||||||
|
|
||||||
|
if (s->config & SDHCFG_DATA_IRPT_EN) {
|
||||||
|
s->status |= SDHSTS_SDIO_IRPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((s->cmd & SDCMD_BUSYWAIT) && (s->config & SDHCFG_BUSY_IRPT_EN)) {
|
||||||
|
s->status |= SDHSTS_BUSY_IRPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((s->cmd & SDCMD_WRITE_CMD) && (s->config & SDHCFG_BLOCK_IRPT_EN)) {
|
||||||
|
s->status |= SDHSTS_BLOCK_IRPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bcm2835_sdhost_update_irq(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->edm &= ~(0x1f << 4);
|
||||||
|
s->edm |= ((s->fifo_len & 0x1f) << 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t bcm2835_sdhost_read(void *opaque, hwaddr offset,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835SDHostState *s = (BCM2835SDHostState *)opaque;
|
||||||
|
uint32_t res = 0;
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case SDCMD:
|
||||||
|
res = s->cmd;
|
||||||
|
break;
|
||||||
|
case SDHSTS:
|
||||||
|
res = s->status;
|
||||||
|
break;
|
||||||
|
case SDRSP0:
|
||||||
|
res = s->rsp[0];
|
||||||
|
break;
|
||||||
|
case SDRSP1:
|
||||||
|
res = s->rsp[1];
|
||||||
|
break;
|
||||||
|
case SDRSP2:
|
||||||
|
res = s->rsp[2];
|
||||||
|
break;
|
||||||
|
case SDRSP3:
|
||||||
|
res = s->rsp[3];
|
||||||
|
break;
|
||||||
|
case SDEDM:
|
||||||
|
res = s->edm;
|
||||||
|
break;
|
||||||
|
case SDVDD:
|
||||||
|
res = s->vdd;
|
||||||
|
break;
|
||||||
|
case SDDATA:
|
||||||
|
res = bcm2835_sdhost_fifo_pop(s);
|
||||||
|
bcm2835_sdhost_fifo_run(s);
|
||||||
|
break;
|
||||||
|
case SDHBCT:
|
||||||
|
res = s->hbct;
|
||||||
|
break;
|
||||||
|
case SDHBLC:
|
||||||
|
res = s->hblc;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
res = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_write(void *opaque, hwaddr offset,
|
||||||
|
uint64_t value, unsigned size)
|
||||||
|
{
|
||||||
|
BCM2835SDHostState *s = (BCM2835SDHostState *)opaque;
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case SDCMD:
|
||||||
|
s->cmd = value;
|
||||||
|
if (value & SDCMD_NEW_FLAG) {
|
||||||
|
bcm2835_sdhost_send_command(s);
|
||||||
|
bcm2835_sdhost_fifo_run(s);
|
||||||
|
s->cmd &= ~SDCMD_NEW_FLAG;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDTOUT:
|
||||||
|
break;
|
||||||
|
case SDCDIV:
|
||||||
|
break;
|
||||||
|
case SDHSTS:
|
||||||
|
s->status &= ~value;
|
||||||
|
bcm2835_sdhost_update_irq(s);
|
||||||
|
break;
|
||||||
|
case SDARG:
|
||||||
|
s->cmdarg = value;
|
||||||
|
break;
|
||||||
|
case SDEDM:
|
||||||
|
if ((value & 0xf) == 0xf) {
|
||||||
|
/* power down */
|
||||||
|
value &= ~0xf;
|
||||||
|
}
|
||||||
|
s->edm = value;
|
||||||
|
break;
|
||||||
|
case SDHCFG:
|
||||||
|
s->config = value;
|
||||||
|
bcm2835_sdhost_fifo_run(s);
|
||||||
|
break;
|
||||||
|
case SDVDD:
|
||||||
|
s->vdd = value;
|
||||||
|
break;
|
||||||
|
case SDDATA:
|
||||||
|
bcm2835_sdhost_fifo_push(s, value);
|
||||||
|
bcm2835_sdhost_fifo_run(s);
|
||||||
|
break;
|
||||||
|
case SDHBCT:
|
||||||
|
s->hbct = value;
|
||||||
|
break;
|
||||||
|
case SDHBLC:
|
||||||
|
s->hblc = value;
|
||||||
|
s->datacnt = s->hblc * s->hbct;
|
||||||
|
bcm2835_sdhost_fifo_run(s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||||
|
__func__, offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps bcm2835_sdhost_ops = {
|
||||||
|
.read = bcm2835_sdhost_read,
|
||||||
|
.write = bcm2835_sdhost_write,
|
||||||
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_bcm2835_sdhost = {
|
||||||
|
.name = TYPE_BCM2835_SDHOST,
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(cmd, BCM2835SDHostState),
|
||||||
|
VMSTATE_UINT32(cmdarg, BCM2835SDHostState),
|
||||||
|
VMSTATE_UINT32(status, BCM2835SDHostState),
|
||||||
|
VMSTATE_UINT32_ARRAY(rsp, BCM2835SDHostState, 4),
|
||||||
|
VMSTATE_UINT32(config, BCM2835SDHostState),
|
||||||
|
VMSTATE_UINT32(edm, BCM2835SDHostState),
|
||||||
|
VMSTATE_UINT32(vdd, BCM2835SDHostState),
|
||||||
|
VMSTATE_UINT32(hbct, BCM2835SDHostState),
|
||||||
|
VMSTATE_UINT32(hblc, BCM2835SDHostState),
|
||||||
|
VMSTATE_INT32(fifo_pos, BCM2835SDHostState),
|
||||||
|
VMSTATE_INT32(fifo_len, BCM2835SDHostState),
|
||||||
|
VMSTATE_UINT32_ARRAY(fifo, BCM2835SDHostState, BCM2835_SDHOST_FIFO_LEN),
|
||||||
|
VMSTATE_UINT32(datacnt, BCM2835SDHostState),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_init(Object *obj)
|
||||||
|
{
|
||||||
|
BCM2835SDHostState *s = BCM2835_SDHOST(obj);
|
||||||
|
|
||||||
|
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
|
||||||
|
TYPE_BCM2835_SDHOST_BUS, DEVICE(s), "sd-bus");
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, obj, &bcm2835_sdhost_ops, s,
|
||||||
|
TYPE_BCM2835_SDHOST, 0x1000);
|
||||||
|
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
|
||||||
|
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
BCM2835SDHostState *s = BCM2835_SDHOST(dev);
|
||||||
|
|
||||||
|
s->cmd = 0;
|
||||||
|
s->cmdarg = 0;
|
||||||
|
s->edm = 0x0000c60f;
|
||||||
|
s->config = 0;
|
||||||
|
s->hbct = 0;
|
||||||
|
s->hblc = 0;
|
||||||
|
s->datacnt = 0;
|
||||||
|
s->fifo_pos = 0;
|
||||||
|
s->fifo_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->reset = bcm2835_sdhost_reset;
|
||||||
|
dc->vmsd = &vmstate_bcm2835_sdhost;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeInfo bcm2835_sdhost_info = {
|
||||||
|
.name = TYPE_BCM2835_SDHOST,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(BCM2835SDHostState),
|
||||||
|
.class_init = bcm2835_sdhost_class_init,
|
||||||
|
.instance_init = bcm2835_sdhost_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeInfo bcm2835_sdhost_bus_info = {
|
||||||
|
.name = TYPE_BCM2835_SDHOST_BUS,
|
||||||
|
.parent = TYPE_SD_BUS,
|
||||||
|
.instance_size = sizeof(SDBus),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bcm2835_sdhost_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&bcm2835_sdhost_info);
|
||||||
|
type_register_static(&bcm2835_sdhost_bus_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(bcm2835_sdhost_register_types)
|
@ -119,6 +119,7 @@
|
|||||||
(SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
|
(SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
|
||||||
(SDHC_CAPAB_TOCLKFREQ))
|
(SDHC_CAPAB_TOCLKFREQ))
|
||||||
|
|
||||||
|
#define MASK_TRNMOD 0x0037
|
||||||
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
|
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
|
||||||
|
|
||||||
static uint8_t sdhci_slotint(SDHCIState *s)
|
static uint8_t sdhci_slotint(SDHCIState *s)
|
||||||
@ -486,6 +487,11 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
|
|||||||
uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
|
uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
|
||||||
uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
|
uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
|
||||||
|
|
||||||
|
if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) {
|
||||||
|
qemu_log_mask(LOG_UNIMP, "infinite transfer is not supported\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
|
/* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
|
||||||
* possible stop at page boundary if initial address is not page aligned,
|
* possible stop at page boundary if initial address is not page aligned,
|
||||||
* allow them to work properly */
|
* allow them to work properly */
|
||||||
@ -564,7 +570,6 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* single block SDMA transfer */
|
/* single block SDMA transfer */
|
||||||
|
|
||||||
static void sdhci_sdma_transfer_single_block(SDHCIState *s)
|
static void sdhci_sdma_transfer_single_block(SDHCIState *s)
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
@ -583,10 +588,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s)
|
|||||||
sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
|
sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s->blkcnt--;
|
||||||
if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
|
|
||||||
s->blkcnt--;
|
|
||||||
}
|
|
||||||
|
|
||||||
sdhci_end_transfer(s);
|
sdhci_end_transfer(s);
|
||||||
}
|
}
|
||||||
@ -797,11 +799,6 @@ static void sdhci_data_transfer(void *opaque)
|
|||||||
if (s->trnmod & SDHC_TRNS_DMA) {
|
if (s->trnmod & SDHC_TRNS_DMA) {
|
||||||
switch (SDHC_DMA_TYPE(s->hostctl)) {
|
switch (SDHC_DMA_TYPE(s->hostctl)) {
|
||||||
case SDHC_CTRL_SDMA:
|
case SDHC_CTRL_SDMA:
|
||||||
if ((s->trnmod & SDHC_TRNS_MULTI) &&
|
|
||||||
(!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
|
if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
|
||||||
sdhci_sdma_transfer_single_block(s);
|
sdhci_sdma_transfer_single_block(s);
|
||||||
} else {
|
} else {
|
||||||
@ -1022,7 +1019,11 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
|||||||
/* Writing to last byte of sdmasysad might trigger transfer */
|
/* Writing to last byte of sdmasysad might trigger transfer */
|
||||||
if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
|
if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
|
||||||
s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
|
s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
|
||||||
sdhci_sdma_transfer_multi_blocks(s);
|
if (s->trnmod & SDHC_TRNS_MULTI) {
|
||||||
|
sdhci_sdma_transfer_multi_blocks(s);
|
||||||
|
} else {
|
||||||
|
sdhci_sdma_transfer_single_block(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDHC_BLKSIZE:
|
case SDHC_BLKSIZE:
|
||||||
@ -1050,7 +1051,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
|||||||
if (!(s->capareg & SDHC_CAN_DO_DMA)) {
|
if (!(s->capareg & SDHC_CAN_DO_DMA)) {
|
||||||
value &= ~SDHC_TRNS_DMA;
|
value &= ~SDHC_TRNS_DMA;
|
||||||
}
|
}
|
||||||
MASKED_WRITE(s->trnmod, mask, value);
|
MASKED_WRITE(s->trnmod, mask, value & MASK_TRNMOD);
|
||||||
MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16);
|
MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16);
|
||||||
|
|
||||||
/* Writing to the upper byte of CMDREG triggers SD command generation */
|
/* Writing to the upper byte of CMDREG triggers SD command generation */
|
||||||
|
@ -296,18 +296,23 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size)
|
|||||||
return reg_value;
|
return reg_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void imx_gpt_reset(DeviceState *dev)
|
|
||||||
{
|
|
||||||
IMXGPTState *s = IMX_GPT(dev);
|
|
||||||
|
|
||||||
|
static void imx_gpt_reset_common(IMXGPTState *s, bool is_soft_reset)
|
||||||
|
{
|
||||||
/* stop timer */
|
/* stop timer */
|
||||||
ptimer_stop(s->timer);
|
ptimer_stop(s->timer);
|
||||||
|
|
||||||
/*
|
/* Soft reset and hard reset differ only in their handling of the CR
|
||||||
* Soft reset doesn't touch some bits; hard reset clears them
|
* register -- soft reset preserves the values of some bits there.
|
||||||
*/
|
*/
|
||||||
s->cr &= ~(GPT_CR_EN|GPT_CR_ENMOD|GPT_CR_STOPEN|GPT_CR_DOZEN|
|
if (is_soft_reset) {
|
||||||
GPT_CR_WAITEN|GPT_CR_DBGEN);
|
/* Clear all CR bits except those that are preserved by soft reset. */
|
||||||
|
s->cr &= GPT_CR_EN | GPT_CR_ENMOD | GPT_CR_STOPEN | GPT_CR_DOZEN |
|
||||||
|
GPT_CR_WAITEN | GPT_CR_DBGEN |
|
||||||
|
(GPT_CR_CLKSRC_MASK << GPT_CR_CLKSRC_SHIFT);
|
||||||
|
} else {
|
||||||
|
s->cr = 0;
|
||||||
|
}
|
||||||
s->sr = 0;
|
s->sr = 0;
|
||||||
s->pr = 0;
|
s->pr = 0;
|
||||||
s->ir = 0;
|
s->ir = 0;
|
||||||
@ -333,6 +338,18 @@ static void imx_gpt_reset(DeviceState *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void imx_gpt_soft_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
IMXGPTState *s = IMX_GPT(dev);
|
||||||
|
imx_gpt_reset_common(s, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void imx_gpt_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
IMXGPTState *s = IMX_GPT(dev);
|
||||||
|
imx_gpt_reset_common(s, false);
|
||||||
|
}
|
||||||
|
|
||||||
static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
|
static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
@ -348,7 +365,7 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
|
|||||||
s->cr = value & ~0x7c14;
|
s->cr = value & ~0x7c14;
|
||||||
if (s->cr & GPT_CR_SWR) { /* force reset */
|
if (s->cr & GPT_CR_SWR) { /* force reset */
|
||||||
/* handle the reset */
|
/* handle the reset */
|
||||||
imx_gpt_reset(DEVICE(s));
|
imx_gpt_soft_reset(DEVICE(s));
|
||||||
} else {
|
} else {
|
||||||
/* set our freq, as the source might have changed */
|
/* set our freq, as the source might have changed */
|
||||||
imx_gpt_set_freq(s);
|
imx_gpt_set_freq(s);
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "hw/dma/bcm2835_dma.h"
|
#include "hw/dma/bcm2835_dma.h"
|
||||||
#include "hw/intc/bcm2835_ic.h"
|
#include "hw/intc/bcm2835_ic.h"
|
||||||
#include "hw/misc/bcm2835_property.h"
|
#include "hw/misc/bcm2835_property.h"
|
||||||
|
#include "hw/misc/bcm2835_rng.h"
|
||||||
#include "hw/misc/bcm2835_mbox.h"
|
#include "hw/misc/bcm2835_mbox.h"
|
||||||
#include "hw/sd/sdhci.h"
|
#include "hw/sd/sdhci.h"
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ typedef struct BCM2835PeripheralState {
|
|||||||
BCM2835DMAState dma;
|
BCM2835DMAState dma;
|
||||||
BCM2835ICState ic;
|
BCM2835ICState ic;
|
||||||
BCM2835PropertyState property;
|
BCM2835PropertyState property;
|
||||||
|
BCM2835RngState rng;
|
||||||
BCM2835MboxState mboxes;
|
BCM2835MboxState mboxes;
|
||||||
SDHCIState sdhci;
|
SDHCIState sdhci;
|
||||||
} BCM2835PeripheralState;
|
} BCM2835PeripheralState;
|
||||||
|
@ -93,6 +93,7 @@ typedef struct {
|
|||||||
FWCfgState *fw_cfg;
|
FWCfgState *fw_cfg;
|
||||||
bool secure;
|
bool secure;
|
||||||
bool highmem;
|
bool highmem;
|
||||||
|
bool its;
|
||||||
bool virt;
|
bool virt;
|
||||||
int32_t gic_version;
|
int32_t gic_version;
|
||||||
struct arm_boot_info bootinfo;
|
struct arm_boot_info bootinfo;
|
||||||
|
27
include/hw/misc/bcm2835_rng.h
Normal file
27
include/hw/misc/bcm2835_rng.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* BCM2835 Random Number Generator emulation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Marcin Chojnacki <marcinch7@gmail.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BCM2835_RNG_H
|
||||||
|
#define BCM2835_RNG_H
|
||||||
|
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
|
||||||
|
#define TYPE_BCM2835_RNG "bcm2835-rng"
|
||||||
|
#define BCM2835_RNG(obj) \
|
||||||
|
OBJECT_CHECK(BCM2835RngState, (obj), TYPE_BCM2835_RNG)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SysBusDevice busdev;
|
||||||
|
MemoryRegion iomem;
|
||||||
|
|
||||||
|
uint32_t rng_ctrl;
|
||||||
|
uint32_t rng_status;
|
||||||
|
} BCM2835RngState;
|
||||||
|
|
||||||
|
#endif
|
48
include/hw/sd/bcm2835_sdhost.h
Normal file
48
include/hw/sd/bcm2835_sdhost.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Raspberry Pi (BCM2835) SD Host Controller
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Antfield SAS
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Clement Deschamps <clement.deschamps@antfield.fr>
|
||||||
|
* Luc Michel <luc.michel@antfield.fr>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BCM2835_SDHOST_H
|
||||||
|
#define BCM2835_SDHOST_H
|
||||||
|
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "hw/sd/sd.h"
|
||||||
|
|
||||||
|
#define TYPE_BCM2835_SDHOST "bcm2835-sdhost"
|
||||||
|
#define BCM2835_SDHOST(obj) \
|
||||||
|
OBJECT_CHECK(BCM2835SDHostState, (obj), TYPE_BCM2835_SDHOST)
|
||||||
|
|
||||||
|
#define BCM2835_SDHOST_FIFO_LEN 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SysBusDevice busdev;
|
||||||
|
SDBus sdbus;
|
||||||
|
MemoryRegion iomem;
|
||||||
|
|
||||||
|
uint32_t cmd;
|
||||||
|
uint32_t cmdarg;
|
||||||
|
uint32_t status;
|
||||||
|
uint32_t rsp[4];
|
||||||
|
uint32_t config;
|
||||||
|
uint32_t edm;
|
||||||
|
uint32_t vdd;
|
||||||
|
uint32_t hbct;
|
||||||
|
uint32_t hblc;
|
||||||
|
int32_t fifo_pos;
|
||||||
|
int32_t fifo_len;
|
||||||
|
uint32_t fifo[BCM2835_SDHOST_FIFO_LEN];
|
||||||
|
uint32_t datacnt;
|
||||||
|
|
||||||
|
qemu_irq irq;
|
||||||
|
} BCM2835SDHostState;
|
||||||
|
|
||||||
|
#endif
|
@ -574,6 +574,7 @@ void cpu_loop(CPUARMState *env)
|
|||||||
switch(trapnr) {
|
switch(trapnr) {
|
||||||
case EXCP_UDEF:
|
case EXCP_UDEF:
|
||||||
case EXCP_NOCP:
|
case EXCP_NOCP:
|
||||||
|
case EXCP_INVSTATE:
|
||||||
{
|
{
|
||||||
TaskState *ts = cs->opaque;
|
TaskState *ts = cs->opaque;
|
||||||
uint32_t opcode;
|
uint32_t opcode;
|
||||||
|
@ -338,13 +338,6 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
|||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
|
|
||||||
if (interrupt_request & CPU_INTERRUPT_FIQ
|
|
||||||
&& !(env->daif & PSTATE_F)) {
|
|
||||||
cs->exception_index = EXCP_FIQ;
|
|
||||||
cc->do_interrupt(cs);
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
/* ARMv7-M interrupt return works by loading a magic value
|
/* ARMv7-M interrupt return works by loading a magic value
|
||||||
* into the PC. On real hardware the load causes the
|
* into the PC. On real hardware the load causes the
|
||||||
* return to occur. The qemu implementation performs the
|
* return to occur. The qemu implementation performs the
|
||||||
@ -354,9 +347,16 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
|||||||
* the stack if an interrupt occurred at the wrong time.
|
* the stack if an interrupt occurred at the wrong time.
|
||||||
* We avoid this by disabling interrupts when
|
* We avoid this by disabling interrupts when
|
||||||
* pc contains a magic address.
|
* pc contains a magic address.
|
||||||
|
*
|
||||||
|
* ARMv7-M interrupt masking works differently than -A or -R.
|
||||||
|
* There is no FIQ/IRQ distinction. Instead of I and F bits
|
||||||
|
* masking FIQ and IRQ interrupts, an exception is taken only
|
||||||
|
* if it is higher priority than the current execution priority
|
||||||
|
* (which depends on state like BASEPRI, FAULTMASK and the
|
||||||
|
* currently active exception).
|
||||||
*/
|
*/
|
||||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||||
&& !(env->daif & PSTATE_I)
|
&& (armv7m_nvic_can_take_pending_exception(env->nvic))
|
||||||
&& (env->regs[15] < 0xfffffff0)) {
|
&& (env->regs[15] < 0xfffffff0)) {
|
||||||
cs->exception_index = EXCP_IRQ;
|
cs->exception_index = EXCP_IRQ;
|
||||||
cc->do_interrupt(cs);
|
cc->do_interrupt(cs);
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
#define EXCP_VFIQ 15
|
#define EXCP_VFIQ 15
|
||||||
#define EXCP_SEMIHOST 16 /* semihosting call */
|
#define EXCP_SEMIHOST 16 /* semihosting call */
|
||||||
#define EXCP_NOCP 17 /* v7M NOCP UsageFault */
|
#define EXCP_NOCP 17 /* v7M NOCP UsageFault */
|
||||||
|
#define EXCP_INVSTATE 18 /* v7M INVSTATE UsageFault */
|
||||||
|
|
||||||
#define ARMV7M_EXCP_RESET 1
|
#define ARMV7M_EXCP_RESET 1
|
||||||
#define ARMV7M_EXCP_NMI 2
|
#define ARMV7M_EXCP_NMI 2
|
||||||
@ -1356,9 +1357,27 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx,
|
|||||||
uint32_t cur_el, bool secure);
|
uint32_t cur_el, bool secure);
|
||||||
|
|
||||||
/* Interface between CPU and Interrupt controller. */
|
/* Interface between CPU and Interrupt controller. */
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
bool armv7m_nvic_can_take_pending_exception(void *opaque);
|
||||||
|
#else
|
||||||
|
static inline bool armv7m_nvic_can_take_pending_exception(void *opaque)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
void armv7m_nvic_set_pending(void *opaque, int irq);
|
void armv7m_nvic_set_pending(void *opaque, int irq);
|
||||||
int armv7m_nvic_acknowledge_irq(void *opaque);
|
void armv7m_nvic_acknowledge_irq(void *opaque);
|
||||||
void armv7m_nvic_complete_irq(void *opaque, int irq);
|
/**
|
||||||
|
* armv7m_nvic_complete_irq: complete specified interrupt or exception
|
||||||
|
* @opaque: the NVIC
|
||||||
|
* @irq: the exception number to complete
|
||||||
|
*
|
||||||
|
* Returns: -1 if the irq was not active
|
||||||
|
* 1 if completing this irq brought us back to base (no active irqs)
|
||||||
|
* 0 if there is still an irq active after this one was completed
|
||||||
|
* (Ignoring -1, this is the same as the RETTOBASE value before completion.)
|
||||||
|
*/
|
||||||
|
int armv7m_nvic_complete_irq(void *opaque, int irq);
|
||||||
|
|
||||||
/* Interface for defining coprocessor registers.
|
/* Interface for defining coprocessor registers.
|
||||||
* Registers are defined in tables of arm_cp_reginfo structs
|
* Registers are defined in tables of arm_cp_reginfo structs
|
||||||
|
@ -6002,22 +6002,165 @@ static void switch_v7m_sp(CPUARMState *env, bool new_spsel)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_v7m_exception_exit(CPUARMState *env)
|
static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
|
||||||
{
|
{
|
||||||
|
CPUState *cs = CPU(cpu);
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
MemTxResult result;
|
||||||
|
hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4;
|
||||||
|
uint32_t addr;
|
||||||
|
|
||||||
|
addr = address_space_ldl(cs->as, vec,
|
||||||
|
MEMTXATTRS_UNSPECIFIED, &result);
|
||||||
|
if (result != MEMTX_OK) {
|
||||||
|
/* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
|
||||||
|
* which would then be immediately followed by our failing to load
|
||||||
|
* the entry vector for that HardFault, which is a Lockup case.
|
||||||
|
* Since we don't model Lockup, we just report this guest error
|
||||||
|
* via cpu_abort().
|
||||||
|
*/
|
||||||
|
cpu_abort(cs, "Failed to read from exception vector table "
|
||||||
|
"entry %08x\n", (unsigned)vec);
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr)
|
||||||
|
{
|
||||||
|
/* Do the "take the exception" parts of exception entry,
|
||||||
|
* but not the pushing of state to the stack. This is
|
||||||
|
* similar to the pseudocode ExceptionTaken() function.
|
||||||
|
*/
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
uint32_t addr;
|
||||||
|
|
||||||
|
armv7m_nvic_acknowledge_irq(env->nvic);
|
||||||
|
switch_v7m_sp(env, 0);
|
||||||
|
/* Clear IT bits */
|
||||||
|
env->condexec_bits = 0;
|
||||||
|
env->regs[14] = lr;
|
||||||
|
addr = arm_v7m_load_vector(cpu);
|
||||||
|
env->regs[15] = addr & 0xfffffffe;
|
||||||
|
env->thumb = addr & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void v7m_push_stack(ARMCPU *cpu)
|
||||||
|
{
|
||||||
|
/* Do the "set up stack frame" part of exception entry,
|
||||||
|
* similar to pseudocode PushStack().
|
||||||
|
*/
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
uint32_t xpsr = xpsr_read(env);
|
||||||
|
|
||||||
|
/* Align stack pointer if the guest wants that */
|
||||||
|
if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) {
|
||||||
|
env->regs[13] -= 4;
|
||||||
|
xpsr |= 0x200;
|
||||||
|
}
|
||||||
|
/* Switch to the handler mode. */
|
||||||
|
v7m_push(env, xpsr);
|
||||||
|
v7m_push(env, env->regs[15]);
|
||||||
|
v7m_push(env, env->regs[14]);
|
||||||
|
v7m_push(env, env->regs[12]);
|
||||||
|
v7m_push(env, env->regs[3]);
|
||||||
|
v7m_push(env, env->regs[2]);
|
||||||
|
v7m_push(env, env->regs[1]);
|
||||||
|
v7m_push(env, env->regs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_v7m_exception_exit(ARMCPU *cpu)
|
||||||
|
{
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t xpsr;
|
uint32_t xpsr;
|
||||||
|
bool ufault = false;
|
||||||
|
bool return_to_sp_process = false;
|
||||||
|
bool return_to_handler = false;
|
||||||
|
bool rettobase = false;
|
||||||
|
|
||||||
|
/* We can only get here from an EXCP_EXCEPTION_EXIT, and
|
||||||
|
* arm_v7m_do_unassigned_access() enforces the architectural rule
|
||||||
|
* that jumps to magic addresses don't have magic behaviour unless
|
||||||
|
* we're in Handler mode (compare pseudocode BXWritePC()).
|
||||||
|
*/
|
||||||
|
assert(env->v7m.exception != 0);
|
||||||
|
|
||||||
|
/* In the spec pseudocode ExceptionReturn() is called directly
|
||||||
|
* from BXWritePC() and gets the full target PC value including
|
||||||
|
* bit zero. In QEMU's implementation we treat it as a normal
|
||||||
|
* jump-to-register (which is then caught later on), and so split
|
||||||
|
* the target value up between env->regs[15] and env->thumb in
|
||||||
|
* gen_bx(). Reconstitute it.
|
||||||
|
*/
|
||||||
type = env->regs[15];
|
type = env->regs[15];
|
||||||
|
if (env->thumb) {
|
||||||
|
type |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "Exception return: magic PC %" PRIx32
|
||||||
|
" previous exception %d\n",
|
||||||
|
type, env->v7m.exception);
|
||||||
|
|
||||||
|
if (extract32(type, 5, 23) != extract32(-1, 5, 23)) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero high bits in exception "
|
||||||
|
"exit PC value 0x%" PRIx32 " are UNPREDICTABLE\n", type);
|
||||||
|
}
|
||||||
|
|
||||||
if (env->v7m.exception != ARMV7M_EXCP_NMI) {
|
if (env->v7m.exception != ARMV7M_EXCP_NMI) {
|
||||||
/* Auto-clear FAULTMASK on return from other than NMI */
|
/* Auto-clear FAULTMASK on return from other than NMI */
|
||||||
env->daif &= ~PSTATE_F;
|
env->daif &= ~PSTATE_F;
|
||||||
}
|
}
|
||||||
if (env->v7m.exception != 0) {
|
|
||||||
armv7m_nvic_complete_irq(env->nvic, env->v7m.exception);
|
switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception)) {
|
||||||
|
case -1:
|
||||||
|
/* attempt to exit an exception that isn't active */
|
||||||
|
ufault = true;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
/* still an irq active now */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* we returned to base exception level, no nesting.
|
||||||
|
* (In the pseudocode this is written using "NestedActivation != 1"
|
||||||
|
* where we have 'rettobase == false'.)
|
||||||
|
*/
|
||||||
|
rettobase = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type & 0xf) {
|
||||||
|
case 1: /* Return to Handler */
|
||||||
|
return_to_handler = true;
|
||||||
|
break;
|
||||||
|
case 13: /* Return to Thread using Process stack */
|
||||||
|
return_to_sp_process = true;
|
||||||
|
/* fall through */
|
||||||
|
case 9: /* Return to Thread using Main stack */
|
||||||
|
if (!rettobase &&
|
||||||
|
!(env->v7m.ccr & R_V7M_CCR_NONBASETHRDENA_MASK)) {
|
||||||
|
ufault = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ufault = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ufault) {
|
||||||
|
/* Bad exception return: instead of popping the exception
|
||||||
|
* stack, directly take a usage fault on the current stack.
|
||||||
|
*/
|
||||||
|
env->v7m.cfsr |= R_V7M_CFSR_INVPC_MASK;
|
||||||
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
|
||||||
|
v7m_exception_taken(cpu, type | 0xf0000000);
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
|
||||||
|
"stackframe: failed exception return integrity check\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch to the target stack. */
|
/* Switch to the target stack. */
|
||||||
switch_v7m_sp(env, (type & 4) != 0);
|
switch_v7m_sp(env, return_to_sp_process);
|
||||||
/* Pop registers. */
|
/* Pop registers. */
|
||||||
env->regs[0] = v7m_pop(env);
|
env->regs[0] = v7m_pop(env);
|
||||||
env->regs[1] = v7m_pop(env);
|
env->regs[1] = v7m_pop(env);
|
||||||
@ -6041,11 +6184,24 @@ static void do_v7m_exception_exit(CPUARMState *env)
|
|||||||
/* Undo stack alignment. */
|
/* Undo stack alignment. */
|
||||||
if (xpsr & 0x200)
|
if (xpsr & 0x200)
|
||||||
env->regs[13] |= 4;
|
env->regs[13] |= 4;
|
||||||
/* ??? The exception return type specifies Thread/Handler mode. However
|
|
||||||
this is also implied by the xPSR value. Not sure what to do
|
/* The restored xPSR exception field will be zero if we're
|
||||||
if there is a mismatch. */
|
* resuming in Thread mode. If that doesn't match what the
|
||||||
/* ??? Likewise for mismatches between the CONTROL register and the stack
|
* exception return type specified then this is a UsageFault.
|
||||||
pointer. */
|
*/
|
||||||
|
if (return_to_handler == (env->v7m.exception == 0)) {
|
||||||
|
/* Take an INVPC UsageFault by pushing the stack again. */
|
||||||
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
|
||||||
|
env->v7m.cfsr |= R_V7M_CFSR_INVPC_MASK;
|
||||||
|
v7m_push_stack(cpu);
|
||||||
|
v7m_exception_taken(cpu, type | 0xf0000000);
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: "
|
||||||
|
"failed exception return integrity check\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, we have a successful exception exit. */
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...successful exception return\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void arm_log_exception(int idx)
|
static void arm_log_exception(int idx)
|
||||||
@ -6063,37 +6219,11 @@ static void arm_log_exception(int idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
|
|
||||||
|
|
||||||
{
|
|
||||||
CPUState *cs = CPU(cpu);
|
|
||||||
CPUARMState *env = &cpu->env;
|
|
||||||
MemTxResult result;
|
|
||||||
hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4;
|
|
||||||
uint32_t addr;
|
|
||||||
|
|
||||||
addr = address_space_ldl(cs->as, vec,
|
|
||||||
MEMTXATTRS_UNSPECIFIED, &result);
|
|
||||||
if (result != MEMTX_OK) {
|
|
||||||
/* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
|
|
||||||
* which would then be immediately followed by our failing to load
|
|
||||||
* the entry vector for that HardFault, which is a Lockup case.
|
|
||||||
* Since we don't model Lockup, we just report this guest error
|
|
||||||
* via cpu_abort().
|
|
||||||
*/
|
|
||||||
cpu_abort(cs, "Failed to read from exception vector table "
|
|
||||||
"entry %08x\n", (unsigned)vec);
|
|
||||||
}
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
||||||
{
|
{
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
uint32_t xpsr = xpsr_read(env);
|
|
||||||
uint32_t lr;
|
uint32_t lr;
|
||||||
uint32_t addr;
|
|
||||||
|
|
||||||
arm_log_exception(cs->exception_index);
|
arm_log_exception(cs->exception_index);
|
||||||
|
|
||||||
@ -6106,28 +6236,30 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
|||||||
|
|
||||||
/* For exceptions we just mark as pending on the NVIC, and let that
|
/* For exceptions we just mark as pending on the NVIC, and let that
|
||||||
handle it. */
|
handle it. */
|
||||||
/* TODO: Need to escalate if the current priority is higher than the
|
|
||||||
one we're raising. */
|
|
||||||
switch (cs->exception_index) {
|
switch (cs->exception_index) {
|
||||||
case EXCP_UDEF:
|
case EXCP_UDEF:
|
||||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
|
||||||
env->v7m.cfsr |= R_V7M_CFSR_UNDEFINSTR_MASK;
|
env->v7m.cfsr |= R_V7M_CFSR_UNDEFINSTR_MASK;
|
||||||
return;
|
break;
|
||||||
case EXCP_NOCP:
|
case EXCP_NOCP:
|
||||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
|
||||||
env->v7m.cfsr |= R_V7M_CFSR_NOCP_MASK;
|
env->v7m.cfsr |= R_V7M_CFSR_NOCP_MASK;
|
||||||
return;
|
break;
|
||||||
|
case EXCP_INVSTATE:
|
||||||
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
|
||||||
|
env->v7m.cfsr |= R_V7M_CFSR_INVSTATE_MASK;
|
||||||
|
break;
|
||||||
case EXCP_SWI:
|
case EXCP_SWI:
|
||||||
/* The PC already points to the next instruction. */
|
/* The PC already points to the next instruction. */
|
||||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC);
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC);
|
||||||
return;
|
break;
|
||||||
case EXCP_PREFETCH_ABORT:
|
case EXCP_PREFETCH_ABORT:
|
||||||
case EXCP_DATA_ABORT:
|
case EXCP_DATA_ABORT:
|
||||||
/* TODO: if we implemented the MPU registers, this is where we
|
/* TODO: if we implemented the MPU registers, this is where we
|
||||||
* should set the MMFAR, etc from exception.fsr and exception.vaddress.
|
* should set the MMFAR, etc from exception.fsr and exception.vaddress.
|
||||||
*/
|
*/
|
||||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
|
||||||
return;
|
break;
|
||||||
case EXCP_BKPT:
|
case EXCP_BKPT:
|
||||||
if (semihosting_enabled()) {
|
if (semihosting_enabled()) {
|
||||||
int nr;
|
int nr;
|
||||||
@ -6142,39 +6274,20 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG);
|
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG);
|
||||||
return;
|
break;
|
||||||
case EXCP_IRQ:
|
case EXCP_IRQ:
|
||||||
env->v7m.exception = armv7m_nvic_acknowledge_irq(env->nvic);
|
|
||||||
break;
|
break;
|
||||||
case EXCP_EXCEPTION_EXIT:
|
case EXCP_EXCEPTION_EXIT:
|
||||||
do_v7m_exception_exit(env);
|
do_v7m_exception_exit(cpu);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
|
cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
|
||||||
return; /* Never happens. Keep compiler happy. */
|
return; /* Never happens. Keep compiler happy. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Align stack pointer if the guest wants that */
|
v7m_push_stack(cpu);
|
||||||
if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) {
|
v7m_exception_taken(cpu, lr);
|
||||||
env->regs[13] -= 4;
|
qemu_log_mask(CPU_LOG_INT, "... as %d\n", env->v7m.exception);
|
||||||
xpsr |= 0x200;
|
|
||||||
}
|
|
||||||
/* Switch to the handler mode. */
|
|
||||||
v7m_push(env, xpsr);
|
|
||||||
v7m_push(env, env->regs[15]);
|
|
||||||
v7m_push(env, env->regs[14]);
|
|
||||||
v7m_push(env, env->regs[12]);
|
|
||||||
v7m_push(env, env->regs[3]);
|
|
||||||
v7m_push(env, env->regs[2]);
|
|
||||||
v7m_push(env, env->regs[1]);
|
|
||||||
v7m_push(env, env->regs[0]);
|
|
||||||
switch_v7m_sp(env, 0);
|
|
||||||
/* Clear IT bits */
|
|
||||||
env->condexec_bits = 0;
|
|
||||||
env->regs[14] = lr;
|
|
||||||
addr = arm_v7m_load_vector(cpu);
|
|
||||||
env->regs[15] = addr & 0xfffffffe;
|
|
||||||
env->thumb = addr & 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function used to synchronize QEMU's AArch64 register set with AArch32
|
/* Function used to synchronize QEMU's AArch64 register set with AArch32
|
||||||
|
@ -10933,6 +10933,10 @@ static void disas_crypto_aes(DisasContext *s, uint32_t insn)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fp_access_check(s)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Note that we convert the Vx register indexes into the
|
/* Note that we convert the Vx register indexes into the
|
||||||
* index within the vfp.regs[] array, so we can share the
|
* index within the vfp.regs[] array, so we can share the
|
||||||
* helper with the AArch32 instructions.
|
* helper with the AArch32 instructions.
|
||||||
@ -10997,6 +11001,10 @@ static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fp_access_check(s)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
tcg_rd_regno = tcg_const_i32(rd << 1);
|
tcg_rd_regno = tcg_const_i32(rd << 1);
|
||||||
tcg_rn_regno = tcg_const_i32(rn << 1);
|
tcg_rn_regno = tcg_const_i32(rn << 1);
|
||||||
tcg_rm_regno = tcg_const_i32(rm << 1);
|
tcg_rm_regno = tcg_const_i32(rm << 1);
|
||||||
@ -11060,6 +11068,10 @@ static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fp_access_check(s)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
tcg_rd_regno = tcg_const_i32(rd << 1);
|
tcg_rd_regno = tcg_const_i32(rd << 1);
|
||||||
tcg_rn_regno = tcg_const_i32(rn << 1);
|
tcg_rn_regno = tcg_const_i32(rn << 1);
|
||||||
|
|
||||||
|
@ -7990,9 +7990,13 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
|||||||
TCGv_i32 addr;
|
TCGv_i32 addr;
|
||||||
TCGv_i64 tmp64;
|
TCGv_i64 tmp64;
|
||||||
|
|
||||||
/* M variants do not implement ARM mode. */
|
/* M variants do not implement ARM mode; this must raise the INVSTATE
|
||||||
|
* UsageFault exception.
|
||||||
|
*/
|
||||||
if (arm_dc_feature(s, ARM_FEATURE_M)) {
|
if (arm_dc_feature(s, ARM_FEATURE_M)) {
|
||||||
goto illegal_op;
|
gen_exception_insn(s, 4, EXCP_INVSTATE, syn_uncategorized(),
|
||||||
|
default_exception_el(s));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
cond = insn >> 28;
|
cond = insn >> 28;
|
||||||
if (cond == 0xf){
|
if (cond == 0xf){
|
||||||
|
Loading…
Reference in New Issue
Block a user