mirror of https://github.com/xemu-project/xemu.git
target-arm queue:
* connect SPI devices in Xilinx Zynq platforms * multiple-address-space support * use multiple-address-space support for ARM TrustZone * arm_gic: return correct ID registers for 11MPCore/v1/v2 GICs * various fixes for (currently disabled) AArch64 EL2 and EL3 support * add 'always-on' property to the virt board timer DT entry -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJWoPFAAAoJEDwlJe0UNgzeKKAP/jpTj6yfwXxkbU3BT+SmiJDe J9vSUkPnVLABxy0TGtnGTvgVZrgJfhKcEkk4i/uswsV7U8Vxxpa5XDir0ZNPo7VG 3KqQBM3ZKZgMD+QuxMIr76ar9+FukfSFI/9yfmZZOthON9P3Tu9BtXAbjLuezdXt jQHI5FDsNhgxvXSRa0qY8fTayKRBCirHzzkpLsaaS2Frj9HUUnrHQtOjEWAyb10N QxQkuFLzqIbzTynKtVkrFbQknuIFF/h6tMe5Oj/J07A/nl1GmLJQtvmy4M7sFG4Z uAqJNxtO36CYGg/RdRYNmW89k5iRqAMHqJNlNYLlAz9q2cB59MPXrTWqgUOQq3UI QOOkINqubd62le0QarnZofGp+YJwUj1Uv+URD4kRnOFMjIvuF3rH6S+PCnDPD53a rCrkNZM2YKYMe7CSgqy5cz2rnrnYhl+ubpo/yz5Gs4g+iqcK7BLyY1QK72oZAy0U 9bUNrMFUCFQrJioel34m9kM3QZFOz14kbR4NTaxovUMATimi+qveLdYp9rLi9WMc tpyEDHL3KYbJv/siUtC9da0A1hWe/WlMqvC/6Fm55xC75+ihQEJhoJvxs4auDqQx GafvkFasjpPZ/2ZpgCj+kwZZvqjU0mzncj+FKF6fflAfxJmdikHIXUQ+L83eYAgd QQC+zYjyWa343GwdO3yl =IB51 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20160121' into staging target-arm queue: * connect SPI devices in Xilinx Zynq platforms * multiple-address-space support * use multiple-address-space support for ARM TrustZone * arm_gic: return correct ID registers for 11MPCore/v1/v2 GICs * various fixes for (currently disabled) AArch64 EL2 and EL3 support * add 'always-on' property to the virt board timer DT entry # gpg: Signature made Thu 21 Jan 2016 14:54:56 GMT using RSA key ID 14360CDE # 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>" * remotes/pmaydell/tags/pull-target-arm-20160121: (36 commits) target-arm: Implement FPEXC32_EL2 system register target-arm: ignore ELR_ELx[1] for exception return to 32-bit ARM mode target-arm: Implement remaining illegal return event checks target-arm: Handle exception return from AArch64 to non-EL0 AArch32 target-arm: Fix wrong AArch64 entry offset for EL2/EL3 target target-arm: Pull semihosting handling out to arm_cpu_do_interrupt() target-arm: Use a single entry point for AArch64 and AArch32 exceptions target-arm: Move aarch64_cpu_do_interrupt() to helper.c target-arm: Properly support EL2 and EL3 in arm_el_is_aa64() arm_gic: Update ID registers based on revision hw/arm/virt: Add always-on property to the virt board timer hw/arm/virt: add secure memory region and UART hw/arm/virt: Wire up memory region to CPUs explicitly target-arm: Support multiple address spaces in page table walks target-arm: Implement cpu_get_phys_page_attrs_debug target-arm: Implement asidx_from_attrs target-arm: Add QOM property for Secure memory region qom/cpu: Add MemoryRegion property memory: Add address_space_init_shareable() exec.c: Use correct AddressSpace in watch_mem_read and watch_mem_write ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1a4f446f81
13
cpus.c
13
cpus.c
|
@ -1310,8 +1310,6 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
|
||||||
static QemuCond *tcg_halt_cond;
|
static QemuCond *tcg_halt_cond;
|
||||||
static QemuThread *tcg_cpu_thread;
|
static QemuThread *tcg_cpu_thread;
|
||||||
|
|
||||||
tcg_cpu_address_space_init(cpu, cpu->as);
|
|
||||||
|
|
||||||
/* share a single thread for all cpus with TCG */
|
/* share a single thread for all cpus with TCG */
|
||||||
if (!tcg_cpu_thread) {
|
if (!tcg_cpu_thread) {
|
||||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||||
|
@ -1372,6 +1370,17 @@ void qemu_init_vcpu(CPUState *cpu)
|
||||||
cpu->nr_cores = smp_cores;
|
cpu->nr_cores = smp_cores;
|
||||||
cpu->nr_threads = smp_threads;
|
cpu->nr_threads = smp_threads;
|
||||||
cpu->stopped = true;
|
cpu->stopped = true;
|
||||||
|
|
||||||
|
if (!cpu->as) {
|
||||||
|
/* If the target cpu hasn't set up any address spaces itself,
|
||||||
|
* give it the default one.
|
||||||
|
*/
|
||||||
|
AddressSpace *as = address_space_init_shareable(cpu->memory,
|
||||||
|
"cpu-memory");
|
||||||
|
cpu->num_ases = 1;
|
||||||
|
cpu_address_space_init(cpu, as, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (kvm_enabled()) {
|
if (kvm_enabled()) {
|
||||||
qemu_kvm_start_vcpu(cpu);
|
qemu_kvm_start_vcpu(cpu);
|
||||||
} else if (tcg_enabled()) {
|
} else if (tcg_enabled()) {
|
||||||
|
|
9
cputlb.c
9
cputlb.c
|
@ -356,6 +356,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||||
CPUTLBEntry *te;
|
CPUTLBEntry *te;
|
||||||
hwaddr iotlb, xlat, sz;
|
hwaddr iotlb, xlat, sz;
|
||||||
unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE;
|
unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE;
|
||||||
|
int asidx = cpu_asidx_from_attrs(cpu, attrs);
|
||||||
|
|
||||||
assert(size >= TARGET_PAGE_SIZE);
|
assert(size >= TARGET_PAGE_SIZE);
|
||||||
if (size != TARGET_PAGE_SIZE) {
|
if (size != TARGET_PAGE_SIZE) {
|
||||||
|
@ -363,7 +364,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
sz = size;
|
sz = size;
|
||||||
section = address_space_translate_for_iotlb(cpu, paddr, &xlat, &sz);
|
section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz);
|
||||||
assert(sz >= TARGET_PAGE_SIZE);
|
assert(sz >= TARGET_PAGE_SIZE);
|
||||||
|
|
||||||
#if defined(DEBUG_TLB)
|
#if defined(DEBUG_TLB)
|
||||||
|
@ -448,6 +449,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||||
void *p;
|
void *p;
|
||||||
MemoryRegion *mr;
|
MemoryRegion *mr;
|
||||||
CPUState *cpu = ENV_GET_CPU(env1);
|
CPUState *cpu = ENV_GET_CPU(env1);
|
||||||
|
CPUIOTLBEntry *iotlbentry;
|
||||||
|
|
||||||
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||||
mmu_idx = cpu_mmu_index(env1, true);
|
mmu_idx = cpu_mmu_index(env1, true);
|
||||||
|
@ -455,8 +457,9 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
|
||||||
(addr & TARGET_PAGE_MASK))) {
|
(addr & TARGET_PAGE_MASK))) {
|
||||||
cpu_ldub_code(env1, addr);
|
cpu_ldub_code(env1, addr);
|
||||||
}
|
}
|
||||||
pd = env1->iotlb[mmu_idx][page_index].addr & ~TARGET_PAGE_MASK;
|
iotlbentry = &env1->iotlb[mmu_idx][page_index];
|
||||||
mr = iotlb_to_region(cpu, pd);
|
pd = iotlbentry->addr & ~TARGET_PAGE_MASK;
|
||||||
|
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);
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||||
|
|
||||||
|
|
103
exec.c
103
exec.c
|
@ -431,12 +431,13 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
||||||
|
|
||||||
/* Called from RCU critical section */
|
/* Called from RCU critical section */
|
||||||
MemoryRegionSection *
|
MemoryRegionSection *
|
||||||
address_space_translate_for_iotlb(CPUState *cpu, hwaddr addr,
|
address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
|
||||||
hwaddr *xlat, hwaddr *plen)
|
hwaddr *xlat, hwaddr *plen)
|
||||||
{
|
{
|
||||||
MemoryRegionSection *section;
|
MemoryRegionSection *section;
|
||||||
section = address_space_translate_internal(cpu->cpu_ases[0].memory_dispatch,
|
AddressSpaceDispatch *d = cpu->cpu_ases[asidx].memory_dispatch;
|
||||||
addr, xlat, plen, false);
|
|
||||||
|
section = address_space_translate_internal(d, addr, xlat, plen, false);
|
||||||
|
|
||||||
assert(!section->mr->iommu_ops);
|
assert(!section->mr->iommu_ops);
|
||||||
return section;
|
return section;
|
||||||
|
@ -536,21 +537,38 @@ CPUState *qemu_get_cpu(int index)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
void tcg_cpu_address_space_init(CPUState *cpu, AddressSpace *as)
|
void cpu_address_space_init(CPUState *cpu, AddressSpace *as, int asidx)
|
||||||
{
|
{
|
||||||
/* We only support one address space per cpu at the moment. */
|
CPUAddressSpace *newas;
|
||||||
assert(cpu->as == as);
|
|
||||||
|
|
||||||
if (cpu->cpu_ases) {
|
/* Target code should have set num_ases before calling us */
|
||||||
/* We've already registered the listener for our only AS */
|
assert(asidx < cpu->num_ases);
|
||||||
return;
|
|
||||||
|
if (asidx == 0) {
|
||||||
|
/* address space 0 gets the convenience alias */
|
||||||
|
cpu->as = as;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu->cpu_ases = g_new0(CPUAddressSpace, 1);
|
/* KVM cannot currently support multiple address spaces. */
|
||||||
cpu->cpu_ases[0].cpu = cpu;
|
assert(asidx == 0 || !kvm_enabled());
|
||||||
cpu->cpu_ases[0].as = as;
|
|
||||||
cpu->cpu_ases[0].tcg_as_listener.commit = tcg_commit;
|
if (!cpu->cpu_ases) {
|
||||||
memory_listener_register(&cpu->cpu_ases[0].tcg_as_listener, as);
|
cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases);
|
||||||
|
}
|
||||||
|
|
||||||
|
newas = &cpu->cpu_ases[asidx];
|
||||||
|
newas->cpu = cpu;
|
||||||
|
newas->as = as;
|
||||||
|
if (tcg_enabled()) {
|
||||||
|
newas->tcg_as_listener.commit = tcg_commit;
|
||||||
|
memory_listener_register(&newas->tcg_as_listener, as);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx)
|
||||||
|
{
|
||||||
|
/* Return the AddressSpace corresponding to the specified index */
|
||||||
|
return cpu->cpu_ases[asidx].as;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -605,9 +623,25 @@ void cpu_exec_init(CPUState *cpu, Error **errp)
|
||||||
int cpu_index;
|
int cpu_index;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
cpu->as = NULL;
|
||||||
|
cpu->num_ases = 0;
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
cpu->as = &address_space_memory;
|
|
||||||
cpu->thread_id = qemu_get_thread_id();
|
cpu->thread_id = qemu_get_thread_id();
|
||||||
|
|
||||||
|
/* This is a softmmu CPU object, so create a property for it
|
||||||
|
* so users can wire up its memory. (This can't go in qom/cpu.c
|
||||||
|
* because that file is compiled only once for both user-mode
|
||||||
|
* and system builds.) The default if no link is set up is to use
|
||||||
|
* the system address space.
|
||||||
|
*/
|
||||||
|
object_property_add_link(OBJECT(cpu), "memory", TYPE_MEMORY_REGION,
|
||||||
|
(Object **)&cpu->memory,
|
||||||
|
qdev_prop_allow_set_link_before_realize,
|
||||||
|
OBJ_PROP_LINK_UNREF_ON_RELEASE,
|
||||||
|
&error_abort);
|
||||||
|
cpu->memory = system_memory;
|
||||||
|
object_ref(OBJECT(cpu->memory));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_USER_ONLY)
|
#if defined(CONFIG_USER_ONLY)
|
||||||
|
@ -647,9 +681,11 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
||||||
#else
|
#else
|
||||||
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
||||||
{
|
{
|
||||||
hwaddr phys = cpu_get_phys_page_debug(cpu, pc);
|
MemTxAttrs attrs;
|
||||||
|
hwaddr phys = cpu_get_phys_page_attrs_debug(cpu, pc, &attrs);
|
||||||
|
int asidx = cpu_asidx_from_attrs(cpu, attrs);
|
||||||
if (phys != -1) {
|
if (phys != -1) {
|
||||||
tb_invalidate_phys_addr(cpu->as,
|
tb_invalidate_phys_addr(cpu->cpu_ases[asidx].as,
|
||||||
phys | (pc & ~TARGET_PAGE_MASK));
|
phys | (pc & ~TARGET_PAGE_MASK));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2034,17 +2070,19 @@ static MemTxResult watch_mem_read(void *opaque, hwaddr addr, uint64_t *pdata,
|
||||||
{
|
{
|
||||||
MemTxResult res;
|
MemTxResult res;
|
||||||
uint64_t data;
|
uint64_t data;
|
||||||
|
int asidx = cpu_asidx_from_attrs(current_cpu, attrs);
|
||||||
|
AddressSpace *as = current_cpu->cpu_ases[asidx].as;
|
||||||
|
|
||||||
check_watchpoint(addr & ~TARGET_PAGE_MASK, size, attrs, BP_MEM_READ);
|
check_watchpoint(addr & ~TARGET_PAGE_MASK, size, attrs, BP_MEM_READ);
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
data = address_space_ldub(&address_space_memory, addr, attrs, &res);
|
data = address_space_ldub(as, addr, attrs, &res);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
data = address_space_lduw(&address_space_memory, addr, attrs, &res);
|
data = address_space_lduw(as, addr, attrs, &res);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
data = address_space_ldl(&address_space_memory, addr, attrs, &res);
|
data = address_space_ldl(as, addr, attrs, &res);
|
||||||
break;
|
break;
|
||||||
default: abort();
|
default: abort();
|
||||||
}
|
}
|
||||||
|
@ -2057,17 +2095,19 @@ static MemTxResult watch_mem_write(void *opaque, hwaddr addr,
|
||||||
MemTxAttrs attrs)
|
MemTxAttrs attrs)
|
||||||
{
|
{
|
||||||
MemTxResult res;
|
MemTxResult res;
|
||||||
|
int asidx = cpu_asidx_from_attrs(current_cpu, attrs);
|
||||||
|
AddressSpace *as = current_cpu->cpu_ases[asidx].as;
|
||||||
|
|
||||||
check_watchpoint(addr & ~TARGET_PAGE_MASK, size, attrs, BP_MEM_WRITE);
|
check_watchpoint(addr & ~TARGET_PAGE_MASK, size, attrs, BP_MEM_WRITE);
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
address_space_stb(&address_space_memory, addr, val, attrs, &res);
|
address_space_stb(as, addr, val, attrs, &res);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
address_space_stw(&address_space_memory, addr, val, attrs, &res);
|
address_space_stw(as, addr, val, attrs, &res);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
address_space_stl(&address_space_memory, addr, val, attrs, &res);
|
address_space_stl(as, addr, val, attrs, &res);
|
||||||
break;
|
break;
|
||||||
default: abort();
|
default: abort();
|
||||||
}
|
}
|
||||||
|
@ -2224,9 +2264,10 @@ static uint16_t dummy_section(PhysPageMap *map, AddressSpace *as,
|
||||||
return phys_section_add(map, §ion);
|
return phys_section_add(map, §ion);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index)
|
MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index, MemTxAttrs attrs)
|
||||||
{
|
{
|
||||||
CPUAddressSpace *cpuas = &cpu->cpu_ases[0];
|
int asidx = cpu_asidx_from_attrs(cpu, attrs);
|
||||||
|
CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx];
|
||||||
AddressSpaceDispatch *d = atomic_rcu_read(&cpuas->memory_dispatch);
|
AddressSpaceDispatch *d = atomic_rcu_read(&cpuas->memory_dispatch);
|
||||||
MemoryRegionSection *sections = d->map.sections;
|
MemoryRegionSection *sections = d->map.sections;
|
||||||
|
|
||||||
|
@ -3565,8 +3606,12 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
||||||
target_ulong page;
|
target_ulong page;
|
||||||
|
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
|
int asidx;
|
||||||
|
MemTxAttrs attrs;
|
||||||
|
|
||||||
page = addr & TARGET_PAGE_MASK;
|
page = addr & TARGET_PAGE_MASK;
|
||||||
phys_addr = cpu_get_phys_page_debug(cpu, page);
|
phys_addr = cpu_get_phys_page_attrs_debug(cpu, page, &attrs);
|
||||||
|
asidx = cpu_asidx_from_attrs(cpu, attrs);
|
||||||
/* if no physical page mapped, return an error */
|
/* if no physical page mapped, return an error */
|
||||||
if (phys_addr == -1)
|
if (phys_addr == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -3575,9 +3620,11 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
||||||
l = len;
|
l = len;
|
||||||
phys_addr += (addr & ~TARGET_PAGE_MASK);
|
phys_addr += (addr & ~TARGET_PAGE_MASK);
|
||||||
if (is_write) {
|
if (is_write) {
|
||||||
cpu_physical_memory_write_rom(cpu->as, phys_addr, buf, l);
|
cpu_physical_memory_write_rom(cpu->cpu_ases[asidx].as,
|
||||||
|
phys_addr, buf, l);
|
||||||
} else {
|
} else {
|
||||||
address_space_rw(cpu->as, phys_addr, MEMTXATTRS_UNSPECIFIED,
|
address_space_rw(cpu->cpu_ases[asidx].as, phys_addr,
|
||||||
|
MEMTXATTRS_UNSPECIFIED,
|
||||||
buf, l, 0);
|
buf, l, 0);
|
||||||
}
|
}
|
||||||
len -= l;
|
len -= l;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "hw/char/serial.h"
|
#include "hw/char/serial.h"
|
||||||
#include "hw/i2c/i2c.h"
|
#include "hw/i2c/i2c.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "sysemu/char.h"
|
#include "sysemu/char.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "sysemu/blockdev.h"
|
#include "sysemu/blockdev.h"
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "hw/pcmcia.h"
|
#include "hw/pcmcia.h"
|
||||||
#include "hw/i2c/i2c.h"
|
#include "hw/i2c/i2c.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "hw/block/flash.h"
|
#include "hw/block/flash.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "hw/devices.h"
|
#include "hw/devices.h"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "hw/arm/arm.h"
|
#include "hw/arm/arm.h"
|
||||||
#include "hw/devices.h"
|
#include "hw/devices.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
#include "hw/arm/arm.h"
|
#include "hw/arm/arm.h"
|
||||||
#include "sysemu/char.h"
|
#include "sysemu/char.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
|
|
||||||
//#define DEBUG
|
//#define DEBUG
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "hw/pcmcia.h"
|
#include "hw/pcmcia.h"
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "hw/i2c/i2c.h"
|
#include "hw/i2c/i2c.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
|
|
|
@ -123,6 +123,7 @@ static const MemMapEntry a15memmap[] = {
|
||||||
[VIRT_RTC] = { 0x09010000, 0x00001000 },
|
[VIRT_RTC] = { 0x09010000, 0x00001000 },
|
||||||
[VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
|
[VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
|
||||||
[VIRT_GPIO] = { 0x09030000, 0x00001000 },
|
[VIRT_GPIO] = { 0x09030000, 0x00001000 },
|
||||||
|
[VIRT_SECURE_UART] = { 0x09040000, 0x00001000 },
|
||||||
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
||||||
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
||||||
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
||||||
|
@ -139,6 +140,7 @@ static const int a15irqmap[] = {
|
||||||
[VIRT_RTC] = 2,
|
[VIRT_RTC] = 2,
|
||||||
[VIRT_PCIE] = 3, /* ... to 6 */
|
[VIRT_PCIE] = 3, /* ... to 6 */
|
||||||
[VIRT_GPIO] = 7,
|
[VIRT_GPIO] = 7,
|
||||||
|
[VIRT_SECURE_UART] = 8,
|
||||||
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
|
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
|
||||||
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
|
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
|
||||||
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
|
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
|
||||||
|
@ -291,6 +293,7 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi, int gictype)
|
||||||
qemu_fdt_setprop_string(vbi->fdt, "/timer", "compatible",
|
qemu_fdt_setprop_string(vbi->fdt, "/timer", "compatible",
|
||||||
"arm,armv7-timer");
|
"arm,armv7-timer");
|
||||||
}
|
}
|
||||||
|
qemu_fdt_setprop(vbi->fdt, "/timer", "always-on", NULL, 0);
|
||||||
qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts",
|
qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts",
|
||||||
GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_S_EL1_IRQ, irqflags,
|
GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_S_EL1_IRQ, irqflags,
|
||||||
GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL1_IRQ, irqflags,
|
GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL1_IRQ, irqflags,
|
||||||
|
@ -489,16 +492,22 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
|
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart,
|
||||||
|
MemoryRegion *mem)
|
||||||
{
|
{
|
||||||
char *nodename;
|
char *nodename;
|
||||||
hwaddr base = vbi->memmap[VIRT_UART].base;
|
hwaddr base = vbi->memmap[uart].base;
|
||||||
hwaddr size = vbi->memmap[VIRT_UART].size;
|
hwaddr size = vbi->memmap[uart].size;
|
||||||
int irq = vbi->irqmap[VIRT_UART];
|
int irq = vbi->irqmap[uart];
|
||||||
const char compat[] = "arm,pl011\0arm,primecell";
|
const char compat[] = "arm,pl011\0arm,primecell";
|
||||||
const char clocknames[] = "uartclk\0apb_pclk";
|
const char clocknames[] = "uartclk\0apb_pclk";
|
||||||
|
DeviceState *dev = qdev_create(NULL, "pl011");
|
||||||
|
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
||||||
|
|
||||||
sysbus_create_simple("pl011", base, pic[irq]);
|
qdev_init_nofail(dev);
|
||||||
|
memory_region_add_subregion(mem, base,
|
||||||
|
sysbus_mmio_get_region(s, 0));
|
||||||
|
sysbus_connect_irq(s, 0, pic[irq]);
|
||||||
|
|
||||||
nodename = g_strdup_printf("/pl011@%" PRIx64, base);
|
nodename = g_strdup_printf("/pl011@%" PRIx64, base);
|
||||||
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
|
@ -515,7 +524,14 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
|
||||||
qemu_fdt_setprop(vbi->fdt, nodename, "clock-names",
|
qemu_fdt_setprop(vbi->fdt, nodename, "clock-names",
|
||||||
clocknames, sizeof(clocknames));
|
clocknames, sizeof(clocknames));
|
||||||
|
|
||||||
|
if (uart == VIRT_UART) {
|
||||||
qemu_fdt_setprop_string(vbi->fdt, "/chosen", "stdout-path", nodename);
|
qemu_fdt_setprop_string(vbi->fdt, "/chosen", "stdout-path", nodename);
|
||||||
|
} else {
|
||||||
|
/* Mark as not usable by the normal world */
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled");
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay");
|
||||||
|
}
|
||||||
|
|
||||||
g_free(nodename);
|
g_free(nodename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,6 +1011,7 @@ static void machvirt_init(MachineState *machine)
|
||||||
VirtMachineState *vms = VIRT_MACHINE(machine);
|
VirtMachineState *vms = VIRT_MACHINE(machine);
|
||||||
qemu_irq pic[NUM_IRQS];
|
qemu_irq pic[NUM_IRQS];
|
||||||
MemoryRegion *sysmem = get_system_memory();
|
MemoryRegion *sysmem = get_system_memory();
|
||||||
|
MemoryRegion *secure_sysmem = NULL;
|
||||||
int gic_version = vms->gic_version;
|
int gic_version = vms->gic_version;
|
||||||
int n, max_cpus;
|
int n, max_cpus;
|
||||||
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
||||||
|
@ -1053,6 +1070,23 @@ static void machvirt_init(MachineState *machine)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vms->secure) {
|
||||||
|
if (kvm_enabled()) {
|
||||||
|
error_report("mach-virt: KVM does not support Security extensions");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Secure view of the world is the same as the NonSecure,
|
||||||
|
* but with a few extra devices. Create it as a container region
|
||||||
|
* containing the system memory at low priority; any secure-only
|
||||||
|
* devices go in at higher priority and take precedence.
|
||||||
|
*/
|
||||||
|
secure_sysmem = g_new(MemoryRegion, 1);
|
||||||
|
memory_region_init(secure_sysmem, OBJECT(machine), "secure-memory",
|
||||||
|
UINT64_MAX);
|
||||||
|
memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1);
|
||||||
|
}
|
||||||
|
|
||||||
create_fdt(vbi);
|
create_fdt(vbi);
|
||||||
|
|
||||||
for (n = 0; n < smp_cpus; n++) {
|
for (n = 0; n < smp_cpus; n++) {
|
||||||
|
@ -1093,6 +1127,13 @@ static void machvirt_init(MachineState *machine)
|
||||||
"reset-cbar", &error_abort);
|
"reset-cbar", &error_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_property_set_link(cpuobj, OBJECT(sysmem), "memory",
|
||||||
|
&error_abort);
|
||||||
|
if (vms->secure) {
|
||||||
|
object_property_set_link(cpuobj, OBJECT(secure_sysmem),
|
||||||
|
"secure-memory", &error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
object_property_set_bool(cpuobj, true, "realized", NULL);
|
object_property_set_bool(cpuobj, true, "realized", NULL);
|
||||||
}
|
}
|
||||||
g_strfreev(cpustr);
|
g_strfreev(cpustr);
|
||||||
|
@ -1108,7 +1149,11 @@ static void machvirt_init(MachineState *machine)
|
||||||
|
|
||||||
create_gic(vbi, pic, gic_version, vms->secure);
|
create_gic(vbi, pic, gic_version, vms->secure);
|
||||||
|
|
||||||
create_uart(vbi, pic);
|
create_uart(vbi, pic, VIRT_UART, sysmem);
|
||||||
|
|
||||||
|
if (vms->secure) {
|
||||||
|
create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem);
|
||||||
|
}
|
||||||
|
|
||||||
create_rtc(vbi, pic);
|
create_rtc(vbi, pic);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "hw/loader.h"
|
#include "hw/loader.h"
|
||||||
#include "hw/misc/zynq-xadc.h"
|
#include "hw/misc/zynq-xadc.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
|
||||||
#define NUM_SPI_FLASHES 4
|
#define NUM_SPI_FLASHES 4
|
||||||
|
|
|
@ -31,6 +31,7 @@ static struct arm_boot_info xlnx_ep108_binfo;
|
||||||
static void xlnx_ep108_init(MachineState *machine)
|
static void xlnx_ep108_init(MachineState *machine)
|
||||||
{
|
{
|
||||||
XlnxEP108 *s = g_new0(XlnxEP108, 1);
|
XlnxEP108 *s = g_new0(XlnxEP108, 1);
|
||||||
|
int i;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
uint64_t ram_size = machine->ram_size;
|
uint64_t ram_size = machine->ram_size;
|
||||||
|
|
||||||
|
@ -63,6 +64,21 @@ static void xlnx_ep108_init(MachineState *machine)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) {
|
||||||
|
SSIBus *spi_bus;
|
||||||
|
DeviceState *flash_dev;
|
||||||
|
qemu_irq cs_line;
|
||||||
|
gchar *bus_name = g_strdup_printf("spi%d", i);
|
||||||
|
|
||||||
|
spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name);
|
||||||
|
g_free(bus_name);
|
||||||
|
|
||||||
|
flash_dev = ssi_create_slave(spi_bus, "sst25wf080");
|
||||||
|
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
|
||||||
|
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line);
|
||||||
|
}
|
||||||
|
|
||||||
xlnx_ep108_binfo.ram_size = ram_size;
|
xlnx_ep108_binfo.ram_size = ram_size;
|
||||||
xlnx_ep108_binfo.kernel_filename = machine->kernel_filename;
|
xlnx_ep108_binfo.kernel_filename = machine->kernel_filename;
|
||||||
xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
|
xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
|
||||||
|
|
|
@ -57,6 +57,14 @@ static const int sdhci_intr[XLNX_ZYNQMP_NUM_SDHCI] = {
|
||||||
48, 49,
|
48, 49,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint64_t spi_addr[XLNX_ZYNQMP_NUM_SPIS] = {
|
||||||
|
0xFF040000, 0xFF050000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int spi_intr[XLNX_ZYNQMP_NUM_SPIS] = {
|
||||||
|
19, 20,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct XlnxZynqMPGICRegion {
|
typedef struct XlnxZynqMPGICRegion {
|
||||||
int region_index;
|
int region_index;
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
|
@ -118,6 +126,12 @@ static void xlnx_zynqmp_init(Object *obj)
|
||||||
qdev_set_parent_bus(DEVICE(&s->sdhci[i]),
|
qdev_set_parent_bus(DEVICE(&s->sdhci[i]),
|
||||||
sysbus_get_default());
|
sysbus_get_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) {
|
||||||
|
object_initialize(&s->spi[i], sizeof(s->spi[i]),
|
||||||
|
TYPE_XILINX_SPIPS);
|
||||||
|
qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||||
|
@ -324,6 +338,23 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
|
||||||
gic_spi[sdhci_intr[i]]);
|
gic_spi[sdhci_intr[i]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) {
|
||||||
|
gchar *bus_name;
|
||||||
|
|
||||||
|
object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err);
|
||||||
|
|
||||||
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_addr[i]);
|
||||||
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
|
||||||
|
gic_spi[spi_intr[i]]);
|
||||||
|
|
||||||
|
/* Alias controller SPI bus to the SoC itself */
|
||||||
|
bus_name = g_strdup_printf("spi%d", i);
|
||||||
|
object_property_add_alias(OBJECT(s), bus_name,
|
||||||
|
OBJECT(&s->spi[i]), "spi0",
|
||||||
|
&error_abort);
|
||||||
|
g_free(bus_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Property xlnx_zynqmp_props[] = {
|
static Property xlnx_zynqmp_props[] = {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "hw/arm/arm.h"
|
#include "hw/arm/arm.h"
|
||||||
#include "hw/devices.h"
|
#include "hw/devices.h"
|
||||||
#include "hw/i2c/i2c.h"
|
#include "hw/i2c/i2c.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "hw/block/flash.h"
|
#include "hw/block/flash.h"
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "sysemu/blockdev.h"
|
#include "sysemu/blockdev.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
|
|
||||||
#ifndef M25P80_ERR_DEBUG
|
#ifndef M25P80_ERR_DEBUG
|
||||||
#define M25P80_ERR_DEBUG 0
|
#define M25P80_ERR_DEBUG 0
|
||||||
|
@ -164,6 +164,7 @@ static const FlashPartInfo known_devices[] = {
|
||||||
{ INFO("sst25wf010", 0xbf2502, 0, 64 << 10, 2, ER_4K) },
|
{ INFO("sst25wf010", 0xbf2502, 0, 64 << 10, 2, ER_4K) },
|
||||||
{ INFO("sst25wf020", 0xbf2503, 0, 64 << 10, 4, ER_4K) },
|
{ INFO("sst25wf020", 0xbf2503, 0, 64 << 10, 4, ER_4K) },
|
||||||
{ INFO("sst25wf040", 0xbf2504, 0, 64 << 10, 8, ER_4K) },
|
{ INFO("sst25wf040", 0xbf2504, 0, 64 << 10, 8, ER_4K) },
|
||||||
|
{ INFO("sst25wf080", 0xbf2505, 0, 64 << 10, 16, ER_4K) },
|
||||||
|
|
||||||
/* ST Microelectronics -- newer production may have feature updates */
|
/* ST Microelectronics -- newer production may have feature updates */
|
||||||
{ INFO("m25p05", 0x202010, 0, 32 << 10, 2, 0) },
|
{ INFO("m25p05", 0x202010, 0, 32 << 10, 2, 0) },
|
||||||
|
|
|
@ -580,6 +580,12 @@ void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
|
||||||
BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
|
BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
|
||||||
{
|
{
|
||||||
BusState *bus;
|
BusState *bus;
|
||||||
|
Object *child = object_resolve_path_component(OBJECT(dev), name);
|
||||||
|
|
||||||
|
bus = (BusState *)object_dynamic_cast(child, TYPE_BUS);
|
||||||
|
if (bus) {
|
||||||
|
return bus;
|
||||||
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||||
if (strcmp(name, bus->name) == 0) {
|
if (strcmp(name, bus->name) == 0) {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* GNU GPL, version 2 or (at your option) any later version.
|
* GNU GPL, version 2 or (at your option) any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
/* The controller can support a variety of different displays, but we only
|
/* The controller can support a variety of different displays, but we only
|
||||||
implement one. Most of the commends relating to brightness and geometry
|
implement one. Most of the commends relating to brightness and geometry
|
||||||
setup are ignored. */
|
setup are ignored. */
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
|
|
||||||
//#define DEBUG_SSD0323 1
|
//#define DEBUG_SSD0323 1
|
||||||
|
|
|
@ -31,8 +31,16 @@ do { fprintf(stderr, "arm_gic: " fmt , ## __VA_ARGS__); } while (0)
|
||||||
#define DPRINTF(fmt, ...) do {} while(0)
|
#define DPRINTF(fmt, ...) do {} while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const uint8_t gic_id[] = {
|
static const uint8_t gic_id_11mpcore[] = {
|
||||||
0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
|
0x00, 0x00, 0x00, 0x00, 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gic_id_gicv1[] = {
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x90, 0xb3, 0x1b, 0x00, 0x0d, 0xf0, 0x05, 0xb1
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gic_id_gicv2[] = {
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x90, 0xb4, 0x2b, 0x00, 0x0d, 0xf0, 0x05, 0xb1
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int gic_get_current_cpu(GICState *s)
|
static inline int gic_get_current_cpu(GICState *s)
|
||||||
|
@ -683,15 +691,32 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
res = s->sgi_pending[irq][cpu];
|
res = s->sgi_pending[irq][cpu];
|
||||||
} else if (offset < 0xfe0) {
|
} else if (offset < 0xfd0) {
|
||||||
goto bad_reg;
|
goto bad_reg;
|
||||||
} else /* offset >= 0xfe0 */ {
|
} else if (offset < 0x1000) {
|
||||||
if (offset & 3) {
|
if (offset & 3) {
|
||||||
res = 0;
|
res = 0;
|
||||||
} else {
|
} else {
|
||||||
res = gic_id[(offset - 0xfe0) >> 2];
|
switch (s->revision) {
|
||||||
|
case REV_11MPCORE:
|
||||||
|
res = gic_id_11mpcore[(offset - 0xfd0) >> 2];
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
res = gic_id_gicv1[(offset - 0xfd0) >> 2];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
res = gic_id_gicv2[(offset - 0xfd0) >> 2];
|
||||||
|
break;
|
||||||
|
case REV_NVIC:
|
||||||
|
/* Shouldn't be able to get here */
|
||||||
|
abort();
|
||||||
|
default:
|
||||||
|
res = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
bad_reg:
|
bad_reg:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "hw/char/serial.h"
|
#include "hw/char/serial.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
|
|
||||||
#include "boot.h"
|
#include "boot.h"
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* GNU GPL, version 2 or (at your option) any later version.
|
* GNU GPL, version 2 or (at your option) any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SSISlave parent_obj;
|
SSISlave parent_obj;
|
||||||
|
|
|
@ -220,7 +220,7 @@ static void zynq_xadc_write(void *opaque, hwaddr offset, uint64_t val,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xadc_reg > ZYNQ_XADC_NUM_ADC_REGS && xadc_cmd != CMD_NOP) {
|
if (xadc_reg >= ZYNQ_XADC_NUM_ADC_REGS && xadc_cmd != CMD_NOP) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "read/write op to invalid xadc "
|
qemu_log_mask(LOG_GUEST_ERROR, "read/write op to invalid xadc "
|
||||||
"reg 0x%x\n", xadc_reg);
|
"reg 0x%x\n", xadc_reg);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "sysemu/blockdev.h"
|
#include "sysemu/blockdev.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "hw/sd/sd.h"
|
#include "hw/sd/sd.h"
|
||||||
|
|
||||||
//#define DEBUG_SSI_SD 1
|
//#define DEBUG_SSI_SD 1
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
|
|
||||||
//#define DEBUG_PL022 1
|
//#define DEBUG_PL022 1
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* GNU GPL, version 2 or (at your option) any later version.
|
* GNU GPL, version 2 or (at your option) any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
|
|
||||||
struct SSIBus {
|
struct SSIBus {
|
||||||
BusState parent_obj;
|
BusState parent_obj;
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "qemu/fifo8.h"
|
#include "qemu/fifo8.h"
|
||||||
|
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
|
|
||||||
#ifdef XILINX_SPI_ERR_DEBUG
|
#ifdef XILINX_SPI_ERR_DEBUG
|
||||||
#define DB_PRINT(...) do { \
|
#define DB_PRINT(...) do { \
|
||||||
|
|
|
@ -27,8 +27,9 @@
|
||||||
#include "hw/ptimer.h"
|
#include "hw/ptimer.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "qemu/fifo8.h"
|
#include "qemu/fifo8.h"
|
||||||
#include "hw/ssi.h"
|
#include "hw/ssi/ssi.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
|
#include "hw/ssi/xilinx_spips.h"
|
||||||
|
|
||||||
#ifndef XILINX_SPIPS_ERR_DEBUG
|
#ifndef XILINX_SPIPS_ERR_DEBUG
|
||||||
#define XILINX_SPIPS_ERR_DEBUG 0
|
#define XILINX_SPIPS_ERR_DEBUG 0
|
||||||
|
@ -103,8 +104,6 @@
|
||||||
|
|
||||||
#define R_MOD_ID (0xFC / 4)
|
#define R_MOD_ID (0xFC / 4)
|
||||||
|
|
||||||
#define R_MAX (R_MOD_ID+1)
|
|
||||||
|
|
||||||
/* size of TXRX FIFOs */
|
/* size of TXRX FIFOs */
|
||||||
#define RXFF_A 32
|
#define RXFF_A 32
|
||||||
#define TXFF_A 32
|
#define TXFF_A 32
|
||||||
|
@ -134,30 +133,6 @@ typedef enum {
|
||||||
QPP = 0x32,
|
QPP = 0x32,
|
||||||
} FlashCMD;
|
} FlashCMD;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
SysBusDevice parent_obj;
|
|
||||||
|
|
||||||
MemoryRegion iomem;
|
|
||||||
MemoryRegion mmlqspi;
|
|
||||||
|
|
||||||
qemu_irq irq;
|
|
||||||
int irqline;
|
|
||||||
|
|
||||||
uint8_t num_cs;
|
|
||||||
uint8_t num_busses;
|
|
||||||
|
|
||||||
uint8_t snoop_state;
|
|
||||||
qemu_irq *cs_lines;
|
|
||||||
SSIBus **spi;
|
|
||||||
|
|
||||||
Fifo8 rx_fifo;
|
|
||||||
Fifo8 tx_fifo;
|
|
||||||
|
|
||||||
uint8_t num_txrx_bytes;
|
|
||||||
|
|
||||||
uint32_t regs[R_MAX];
|
|
||||||
} XilinxSPIPS;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
XilinxSPIPS parent_obj;
|
XilinxSPIPS parent_obj;
|
||||||
|
|
||||||
|
@ -174,19 +149,6 @@ typedef struct XilinxSPIPSClass {
|
||||||
uint32_t tx_fifo_size;
|
uint32_t tx_fifo_size;
|
||||||
} XilinxSPIPSClass;
|
} XilinxSPIPSClass;
|
||||||
|
|
||||||
#define TYPE_XILINX_SPIPS "xlnx.ps7-spi"
|
|
||||||
#define TYPE_XILINX_QSPIPS "xlnx.ps7-qspi"
|
|
||||||
|
|
||||||
#define XILINX_SPIPS(obj) \
|
|
||||||
OBJECT_CHECK(XilinxSPIPS, (obj), TYPE_XILINX_SPIPS)
|
|
||||||
#define XILINX_SPIPS_CLASS(klass) \
|
|
||||||
OBJECT_CLASS_CHECK(XilinxSPIPSClass, (klass), TYPE_XILINX_SPIPS)
|
|
||||||
#define XILINX_SPIPS_GET_CLASS(obj) \
|
|
||||||
OBJECT_GET_CLASS(XilinxSPIPSClass, (obj), TYPE_XILINX_SPIPS)
|
|
||||||
|
|
||||||
#define XILINX_QSPIPS(obj) \
|
|
||||||
OBJECT_CHECK(XilinxQSPIPS, (obj), TYPE_XILINX_QSPIPS)
|
|
||||||
|
|
||||||
static inline int num_effective_busses(XilinxSPIPS *s)
|
static inline int num_effective_busses(XilinxSPIPS *s)
|
||||||
{
|
{
|
||||||
return (s->regs[R_LQSPI_CFG] & LQSPI_CFG_SEP_BUS &&
|
return (s->regs[R_LQSPI_CFG] & LQSPI_CFG_SEP_BUS &&
|
||||||
|
@ -257,7 +219,7 @@ static void xilinx_spips_reset(DeviceState *d)
|
||||||
XilinxSPIPS *s = XILINX_SPIPS(d);
|
XilinxSPIPS *s = XILINX_SPIPS(d);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < R_MAX; i++) {
|
for (i = 0; i < XLNX_SPIPS_R_MAX; i++) {
|
||||||
s->regs[i] = 0;
|
s->regs[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,7 +626,7 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_region_init_io(&s->iomem, OBJECT(s), xsc->reg_ops, s,
|
memory_region_init_io(&s->iomem, OBJECT(s), xsc->reg_ops, s,
|
||||||
"spi", R_MAX*4);
|
"spi", XLNX_SPIPS_R_MAX * 4);
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
|
|
||||||
s->irqline = -1;
|
s->irqline = -1;
|
||||||
|
@ -708,7 +670,7 @@ static const VMStateDescription vmstate_xilinx_spips = {
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_FIFO8(tx_fifo, XilinxSPIPS),
|
VMSTATE_FIFO8(tx_fifo, XilinxSPIPS),
|
||||||
VMSTATE_FIFO8(rx_fifo, XilinxSPIPS),
|
VMSTATE_FIFO8(rx_fifo, XilinxSPIPS),
|
||||||
VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, R_MAX),
|
VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, XLNX_SPIPS_R_MAX),
|
||||||
VMSTATE_UINT8(snoop_state, XilinxSPIPS),
|
VMSTATE_UINT8(snoop_state, XilinxSPIPS),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,34 @@ void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc);
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
void cpu_reloading_memory_map(void);
|
void cpu_reloading_memory_map(void);
|
||||||
void tcg_cpu_address_space_init(CPUState *cpu, AddressSpace *as);
|
/**
|
||||||
|
* cpu_address_space_init:
|
||||||
|
* @cpu: CPU to add this address space to
|
||||||
|
* @as: address space to add
|
||||||
|
* @asidx: integer index of this address space
|
||||||
|
*
|
||||||
|
* Add the specified address space to the CPU's cpu_ases list.
|
||||||
|
* The address space added with @asidx 0 is the one used for the
|
||||||
|
* convenience pointer cpu->as.
|
||||||
|
* The target-specific code which registers ASes is responsible
|
||||||
|
* for defining what semantics address space 0, 1, 2, etc have.
|
||||||
|
*
|
||||||
|
* Before the first call to this function, the caller must set
|
||||||
|
* cpu->num_ases to the total number of address spaces it needs
|
||||||
|
* to support.
|
||||||
|
*
|
||||||
|
* Note that with KVM only one address space is supported.
|
||||||
|
*/
|
||||||
|
void cpu_address_space_init(CPUState *cpu, AddressSpace *as, int asidx);
|
||||||
|
/**
|
||||||
|
* cpu_get_address_space:
|
||||||
|
* @cpu: CPU to get address space from
|
||||||
|
* @asidx: index identifying which address space to get
|
||||||
|
*
|
||||||
|
* Return the requested address space of this CPU. @asidx
|
||||||
|
* specifies which address space to read.
|
||||||
|
*/
|
||||||
|
AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx);
|
||||||
/* cputlb.c */
|
/* cputlb.c */
|
||||||
/**
|
/**
|
||||||
* tlb_flush_page:
|
* tlb_flush_page:
|
||||||
|
@ -126,12 +153,40 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...);
|
||||||
* MMU indexes.
|
* MMU indexes.
|
||||||
*/
|
*/
|
||||||
void tlb_flush_by_mmuidx(CPUState *cpu, ...);
|
void tlb_flush_by_mmuidx(CPUState *cpu, ...);
|
||||||
void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
/**
|
||||||
hwaddr paddr, int prot,
|
* tlb_set_page_with_attrs:
|
||||||
int mmu_idx, target_ulong size);
|
* @cpu: CPU to add this TLB entry for
|
||||||
|
* @vaddr: virtual address of page to add entry for
|
||||||
|
* @paddr: physical address of the page
|
||||||
|
* @attrs: memory transaction attributes
|
||||||
|
* @prot: access permissions (PAGE_READ/PAGE_WRITE/PAGE_EXEC bits)
|
||||||
|
* @mmu_idx: MMU index to insert TLB entry for
|
||||||
|
* @size: size of the page in bytes
|
||||||
|
*
|
||||||
|
* Add an entry to this CPU's TLB (a mapping from virtual address
|
||||||
|
* @vaddr to physical address @paddr) with the specified memory
|
||||||
|
* transaction attributes. This is generally called by the target CPU
|
||||||
|
* specific code after it has been called through the tlb_fill()
|
||||||
|
* entry point and performed a successful page table walk to find
|
||||||
|
* the physical address and attributes for the virtual address
|
||||||
|
* which provoked the TLB miss.
|
||||||
|
*
|
||||||
|
* At most one entry for a given virtual address is permitted. Only a
|
||||||
|
* single TARGET_PAGE_SIZE region is mapped; the supplied @size is only
|
||||||
|
* used by tlb_flush_page.
|
||||||
|
*/
|
||||||
void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||||
hwaddr paddr, MemTxAttrs attrs,
|
hwaddr paddr, MemTxAttrs attrs,
|
||||||
int prot, int mmu_idx, target_ulong size);
|
int prot, int mmu_idx, target_ulong size);
|
||||||
|
/* tlb_set_page:
|
||||||
|
*
|
||||||
|
* This function is equivalent to calling tlb_set_page_with_attrs()
|
||||||
|
* with an @attrs argument of MEMTXATTRS_UNSPECIFIED. It's provided
|
||||||
|
* as a convenience for CPUs which don't use memory transaction attributes.
|
||||||
|
*/
|
||||||
|
void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
||||||
|
hwaddr paddr, int prot,
|
||||||
|
int mmu_idx, target_ulong size);
|
||||||
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr);
|
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr);
|
||||||
void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx,
|
void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx,
|
||||||
uintptr_t retaddr);
|
uintptr_t retaddr);
|
||||||
|
@ -357,7 +412,7 @@ extern uintptr_t tci_tb_ptr;
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
|
||||||
struct MemoryRegion *iotlb_to_region(CPUState *cpu,
|
struct MemoryRegion *iotlb_to_region(CPUState *cpu,
|
||||||
hwaddr index);
|
hwaddr index, MemTxAttrs attrs);
|
||||||
|
|
||||||
void tlb_fill(CPUState *cpu, target_ulong addr, int is_write, int mmu_idx,
|
void tlb_fill(CPUState *cpu, target_ulong addr, int is_write, int mmu_idx,
|
||||||
uintptr_t retaddr);
|
uintptr_t retaddr);
|
||||||
|
@ -386,8 +441,8 @@ void tlb_set_dirty(CPUState *cpu, target_ulong vaddr);
|
||||||
void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr);
|
void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr);
|
||||||
|
|
||||||
MemoryRegionSection *
|
MemoryRegionSection *
|
||||||
address_space_translate_for_iotlb(CPUState *cpu, hwaddr addr, hwaddr *xlat,
|
address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
|
||||||
hwaddr *plen);
|
hwaddr *xlat, hwaddr *plen);
|
||||||
hwaddr memory_region_section_get_iotlb(CPUState *cpu,
|
hwaddr memory_region_section_get_iotlb(CPUState *cpu,
|
||||||
MemoryRegionSection *section,
|
MemoryRegionSection *section,
|
||||||
target_ulong vaddr,
|
target_ulong vaddr,
|
||||||
|
|
|
@ -241,6 +241,8 @@ struct AddressSpace {
|
||||||
struct rcu_head rcu;
|
struct rcu_head rcu;
|
||||||
char *name;
|
char *name;
|
||||||
MemoryRegion *root;
|
MemoryRegion *root;
|
||||||
|
int ref_count;
|
||||||
|
bool malloced;
|
||||||
|
|
||||||
/* Accessed via RCU. */
|
/* Accessed via RCU. */
|
||||||
struct FlatView *current_map;
|
struct FlatView *current_map;
|
||||||
|
@ -1189,6 +1191,22 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr,
|
||||||
*/
|
*/
|
||||||
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name);
|
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* address_space_init_shareable: return an address space for a memory region,
|
||||||
|
* creating it if it does not already exist
|
||||||
|
*
|
||||||
|
* @root: a #MemoryRegion that routes addresses for the address space
|
||||||
|
* @name: an address space name. The name is only used for debugging
|
||||||
|
* output.
|
||||||
|
*
|
||||||
|
* This function will return a pointer to an existing AddressSpace
|
||||||
|
* which was initialized with the specified MemoryRegion, or it will
|
||||||
|
* create and initialize one if it does not already exist. The ASes
|
||||||
|
* are reference-counted, so the memory will be freed automatically
|
||||||
|
* when the AddressSpace is destroyed via address_space_destroy.
|
||||||
|
*/
|
||||||
|
AddressSpace *address_space_init_shareable(MemoryRegion *root,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* address_space_destroy: destroy an address space
|
* address_space_destroy: destroy an address space
|
||||||
|
|
|
@ -60,6 +60,7 @@ enum {
|
||||||
VIRT_PLATFORM_BUS,
|
VIRT_PLATFORM_BUS,
|
||||||
VIRT_PCIE_MMIO_HIGH,
|
VIRT_PCIE_MMIO_HIGH,
|
||||||
VIRT_GPIO,
|
VIRT_GPIO,
|
||||||
|
VIRT_SECURE_UART,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct MemMapEntry {
|
typedef struct MemMapEntry {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "hw/ide/pci.h"
|
#include "hw/ide/pci.h"
|
||||||
#include "hw/ide/ahci.h"
|
#include "hw/ide/ahci.h"
|
||||||
#include "hw/sd/sdhci.h"
|
#include "hw/sd/sdhci.h"
|
||||||
|
#include "hw/ssi/xilinx_spips.h"
|
||||||
|
|
||||||
#define TYPE_XLNX_ZYNQMP "xlnx,zynqmp"
|
#define TYPE_XLNX_ZYNQMP "xlnx,zynqmp"
|
||||||
#define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \
|
#define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
#define XLNX_ZYNQMP_NUM_GEMS 4
|
#define XLNX_ZYNQMP_NUM_GEMS 4
|
||||||
#define XLNX_ZYNQMP_NUM_UARTS 2
|
#define XLNX_ZYNQMP_NUM_UARTS 2
|
||||||
#define XLNX_ZYNQMP_NUM_SDHCI 2
|
#define XLNX_ZYNQMP_NUM_SDHCI 2
|
||||||
|
#define XLNX_ZYNQMP_NUM_SPIS 2
|
||||||
|
|
||||||
#define XLNX_ZYNQMP_NUM_OCM_BANKS 4
|
#define XLNX_ZYNQMP_NUM_OCM_BANKS 4
|
||||||
#define XLNX_ZYNQMP_OCM_RAM_0_ADDRESS 0xFFFC0000
|
#define XLNX_ZYNQMP_OCM_RAM_0_ADDRESS 0xFFFC0000
|
||||||
|
@ -78,6 +80,7 @@ typedef struct XlnxZynqMPState {
|
||||||
CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
|
CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
|
||||||
SysbusAHCIState sata;
|
SysbusAHCIState sata;
|
||||||
SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI];
|
SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI];
|
||||||
|
XilinxSPIPS spi[XLNX_ZYNQMP_NUM_SPIS];
|
||||||
|
|
||||||
char *boot_cpu;
|
char *boot_cpu;
|
||||||
ARMCPU *boot_cpu_ptr;
|
ARMCPU *boot_cpu_ptr;
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "hw/qdev.h"
|
#include "hw/qdev.h"
|
||||||
|
|
||||||
typedef struct SSISlave SSISlave;
|
typedef struct SSISlave SSISlave;
|
||||||
|
typedef struct SSISlaveClass SSISlaveClass;
|
||||||
|
typedef enum SSICSMode SSICSMode;
|
||||||
|
|
||||||
#define TYPE_SSI_SLAVE "ssi-slave"
|
#define TYPE_SSI_SLAVE "ssi-slave"
|
||||||
#define SSI_SLAVE(obj) \
|
#define SSI_SLAVE(obj) \
|
||||||
|
@ -25,14 +27,14 @@ typedef struct SSISlave SSISlave;
|
||||||
|
|
||||||
#define SSI_GPIO_CS "ssi-gpio-cs"
|
#define SSI_GPIO_CS "ssi-gpio-cs"
|
||||||
|
|
||||||
typedef enum {
|
enum SSICSMode {
|
||||||
SSI_CS_NONE = 0,
|
SSI_CS_NONE = 0,
|
||||||
SSI_CS_LOW,
|
SSI_CS_LOW,
|
||||||
SSI_CS_HIGH,
|
SSI_CS_HIGH,
|
||||||
} SSICSMode;
|
};
|
||||||
|
|
||||||
/* Slave devices. */
|
/* Slave devices. */
|
||||||
typedef struct SSISlaveClass {
|
struct SSISlaveClass {
|
||||||
DeviceClass parent_class;
|
DeviceClass parent_class;
|
||||||
|
|
||||||
int (*init)(SSISlave *dev);
|
int (*init)(SSISlave *dev);
|
||||||
|
@ -55,7 +57,7 @@ typedef struct SSISlaveClass {
|
||||||
* always be called for the device for every txrx access to the parent bus
|
* always be called for the device for every txrx access to the parent bus
|
||||||
*/
|
*/
|
||||||
uint32_t (*transfer_raw)(SSISlave *dev, uint32_t val);
|
uint32_t (*transfer_raw)(SSISlave *dev, uint32_t val);
|
||||||
} SSISlaveClass;
|
};
|
||||||
|
|
||||||
struct SSISlave {
|
struct SSISlave {
|
||||||
DeviceState parent_obj;
|
DeviceState parent_obj;
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Header file for the Xilinx Zynq SPI controller
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Xilinx Inc
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XLNX_SPIPS_H
|
||||||
|
#define XLNX_SPIPS_H
|
||||||
|
|
||||||
|
#include "hw/ssi/ssi.h"
|
||||||
|
#include "qemu/fifo8.h"
|
||||||
|
|
||||||
|
typedef struct XilinxSPIPS XilinxSPIPS;
|
||||||
|
|
||||||
|
#define XLNX_SPIPS_R_MAX (0x100 / 4)
|
||||||
|
|
||||||
|
struct XilinxSPIPS {
|
||||||
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
|
MemoryRegion iomem;
|
||||||
|
MemoryRegion mmlqspi;
|
||||||
|
|
||||||
|
qemu_irq irq;
|
||||||
|
int irqline;
|
||||||
|
|
||||||
|
uint8_t num_cs;
|
||||||
|
uint8_t num_busses;
|
||||||
|
|
||||||
|
uint8_t snoop_state;
|
||||||
|
qemu_irq *cs_lines;
|
||||||
|
SSIBus **spi;
|
||||||
|
|
||||||
|
Fifo8 rx_fifo;
|
||||||
|
Fifo8 tx_fifo;
|
||||||
|
|
||||||
|
uint8_t num_txrx_bytes;
|
||||||
|
|
||||||
|
uint32_t regs[XLNX_SPIPS_R_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TYPE_XILINX_SPIPS "xlnx.ps7-spi"
|
||||||
|
#define TYPE_XILINX_QSPIPS "xlnx.ps7-qspi"
|
||||||
|
|
||||||
|
#define XILINX_SPIPS(obj) \
|
||||||
|
OBJECT_CHECK(XilinxSPIPS, (obj), TYPE_XILINX_SPIPS)
|
||||||
|
#define XILINX_SPIPS_CLASS(klass) \
|
||||||
|
OBJECT_CLASS_CHECK(XilinxSPIPSClass, (klass), TYPE_XILINX_SPIPS)
|
||||||
|
#define XILINX_SPIPS_GET_CLASS(obj) \
|
||||||
|
OBJECT_GET_CLASS(XilinxSPIPSClass, (obj), TYPE_XILINX_SPIPS)
|
||||||
|
|
||||||
|
#define XILINX_QSPIPS(obj) \
|
||||||
|
OBJECT_CHECK(XilinxQSPIPS, (obj), TYPE_XILINX_QSPIPS)
|
||||||
|
|
||||||
|
#endif /* XLNX_SPIPS_H */
|
|
@ -98,6 +98,12 @@ struct TranslationBlock;
|
||||||
* #TranslationBlock.
|
* #TranslationBlock.
|
||||||
* @handle_mmu_fault: Callback for handling an MMU fault.
|
* @handle_mmu_fault: Callback for handling an MMU fault.
|
||||||
* @get_phys_page_debug: Callback for obtaining a physical address.
|
* @get_phys_page_debug: Callback for obtaining a physical address.
|
||||||
|
* @get_phys_page_attrs_debug: Callback for obtaining a physical address and the
|
||||||
|
* associated memory transaction attributes to use for the access.
|
||||||
|
* CPUs which use memory transaction attributes should implement this
|
||||||
|
* instead of get_phys_page_debug.
|
||||||
|
* @asidx_from_attrs: Callback to return the CPU AddressSpace to use for
|
||||||
|
* a memory access with the specified memory transaction attributes.
|
||||||
* @gdb_read_register: Callback for letting GDB read a register.
|
* @gdb_read_register: Callback for letting GDB read a register.
|
||||||
* @gdb_write_register: Callback for letting GDB write a register.
|
* @gdb_write_register: Callback for letting GDB write a register.
|
||||||
* @debug_excp_handler: Callback for handling debug exceptions.
|
* @debug_excp_handler: Callback for handling debug exceptions.
|
||||||
|
@ -152,6 +158,9 @@ typedef struct CPUClass {
|
||||||
int (*handle_mmu_fault)(CPUState *cpu, vaddr address, int rw,
|
int (*handle_mmu_fault)(CPUState *cpu, vaddr address, int rw,
|
||||||
int mmu_index);
|
int mmu_index);
|
||||||
hwaddr (*get_phys_page_debug)(CPUState *cpu, vaddr addr);
|
hwaddr (*get_phys_page_debug)(CPUState *cpu, vaddr addr);
|
||||||
|
hwaddr (*get_phys_page_attrs_debug)(CPUState *cpu, vaddr addr,
|
||||||
|
MemTxAttrs *attrs);
|
||||||
|
int (*asidx_from_attrs)(CPUState *cpu, MemTxAttrs attrs);
|
||||||
int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg);
|
int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg);
|
int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
void (*debug_excp_handler)(CPUState *cpu);
|
void (*debug_excp_handler)(CPUState *cpu);
|
||||||
|
@ -236,6 +245,7 @@ struct kvm_run;
|
||||||
* so that interrupts take effect immediately.
|
* so that interrupts take effect immediately.
|
||||||
* @cpu_ases: Pointer to array of CPUAddressSpaces (which define the
|
* @cpu_ases: Pointer to array of CPUAddressSpaces (which define the
|
||||||
* AddressSpaces this CPU has)
|
* AddressSpaces this CPU has)
|
||||||
|
* @num_ases: number of CPUAddressSpaces in @cpu_ases
|
||||||
* @as: Pointer to the first AddressSpace, for the convenience of targets which
|
* @as: Pointer to the first AddressSpace, for the convenience of targets which
|
||||||
* only have a single AddressSpace
|
* only have a single AddressSpace
|
||||||
* @env_ptr: Pointer to subclass-specific CPUArchState field.
|
* @env_ptr: Pointer to subclass-specific CPUArchState field.
|
||||||
|
@ -285,7 +295,9 @@ struct CPUState {
|
||||||
struct qemu_work_item *queued_work_first, *queued_work_last;
|
struct qemu_work_item *queued_work_first, *queued_work_last;
|
||||||
|
|
||||||
CPUAddressSpace *cpu_ases;
|
CPUAddressSpace *cpu_ases;
|
||||||
|
int num_ases;
|
||||||
AddressSpace *as;
|
AddressSpace *as;
|
||||||
|
MemoryRegion *memory;
|
||||||
|
|
||||||
void *env_ptr; /* CPUArchState */
|
void *env_ptr; /* CPUArchState */
|
||||||
struct TranslationBlock *current_tb;
|
struct TranslationBlock *current_tb;
|
||||||
|
@ -442,6 +454,32 @@ void cpu_dump_statistics(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
|
||||||
int flags);
|
int flags);
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
/**
|
||||||
|
* cpu_get_phys_page_attrs_debug:
|
||||||
|
* @cpu: The CPU to obtain the physical page address for.
|
||||||
|
* @addr: The virtual address.
|
||||||
|
* @attrs: Updated on return with the memory transaction attributes to use
|
||||||
|
* for this access.
|
||||||
|
*
|
||||||
|
* Obtains the physical page corresponding to a virtual one, together
|
||||||
|
* with the corresponding memory transaction attributes to use for the access.
|
||||||
|
* Use it only for debugging because no protection checks are done.
|
||||||
|
*
|
||||||
|
* Returns: Corresponding physical page address or -1 if no page found.
|
||||||
|
*/
|
||||||
|
static inline hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr,
|
||||||
|
MemTxAttrs *attrs)
|
||||||
|
{
|
||||||
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||||
|
|
||||||
|
if (cc->get_phys_page_attrs_debug) {
|
||||||
|
return cc->get_phys_page_attrs_debug(cpu, addr, attrs);
|
||||||
|
}
|
||||||
|
/* Fallback for CPUs which don't implement the _attrs_ hook */
|
||||||
|
*attrs = MEMTXATTRS_UNSPECIFIED;
|
||||||
|
return cc->get_phys_page_debug(cpu, addr);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cpu_get_phys_page_debug:
|
* cpu_get_phys_page_debug:
|
||||||
* @cpu: The CPU to obtain the physical page address for.
|
* @cpu: The CPU to obtain the physical page address for.
|
||||||
|
@ -453,10 +491,27 @@ void cpu_dump_statistics(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
|
||||||
* Returns: Corresponding physical page address or -1 if no page found.
|
* Returns: Corresponding physical page address or -1 if no page found.
|
||||||
*/
|
*/
|
||||||
static inline hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr)
|
static inline hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr)
|
||||||
|
{
|
||||||
|
MemTxAttrs attrs = {};
|
||||||
|
|
||||||
|
return cpu_get_phys_page_attrs_debug(cpu, addr, &attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** cpu_asidx_from_attrs:
|
||||||
|
* @cpu: CPU
|
||||||
|
* @attrs: memory transaction attributes
|
||||||
|
*
|
||||||
|
* Returns the address space index specifying the CPU AddressSpace
|
||||||
|
* to use for a memory access with the given transaction attributes.
|
||||||
|
*/
|
||||||
|
static inline int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs)
|
||||||
{
|
{
|
||||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||||
|
|
||||||
return cc->get_phys_page_debug(cpu, addr);
|
if (cc->asidx_from_attrs) {
|
||||||
|
return cc->asidx_from_attrs(cpu, attrs);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
27
memory.c
27
memory.c
|
@ -2124,7 +2124,9 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
|
||||||
{
|
{
|
||||||
memory_region_ref(root);
|
memory_region_ref(root);
|
||||||
memory_region_transaction_begin();
|
memory_region_transaction_begin();
|
||||||
|
as->ref_count = 1;
|
||||||
as->root = root;
|
as->root = root;
|
||||||
|
as->malloced = false;
|
||||||
as->current_map = g_new(FlatView, 1);
|
as->current_map = g_new(FlatView, 1);
|
||||||
flatview_init(as->current_map);
|
flatview_init(as->current_map);
|
||||||
as->ioeventfd_nb = 0;
|
as->ioeventfd_nb = 0;
|
||||||
|
@ -2139,6 +2141,7 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
|
||||||
static void do_address_space_destroy(AddressSpace *as)
|
static void do_address_space_destroy(AddressSpace *as)
|
||||||
{
|
{
|
||||||
MemoryListener *listener;
|
MemoryListener *listener;
|
||||||
|
bool do_free = as->malloced;
|
||||||
|
|
||||||
address_space_destroy_dispatch(as);
|
address_space_destroy_dispatch(as);
|
||||||
|
|
||||||
|
@ -2150,12 +2153,36 @@ static void do_address_space_destroy(AddressSpace *as)
|
||||||
g_free(as->name);
|
g_free(as->name);
|
||||||
g_free(as->ioeventfds);
|
g_free(as->ioeventfds);
|
||||||
memory_region_unref(as->root);
|
memory_region_unref(as->root);
|
||||||
|
if (do_free) {
|
||||||
|
g_free(as);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressSpace *address_space_init_shareable(MemoryRegion *root, const char *name)
|
||||||
|
{
|
||||||
|
AddressSpace *as;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
|
||||||
|
if (root == as->root && as->malloced) {
|
||||||
|
as->ref_count++;
|
||||||
|
return as;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
as = g_malloc0(sizeof *as);
|
||||||
|
address_space_init(as, root, name);
|
||||||
|
as->malloced = true;
|
||||||
|
return as;
|
||||||
}
|
}
|
||||||
|
|
||||||
void address_space_destroy(AddressSpace *as)
|
void address_space_destroy(AddressSpace *as)
|
||||||
{
|
{
|
||||||
MemoryRegion *root = as->root;
|
MemoryRegion *root = as->root;
|
||||||
|
|
||||||
|
as->ref_count--;
|
||||||
|
if (as->ref_count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
/* Flush out anything from MemoryListeners listening in on this */
|
/* Flush out anything from MemoryListeners listening in on this */
|
||||||
memory_region_transaction_begin();
|
memory_region_transaction_begin();
|
||||||
as->root = NULL;
|
as->root = NULL;
|
||||||
|
|
|
@ -150,7 +150,7 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
CPUState *cpu = ENV_GET_CPU(env);
|
CPUState *cpu = ENV_GET_CPU(env);
|
||||||
hwaddr physaddr = iotlbentry->addr;
|
hwaddr physaddr = iotlbentry->addr;
|
||||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr);
|
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||||
|
|
||||||
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
||||||
cpu->mem_io_pc = retaddr;
|
cpu->mem_io_pc = retaddr;
|
||||||
|
@ -357,7 +357,7 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
||||||
{
|
{
|
||||||
CPUState *cpu = ENV_GET_CPU(env);
|
CPUState *cpu = ENV_GET_CPU(env);
|
||||||
hwaddr physaddr = iotlbentry->addr;
|
hwaddr physaddr = iotlbentry->addr;
|
||||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr);
|
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||||
|
|
||||||
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
|
||||||
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
||||||
|
|
|
@ -87,6 +87,9 @@ typedef struct ARMCPU {
|
||||||
/* GPIO outputs for generic timer */
|
/* GPIO outputs for generic timer */
|
||||||
qemu_irq gt_timer_outputs[NUM_GTIMERS];
|
qemu_irq gt_timer_outputs[NUM_GTIMERS];
|
||||||
|
|
||||||
|
/* MemoryRegion to use for secure physical accesses */
|
||||||
|
MemoryRegion *secure_memory;
|
||||||
|
|
||||||
/* 'compatible' string for this CPU for Linux device trees */
|
/* 'compatible' string for this CPU for Linux device trees */
|
||||||
const char *dtb_compatible;
|
const char *dtb_compatible;
|
||||||
|
|
||||||
|
@ -216,7 +219,8 @@ bool arm_cpu_exec_interrupt(CPUState *cpu, int int_req);
|
||||||
void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
|
void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
|
||||||
int flags);
|
int flags);
|
||||||
|
|
||||||
hwaddr arm_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr,
|
||||||
|
MemTxAttrs *attrs);
|
||||||
|
|
||||||
int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
|
@ -248,8 +252,6 @@ void arm_gt_stimer_cb(void *opaque);
|
||||||
#ifdef TARGET_AARCH64
|
#ifdef TARGET_AARCH64
|
||||||
int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
|
|
||||||
void aarch64_cpu_do_interrupt(CPUState *cs);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -543,6 +543,15 @@ static void arm_cpu_post_init(Object *obj)
|
||||||
*/
|
*/
|
||||||
qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property,
|
qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
object_property_add_link(obj, "secure-memory",
|
||||||
|
TYPE_MEMORY_REGION,
|
||||||
|
(Object **)&cpu->secure_memory,
|
||||||
|
qdev_prop_allow_set_link_before_realize,
|
||||||
|
OBJ_PROP_LINK_UNREF_ON_RELEASE,
|
||||||
|
&error_abort);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) {
|
if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) {
|
||||||
|
@ -666,6 +675,29 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
init_cpreg_list(cpu);
|
init_cpreg_list(cpu);
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
if (cpu->has_el3) {
|
||||||
|
cs->num_ases = 2;
|
||||||
|
} else {
|
||||||
|
cs->num_ases = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpu->has_el3) {
|
||||||
|
AddressSpace *as;
|
||||||
|
|
||||||
|
if (!cpu->secure_memory) {
|
||||||
|
cpu->secure_memory = cs->memory;
|
||||||
|
}
|
||||||
|
as = address_space_init_shareable(cpu->secure_memory,
|
||||||
|
"cpu-secure-memory");
|
||||||
|
cpu_address_space_init(cs, as, ARMASIdx_S);
|
||||||
|
}
|
||||||
|
cpu_address_space_init(cs,
|
||||||
|
address_space_init_shareable(cs->memory,
|
||||||
|
"cpu-memory"),
|
||||||
|
ARMASIdx_NS);
|
||||||
|
#endif
|
||||||
|
|
||||||
qemu_init_vcpu(cs);
|
qemu_init_vcpu(cs);
|
||||||
cpu_reset(cs);
|
cpu_reset(cs);
|
||||||
|
|
||||||
|
@ -1419,7 +1451,8 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
|
||||||
#else
|
#else
|
||||||
cc->do_interrupt = arm_cpu_do_interrupt;
|
cc->do_interrupt = arm_cpu_do_interrupt;
|
||||||
cc->do_unaligned_access = arm_cpu_do_unaligned_access;
|
cc->do_unaligned_access = arm_cpu_do_unaligned_access;
|
||||||
cc->get_phys_page_debug = arm_cpu_get_phys_page_debug;
|
cc->get_phys_page_attrs_debug = arm_cpu_get_phys_page_attrs_debug;
|
||||||
|
cc->asidx_from_attrs = arm_asidx_from_attrs;
|
||||||
cc->vmsd = &vmstate_arm_cpu;
|
cc->vmsd = &vmstate_arm_cpu;
|
||||||
cc->virtio_is_big_endian = arm_cpu_is_big_endian;
|
cc->virtio_is_big_endian = arm_cpu_is_big_endian;
|
||||||
cc->write_elf64_note = arm_cpu_write_elf64_note;
|
cc->write_elf64_note = arm_cpu_write_elf64_note;
|
||||||
|
|
|
@ -969,18 +969,33 @@ static inline bool arm_is_secure(CPUARMState *env)
|
||||||
/* Return true if the specified exception level is running in AArch64 state. */
|
/* Return true if the specified exception level is running in AArch64 state. */
|
||||||
static inline bool arm_el_is_aa64(CPUARMState *env, int el)
|
static inline bool arm_el_is_aa64(CPUARMState *env, int el)
|
||||||
{
|
{
|
||||||
/* We don't currently support EL2, and this isn't valid for EL0
|
/* This isn't valid for EL0 (if we're in EL0, is_a64() is what you want,
|
||||||
* (if we're in EL0, is_a64() is what you want, and if we're not in EL0
|
* and if we're not in EL0 then the state of EL0 isn't well defined.)
|
||||||
* then the state of EL0 isn't well defined.)
|
|
||||||
*/
|
*/
|
||||||
assert(el == 1 || el == 3);
|
assert(el >= 1 && el <= 3);
|
||||||
|
bool aa64 = arm_feature(env, ARM_FEATURE_AARCH64);
|
||||||
|
|
||||||
/* AArch64-capable CPUs always run with EL1 in AArch64 mode. This
|
/* The highest exception level is always at the maximum supported
|
||||||
* is a QEMU-imposed simplification which we may wish to change later.
|
* register width, and then lower levels have a register width controlled
|
||||||
* If we in future support EL2 and/or EL3, then the state of lower
|
* by bits in the SCR or HCR registers.
|
||||||
* exception levels is controlled by the HCR.RW and SCR.RW bits.
|
|
||||||
*/
|
*/
|
||||||
return arm_feature(env, ARM_FEATURE_AARCH64);
|
if (el == 3) {
|
||||||
|
return aa64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arm_feature(env, ARM_FEATURE_EL3)) {
|
||||||
|
aa64 = aa64 && (env->cp15.scr_el3 & SCR_RW);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el == 2) {
|
||||||
|
return aa64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arm_feature(env, ARM_FEATURE_EL2) && !arm_is_secure_below_el3(env)) {
|
||||||
|
aa64 = aa64 && (env->cp15.hcr_el2 & HCR_RW);
|
||||||
|
}
|
||||||
|
|
||||||
|
return aa64;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function for determing whether guest cp register reads and writes should
|
/* Function for determing whether guest cp register reads and writes should
|
||||||
|
@ -1720,6 +1735,12 @@ static inline int cpu_mmu_index(CPUARMState *env, bool ifetch)
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Indexes used when registering address spaces with cpu_address_space_init */
|
||||||
|
typedef enum ARMASIdx {
|
||||||
|
ARMASIdx_NS = 0,
|
||||||
|
ARMASIdx_S = 1,
|
||||||
|
} ARMASIdx;
|
||||||
|
|
||||||
/* Return the Exception Level targeted by debug exceptions;
|
/* Return the Exception Level targeted by debug exceptions;
|
||||||
* currently always EL1 since we don't implement EL2 or EL3.
|
* currently always EL1 since we don't implement EL2 or EL3.
|
||||||
*/
|
*/
|
||||||
|
@ -1991,4 +2012,21 @@ enum {
|
||||||
QEMU_PSCI_CONDUIT_HVC = 2,
|
QEMU_PSCI_CONDUIT_HVC = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
/* Return the address space index to use for a memory access */
|
||||||
|
static inline int arm_asidx_from_attrs(CPUState *cs, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
return attrs.secure ? ARMASIdx_S : ARMASIdx_NS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the AddressSpace to use for a memory access
|
||||||
|
* (which depends on whether the access is S or NS, and whether
|
||||||
|
* the board gave us a separate AddressSpace for S accesses).
|
||||||
|
*/
|
||||||
|
static inline AddressSpace *arm_addressspace(CPUState *cs, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
return cpu_get_address_space(cs, arm_asidx_from_attrs(cs, attrs));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -291,9 +291,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
CPUClass *cc = CPU_CLASS(oc);
|
CPUClass *cc = CPU_CLASS(oc);
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
cc->do_interrupt = aarch64_cpu_do_interrupt;
|
|
||||||
#endif
|
|
||||||
cc->cpu_exec_interrupt = arm_cpu_exec_interrupt;
|
cc->cpu_exec_interrupt = arm_cpu_exec_interrupt;
|
||||||
cc->set_pc = aarch64_cpu_set_pc;
|
cc->set_pc = aarch64_cpu_set_pc;
|
||||||
cc->gdb_read_register = aarch64_cpu_gdb_read_register;
|
cc->gdb_read_register = aarch64_cpu_gdb_read_register;
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
#include "qemu/crc32c.h"
|
#include "qemu/crc32c.h"
|
||||||
#include "sysemu/kvm.h"
|
|
||||||
#include <zlib.h> /* For crc32 */
|
#include <zlib.h> /* For crc32 */
|
||||||
|
|
||||||
/* C2.4.7 Multiply and divide */
|
/* C2.4.7 Multiply and divide */
|
||||||
|
@ -444,106 +443,3 @@ uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes)
|
||||||
/* Linux crc32c converts the output to one's complement. */
|
/* Linux crc32c converts the output to one's complement. */
|
||||||
return crc32c(acc, buf, bytes) ^ 0xffffffff;
|
return crc32c(acc, buf, bytes) ^ 0xffffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
|
|
||||||
/* Handle a CPU exception. */
|
|
||||||
void aarch64_cpu_do_interrupt(CPUState *cs)
|
|
||||||
{
|
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
|
||||||
CPUARMState *env = &cpu->env;
|
|
||||||
unsigned int new_el = env->exception.target_el;
|
|
||||||
target_ulong addr = env->cp15.vbar_el[new_el];
|
|
||||||
unsigned int new_mode = aarch64_pstate_mode(new_el, true);
|
|
||||||
|
|
||||||
if (arm_current_el(env) < new_el) {
|
|
||||||
if (env->aarch64) {
|
|
||||||
addr += 0x400;
|
|
||||||
} else {
|
|
||||||
addr += 0x600;
|
|
||||||
}
|
|
||||||
} else if (pstate_read(env) & PSTATE_SP) {
|
|
||||||
addr += 0x200;
|
|
||||||
}
|
|
||||||
|
|
||||||
arm_log_exception(cs->exception_index);
|
|
||||||
qemu_log_mask(CPU_LOG_INT, "...from EL%d to EL%d\n", arm_current_el(env),
|
|
||||||
new_el);
|
|
||||||
if (qemu_loglevel_mask(CPU_LOG_INT)
|
|
||||||
&& !excp_is_internal(cs->exception_index)) {
|
|
||||||
qemu_log_mask(CPU_LOG_INT, "...with ESR %x/0x%" PRIx32 "\n",
|
|
||||||
env->exception.syndrome >> ARM_EL_EC_SHIFT,
|
|
||||||
env->exception.syndrome);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arm_is_psci_call(cpu, cs->exception_index)) {
|
|
||||||
arm_handle_psci_call(cpu);
|
|
||||||
qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (cs->exception_index) {
|
|
||||||
case EXCP_PREFETCH_ABORT:
|
|
||||||
case EXCP_DATA_ABORT:
|
|
||||||
env->cp15.far_el[new_el] = env->exception.vaddress;
|
|
||||||
qemu_log_mask(CPU_LOG_INT, "...with FAR 0x%" PRIx64 "\n",
|
|
||||||
env->cp15.far_el[new_el]);
|
|
||||||
/* fall through */
|
|
||||||
case EXCP_BKPT:
|
|
||||||
case EXCP_UDEF:
|
|
||||||
case EXCP_SWI:
|
|
||||||
case EXCP_HVC:
|
|
||||||
case EXCP_HYP_TRAP:
|
|
||||||
case EXCP_SMC:
|
|
||||||
env->cp15.esr_el[new_el] = env->exception.syndrome;
|
|
||||||
break;
|
|
||||||
case EXCP_IRQ:
|
|
||||||
case EXCP_VIRQ:
|
|
||||||
addr += 0x80;
|
|
||||||
break;
|
|
||||||
case EXCP_FIQ:
|
|
||||||
case EXCP_VFIQ:
|
|
||||||
addr += 0x100;
|
|
||||||
break;
|
|
||||||
case EXCP_SEMIHOST:
|
|
||||||
qemu_log_mask(CPU_LOG_INT,
|
|
||||||
"...handling as semihosting call 0x%" PRIx64 "\n",
|
|
||||||
env->xregs[0]);
|
|
||||||
env->xregs[0] = do_arm_semihosting(env);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_a64(env)) {
|
|
||||||
env->banked_spsr[aarch64_banked_spsr_index(new_el)] = pstate_read(env);
|
|
||||||
aarch64_save_sp(env, arm_current_el(env));
|
|
||||||
env->elr_el[new_el] = env->pc;
|
|
||||||
} else {
|
|
||||||
env->banked_spsr[aarch64_banked_spsr_index(new_el)] = cpsr_read(env);
|
|
||||||
if (!env->thumb) {
|
|
||||||
env->cp15.esr_el[new_el] |= 1 << 25;
|
|
||||||
}
|
|
||||||
env->elr_el[new_el] = env->regs[15];
|
|
||||||
|
|
||||||
aarch64_sync_32_to_64(env);
|
|
||||||
|
|
||||||
env->condexec_bits = 0;
|
|
||||||
}
|
|
||||||
qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n",
|
|
||||||
env->elr_el[new_el]);
|
|
||||||
|
|
||||||
pstate_write(env, PSTATE_DAIF | new_mode);
|
|
||||||
env->aarch64 = 1;
|
|
||||||
aarch64_restore_sp(env, new_el);
|
|
||||||
|
|
||||||
env->pc = addr;
|
|
||||||
|
|
||||||
qemu_log_mask(CPU_LOG_INT, "...to EL%d PC 0x%" PRIx64 " PSTATE 0x%x\n",
|
|
||||||
new_el, env->pc, pstate_read(env));
|
|
||||||
|
|
||||||
if (!kvm_enabled()) {
|
|
||||||
cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "arm_ldst.h"
|
#include "arm_ldst.h"
|
||||||
#include <zlib.h> /* For crc32 */
|
#include <zlib.h> /* For crc32 */
|
||||||
#include "exec/semihost.h"
|
#include "exec/semihost.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
|
||||||
#define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */
|
#define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */
|
||||||
|
|
||||||
|
@ -2890,6 +2891,17 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
tlb_flush(CPU(cpu), 1);
|
tlb_flush(CPU(cpu), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
|
{
|
||||||
|
if ((env->cp15.cptr_el[2] & CPTR_TFP) && arm_current_el(env) == 2) {
|
||||||
|
return CP_ACCESS_TRAP_EL2;
|
||||||
|
}
|
||||||
|
if (env->cp15.cptr_el[3] & CPTR_TFP) {
|
||||||
|
return CP_ACCESS_TRAP_EL3;
|
||||||
|
}
|
||||||
|
return CP_ACCESS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static const ARMCPRegInfo v8_cp_reginfo[] = {
|
static const ARMCPRegInfo v8_cp_reginfo[] = {
|
||||||
/* Minimal set of EL0-visible registers. This will need to be expanded
|
/* Minimal set of EL0-visible registers. This will need to be expanded
|
||||||
* significantly for system emulation of AArch64 CPUs.
|
* significantly for system emulation of AArch64 CPUs.
|
||||||
|
@ -3150,6 +3162,11 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
|
||||||
.opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 0,
|
.opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 0,
|
||||||
.type = ARM_CP_NO_RAW,
|
.type = ARM_CP_NO_RAW,
|
||||||
.access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write },
|
.access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write },
|
||||||
|
{ .name = "FPEXC32_EL2", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 3, .opc2 = 0,
|
||||||
|
.type = ARM_CP_ALIAS,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]),
|
||||||
|
.access = PL2_RW, .accessfn = fpexc32_access },
|
||||||
REGINFO_SENTINEL
|
REGINFO_SENTINEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5707,8 +5724,7 @@ void aarch64_sync_64_to_32(CPUARMState *env)
|
||||||
env->regs[15] = env->pc;
|
env->regs[15] = env->pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle a CPU exception. */
|
static void arm_cpu_do_interrupt_aarch32(CPUState *cs)
|
||||||
void arm_cpu_do_interrupt(CPUState *cs)
|
|
||||||
{
|
{
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
|
@ -5718,16 +5734,6 @@ void arm_cpu_do_interrupt(CPUState *cs)
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
uint32_t moe;
|
uint32_t moe;
|
||||||
|
|
||||||
assert(!IS_M(env));
|
|
||||||
|
|
||||||
arm_log_exception(cs->exception_index);
|
|
||||||
|
|
||||||
if (arm_is_psci_call(cpu, cs->exception_index)) {
|
|
||||||
arm_handle_psci_call(cpu);
|
|
||||||
qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this is a debug exception we must update the DBGDSCR.MOE bits */
|
/* If this is a debug exception we must update the DBGDSCR.MOE bits */
|
||||||
switch (env->exception.syndrome >> ARM_EL_EC_SHIFT) {
|
switch (env->exception.syndrome >> ARM_EL_EC_SHIFT) {
|
||||||
case EC_BREAKPOINT:
|
case EC_BREAKPOINT:
|
||||||
|
@ -5765,27 +5771,6 @@ void arm_cpu_do_interrupt(CPUState *cs)
|
||||||
offset = 4;
|
offset = 4;
|
||||||
break;
|
break;
|
||||||
case EXCP_SWI:
|
case EXCP_SWI:
|
||||||
if (semihosting_enabled()) {
|
|
||||||
/* Check for semihosting interrupt. */
|
|
||||||
if (env->thumb) {
|
|
||||||
mask = arm_lduw_code(env, env->regs[15] - 2, env->bswap_code)
|
|
||||||
& 0xff;
|
|
||||||
} else {
|
|
||||||
mask = arm_ldl_code(env, env->regs[15] - 4, env->bswap_code)
|
|
||||||
& 0xffffff;
|
|
||||||
}
|
|
||||||
/* Only intercept calls from privileged modes, to provide some
|
|
||||||
semblance of security. */
|
|
||||||
if (((mask == 0x123456 && !env->thumb)
|
|
||||||
|| (mask == 0xab && env->thumb))
|
|
||||||
&& (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
|
|
||||||
qemu_log_mask(CPU_LOG_INT,
|
|
||||||
"...handling as semihosting call 0x%x\n",
|
|
||||||
env->regs[0]);
|
|
||||||
env->regs[0] = do_arm_semihosting(env);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_mode = ARM_CPU_MODE_SVC;
|
new_mode = ARM_CPU_MODE_SVC;
|
||||||
addr = 0x08;
|
addr = 0x08;
|
||||||
mask = CPSR_I;
|
mask = CPSR_I;
|
||||||
|
@ -5793,19 +5778,6 @@ void arm_cpu_do_interrupt(CPUState *cs)
|
||||||
offset = 0;
|
offset = 0;
|
||||||
break;
|
break;
|
||||||
case EXCP_BKPT:
|
case EXCP_BKPT:
|
||||||
/* See if this is a semihosting syscall. */
|
|
||||||
if (env->thumb && semihosting_enabled()) {
|
|
||||||
mask = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff;
|
|
||||||
if (mask == 0xab
|
|
||||||
&& (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
|
|
||||||
env->regs[15] += 2;
|
|
||||||
qemu_log_mask(CPU_LOG_INT,
|
|
||||||
"...handling as semihosting call 0x%x\n",
|
|
||||||
env->regs[0]);
|
|
||||||
env->regs[0] = do_arm_semihosting(env);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env->exception.fsr = 2;
|
env->exception.fsr = 2;
|
||||||
/* Fall through to prefetch abort. */
|
/* Fall through to prefetch abort. */
|
||||||
case EXCP_PREFETCH_ABORT:
|
case EXCP_PREFETCH_ABORT:
|
||||||
|
@ -5899,9 +5871,227 @@ void arm_cpu_do_interrupt(CPUState *cs)
|
||||||
}
|
}
|
||||||
env->regs[14] = env->regs[15] + offset;
|
env->regs[14] = env->regs[15] + offset;
|
||||||
env->regs[15] = addr;
|
env->regs[15] = addr;
|
||||||
cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle exception entry to a target EL which is using AArch64 */
|
||||||
|
static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
|
||||||
|
{
|
||||||
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
unsigned int new_el = env->exception.target_el;
|
||||||
|
target_ulong addr = env->cp15.vbar_el[new_el];
|
||||||
|
unsigned int new_mode = aarch64_pstate_mode(new_el, true);
|
||||||
|
|
||||||
|
if (arm_current_el(env) < new_el) {
|
||||||
|
/* Entry vector offset depends on whether the implemented EL
|
||||||
|
* immediately lower than the target level is using AArch32 or AArch64
|
||||||
|
*/
|
||||||
|
bool is_aa64;
|
||||||
|
|
||||||
|
switch (new_el) {
|
||||||
|
case 3:
|
||||||
|
is_aa64 = (env->cp15.scr_el3 & SCR_RW) != 0;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
is_aa64 = (env->cp15.hcr_el2 & HCR_RW) != 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
is_aa64 = is_a64(env);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_aa64) {
|
||||||
|
addr += 0x400;
|
||||||
|
} else {
|
||||||
|
addr += 0x600;
|
||||||
|
}
|
||||||
|
} else if (pstate_read(env) & PSTATE_SP) {
|
||||||
|
addr += 0x200;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cs->exception_index) {
|
||||||
|
case EXCP_PREFETCH_ABORT:
|
||||||
|
case EXCP_DATA_ABORT:
|
||||||
|
env->cp15.far_el[new_el] = env->exception.vaddress;
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...with FAR 0x%" PRIx64 "\n",
|
||||||
|
env->cp15.far_el[new_el]);
|
||||||
|
/* fall through */
|
||||||
|
case EXCP_BKPT:
|
||||||
|
case EXCP_UDEF:
|
||||||
|
case EXCP_SWI:
|
||||||
|
case EXCP_HVC:
|
||||||
|
case EXCP_HYP_TRAP:
|
||||||
|
case EXCP_SMC:
|
||||||
|
env->cp15.esr_el[new_el] = env->exception.syndrome;
|
||||||
|
break;
|
||||||
|
case EXCP_IRQ:
|
||||||
|
case EXCP_VIRQ:
|
||||||
|
addr += 0x80;
|
||||||
|
break;
|
||||||
|
case EXCP_FIQ:
|
||||||
|
case EXCP_VFIQ:
|
||||||
|
addr += 0x100;
|
||||||
|
break;
|
||||||
|
case EXCP_SEMIHOST:
|
||||||
|
qemu_log_mask(CPU_LOG_INT,
|
||||||
|
"...handling as semihosting call 0x%" PRIx64 "\n",
|
||||||
|
env->xregs[0]);
|
||||||
|
env->xregs[0] = do_arm_semihosting(env);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_a64(env)) {
|
||||||
|
env->banked_spsr[aarch64_banked_spsr_index(new_el)] = pstate_read(env);
|
||||||
|
aarch64_save_sp(env, arm_current_el(env));
|
||||||
|
env->elr_el[new_el] = env->pc;
|
||||||
|
} else {
|
||||||
|
env->banked_spsr[aarch64_banked_spsr_index(new_el)] = cpsr_read(env);
|
||||||
|
if (!env->thumb) {
|
||||||
|
env->cp15.esr_el[new_el] |= 1 << 25;
|
||||||
|
}
|
||||||
|
env->elr_el[new_el] = env->regs[15];
|
||||||
|
|
||||||
|
aarch64_sync_32_to_64(env);
|
||||||
|
|
||||||
|
env->condexec_bits = 0;
|
||||||
|
}
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n",
|
||||||
|
env->elr_el[new_el]);
|
||||||
|
|
||||||
|
pstate_write(env, PSTATE_DAIF | new_mode);
|
||||||
|
env->aarch64 = 1;
|
||||||
|
aarch64_restore_sp(env, new_el);
|
||||||
|
|
||||||
|
env->pc = addr;
|
||||||
|
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...to EL%d PC 0x%" PRIx64 " PSTATE 0x%x\n",
|
||||||
|
new_el, env->pc, pstate_read(env));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool check_for_semihosting(CPUState *cs)
|
||||||
|
{
|
||||||
|
/* Check whether this exception is a semihosting call; if so
|
||||||
|
* then handle it and return true; otherwise return false.
|
||||||
|
*/
|
||||||
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
|
||||||
|
if (is_a64(env)) {
|
||||||
|
if (cs->exception_index == EXCP_SEMIHOST) {
|
||||||
|
/* This is always the 64-bit semihosting exception.
|
||||||
|
* The "is this usermode" and "is semihosting enabled"
|
||||||
|
* checks have been done at translate time.
|
||||||
|
*/
|
||||||
|
qemu_log_mask(CPU_LOG_INT,
|
||||||
|
"...handling as semihosting call 0x%" PRIx64 "\n",
|
||||||
|
env->xregs[0]);
|
||||||
|
env->xregs[0] = do_arm_semihosting(env);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
uint32_t imm;
|
||||||
|
|
||||||
|
/* Only intercept calls from privileged modes, to provide some
|
||||||
|
* semblance of security.
|
||||||
|
*/
|
||||||
|
if (!semihosting_enabled() ||
|
||||||
|
((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cs->exception_index) {
|
||||||
|
case EXCP_SWI:
|
||||||
|
/* Check for semihosting interrupt. */
|
||||||
|
if (env->thumb) {
|
||||||
|
imm = arm_lduw_code(env, env->regs[15] - 2, env->bswap_code)
|
||||||
|
& 0xff;
|
||||||
|
if (imm == 0xab) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imm = arm_ldl_code(env, env->regs[15] - 4, env->bswap_code)
|
||||||
|
& 0xffffff;
|
||||||
|
if (imm == 0x123456) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case EXCP_BKPT:
|
||||||
|
/* See if this is a semihosting syscall. */
|
||||||
|
if (env->thumb) {
|
||||||
|
imm = arm_lduw_code(env, env->regs[15], env->bswap_code)
|
||||||
|
& 0xff;
|
||||||
|
if (imm == 0xab) {
|
||||||
|
env->regs[15] += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_log_mask(CPU_LOG_INT,
|
||||||
|
"...handling as semihosting call 0x%x\n",
|
||||||
|
env->regs[0]);
|
||||||
|
env->regs[0] = do_arm_semihosting(env);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle a CPU exception for A and R profile CPUs.
|
||||||
|
* Do any appropriate logging, handle PSCI calls, and then hand off
|
||||||
|
* to the AArch64-entry or AArch32-entry function depending on the
|
||||||
|
* target exception level's register width.
|
||||||
|
*/
|
||||||
|
void arm_cpu_do_interrupt(CPUState *cs)
|
||||||
|
{
|
||||||
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
|
CPUARMState *env = &cpu->env;
|
||||||
|
unsigned int new_el = env->exception.target_el;
|
||||||
|
|
||||||
|
assert(!IS_M(env));
|
||||||
|
|
||||||
|
arm_log_exception(cs->exception_index);
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...from EL%d to EL%d\n", arm_current_el(env),
|
||||||
|
new_el);
|
||||||
|
if (qemu_loglevel_mask(CPU_LOG_INT)
|
||||||
|
&& !excp_is_internal(cs->exception_index)) {
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...with ESR %x/0x%" PRIx32 "\n",
|
||||||
|
env->exception.syndrome >> ARM_EL_EC_SHIFT,
|
||||||
|
env->exception.syndrome);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arm_is_psci_call(cpu, cs->exception_index)) {
|
||||||
|
arm_handle_psci_call(cpu);
|
||||||
|
qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Semihosting semantics depend on the register width of the
|
||||||
|
* code that caused the exception, not the target exception level,
|
||||||
|
* so must be handled here.
|
||||||
|
*/
|
||||||
|
if (check_for_semihosting(cs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!excp_is_internal(cs->exception_index));
|
||||||
|
if (arm_el_is_aa64(env, new_el)) {
|
||||||
|
arm_cpu_do_interrupt_aarch64(cs);
|
||||||
|
} else {
|
||||||
|
arm_cpu_do_interrupt_aarch32(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kvm_enabled()) {
|
||||||
|
cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Return the exception level which controls this address translation regime */
|
/* Return the exception level which controls this address translation regime */
|
||||||
static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx)
|
static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx)
|
||||||
|
@ -6273,13 +6463,15 @@ static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
MemTxAttrs attrs = {};
|
MemTxAttrs attrs = {};
|
||||||
|
AddressSpace *as;
|
||||||
|
|
||||||
attrs.secure = is_secure;
|
attrs.secure = is_secure;
|
||||||
|
as = arm_addressspace(cs, attrs);
|
||||||
addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fsr, fi);
|
addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fsr, fi);
|
||||||
if (fi->s1ptw) {
|
if (fi->s1ptw) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return address_space_ldl(cs->as, addr, attrs, NULL);
|
return address_space_ldl(as, addr, attrs, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
||||||
|
@ -6289,13 +6481,15 @@ static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure,
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
MemTxAttrs attrs = {};
|
MemTxAttrs attrs = {};
|
||||||
|
AddressSpace *as;
|
||||||
|
|
||||||
attrs.secure = is_secure;
|
attrs.secure = is_secure;
|
||||||
|
as = arm_addressspace(cs, attrs);
|
||||||
addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fsr, fi);
|
addr = S1_ptw_translate(env, mmu_idx, addr, attrs, fsr, fi);
|
||||||
if (fi->s1ptw) {
|
if (fi->s1ptw) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return address_space_ldq(cs->as, addr, attrs, NULL);
|
return address_space_ldq(as, addr, attrs, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
|
static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
|
||||||
|
@ -7346,7 +7540,8 @@ bool arm_tlb_fill(CPUState *cs, vaddr address,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
|
||||||
|
MemTxAttrs *attrs)
|
||||||
{
|
{
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
|
@ -7355,16 +7550,16 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
||||||
int prot;
|
int prot;
|
||||||
bool ret;
|
bool ret;
|
||||||
uint32_t fsr;
|
uint32_t fsr;
|
||||||
MemTxAttrs attrs = {};
|
|
||||||
ARMMMUFaultInfo fi = {};
|
ARMMMUFaultInfo fi = {};
|
||||||
|
|
||||||
|
*attrs = (MemTxAttrs) {};
|
||||||
|
|
||||||
ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env, false), &phys_addr,
|
ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env, false), &phys_addr,
|
||||||
&attrs, &prot, &page_size, &fsr, &fi);
|
attrs, &prot, &page_size, &fsr, &fi);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return phys_addr;
|
return phys_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -641,12 +641,51 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int el_from_spsr(uint32_t spsr)
|
||||||
|
{
|
||||||
|
/* Return the exception level that this SPSR is requesting a return to,
|
||||||
|
* or -1 if it is invalid (an illegal return)
|
||||||
|
*/
|
||||||
|
if (spsr & PSTATE_nRW) {
|
||||||
|
switch (spsr & CPSR_M) {
|
||||||
|
case ARM_CPU_MODE_USR:
|
||||||
|
return 0;
|
||||||
|
case ARM_CPU_MODE_HYP:
|
||||||
|
return 2;
|
||||||
|
case ARM_CPU_MODE_FIQ:
|
||||||
|
case ARM_CPU_MODE_IRQ:
|
||||||
|
case ARM_CPU_MODE_SVC:
|
||||||
|
case ARM_CPU_MODE_ABT:
|
||||||
|
case ARM_CPU_MODE_UND:
|
||||||
|
case ARM_CPU_MODE_SYS:
|
||||||
|
return 1;
|
||||||
|
case ARM_CPU_MODE_MON:
|
||||||
|
/* Returning to Mon from AArch64 is never possible,
|
||||||
|
* so this is an illegal return.
|
||||||
|
*/
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (extract32(spsr, 1, 1)) {
|
||||||
|
/* Return with reserved M[1] bit set */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (extract32(spsr, 0, 4) == 1) {
|
||||||
|
/* return to EL0 with M[0] bit set */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return extract32(spsr, 2, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HELPER(exception_return)(CPUARMState *env)
|
void HELPER(exception_return)(CPUARMState *env)
|
||||||
{
|
{
|
||||||
int cur_el = arm_current_el(env);
|
int cur_el = arm_current_el(env);
|
||||||
unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el);
|
unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el);
|
||||||
uint32_t spsr = env->banked_spsr[spsr_idx];
|
uint32_t spsr = env->banked_spsr[spsr_idx];
|
||||||
int new_el;
|
int new_el;
|
||||||
|
bool return_to_aa64 = (spsr & PSTATE_nRW) == 0;
|
||||||
|
|
||||||
aarch64_save_sp(env, cur_el);
|
aarch64_save_sp(env, cur_el);
|
||||||
|
|
||||||
|
@ -663,20 +702,10 @@ void HELPER(exception_return)(CPUARMState *env)
|
||||||
spsr &= ~PSTATE_SS;
|
spsr &= ~PSTATE_SS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spsr & PSTATE_nRW) {
|
new_el = el_from_spsr(spsr);
|
||||||
/* TODO: We currently assume EL1/2/3 are running in AArch64. */
|
if (new_el == -1) {
|
||||||
env->aarch64 = 0;
|
goto illegal_return;
|
||||||
new_el = 0;
|
|
||||||
env->uncached_cpsr = 0x10;
|
|
||||||
cpsr_write(env, spsr, ~0);
|
|
||||||
if (!arm_singlestep_active(env)) {
|
|
||||||
env->uncached_cpsr &= ~PSTATE_SS;
|
|
||||||
}
|
}
|
||||||
aarch64_sync_64_to_32(env);
|
|
||||||
|
|
||||||
env->regs[15] = env->elr_el[1] & ~0x1;
|
|
||||||
} else {
|
|
||||||
new_el = extract32(spsr, 2, 2);
|
|
||||||
if (new_el > cur_el
|
if (new_el > cur_el
|
||||||
|| (new_el == 2 && !arm_feature(env, ARM_FEATURE_EL2))) {
|
|| (new_el == 2 && !arm_feature(env, ARM_FEATURE_EL2))) {
|
||||||
/* Disallow return to an EL which is unimplemented or higher
|
/* Disallow return to an EL which is unimplemented or higher
|
||||||
|
@ -684,14 +713,37 @@ void HELPER(exception_return)(CPUARMState *env)
|
||||||
*/
|
*/
|
||||||
goto illegal_return;
|
goto illegal_return;
|
||||||
}
|
}
|
||||||
if (extract32(spsr, 1, 1)) {
|
|
||||||
/* Return with reserved M[1] bit set */
|
if (new_el != 0 && arm_el_is_aa64(env, new_el) != return_to_aa64) {
|
||||||
|
/* Return to an EL which is configured for a different register width */
|
||||||
goto illegal_return;
|
goto illegal_return;
|
||||||
}
|
}
|
||||||
if (new_el == 0 && (spsr & PSTATE_SP)) {
|
|
||||||
/* Return to EL0 with M[0] bit set */
|
if (new_el == 2 && arm_is_secure_below_el3(env)) {
|
||||||
|
/* Return to the non-existent secure-EL2 */
|
||||||
goto illegal_return;
|
goto illegal_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (new_el == 1 && (env->cp15.hcr_el2 & HCR_TGE)
|
||||||
|
&& !arm_is_secure_below_el3(env)) {
|
||||||
|
goto illegal_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!return_to_aa64) {
|
||||||
|
env->aarch64 = 0;
|
||||||
|
env->uncached_cpsr = spsr & CPSR_M;
|
||||||
|
cpsr_write(env, spsr, ~0);
|
||||||
|
if (!arm_singlestep_active(env)) {
|
||||||
|
env->uncached_cpsr &= ~PSTATE_SS;
|
||||||
|
}
|
||||||
|
aarch64_sync_64_to_32(env);
|
||||||
|
|
||||||
|
if (spsr & CPSR_T) {
|
||||||
|
env->regs[15] = env->elr_el[cur_el] & ~0x1;
|
||||||
|
} else {
|
||||||
|
env->regs[15] = env->elr_el[cur_el] & ~0x3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
env->aarch64 = 1;
|
env->aarch64 = 1;
|
||||||
pstate_write(env, spsr);
|
pstate_write(env, spsr);
|
||||||
if (!arm_singlestep_active(env)) {
|
if (!arm_singlestep_active(env)) {
|
||||||
|
|
|
@ -2861,9 +2861,10 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
if (tcg_enabled()) {
|
if (tcg_enabled()) {
|
||||||
|
AddressSpace *newas = g_new(AddressSpace, 1);
|
||||||
|
|
||||||
cpu->cpu_as_mem = g_new(MemoryRegion, 1);
|
cpu->cpu_as_mem = g_new(MemoryRegion, 1);
|
||||||
cpu->cpu_as_root = g_new(MemoryRegion, 1);
|
cpu->cpu_as_root = g_new(MemoryRegion, 1);
|
||||||
cs->as = g_new(AddressSpace, 1);
|
|
||||||
|
|
||||||
/* Outer container... */
|
/* Outer container... */
|
||||||
memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull);
|
memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull);
|
||||||
|
@ -2876,7 +2877,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||||
get_system_memory(), 0, ~0ull);
|
get_system_memory(), 0, ~0ull);
|
||||||
memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->cpu_as_mem, 0);
|
memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->cpu_as_mem, 0);
|
||||||
memory_region_set_enabled(cpu->cpu_as_mem, true);
|
memory_region_set_enabled(cpu->cpu_as_mem, true);
|
||||||
address_space_init(cs->as, cpu->cpu_as_root, "CPU");
|
address_space_init(newas, cpu->cpu_as_root, "CPU");
|
||||||
|
cs->num_ases = 1;
|
||||||
|
cpu_address_space_init(cs, newas, 0);
|
||||||
|
|
||||||
/* ... SMRAM with higher priority, linked from /machine/smram. */
|
/* ... SMRAM with higher priority, linked from /machine/smram. */
|
||||||
cpu->machine_done.notify = x86_cpu_machine_done;
|
cpu->machine_done.notify = x86_cpu_machine_done;
|
||||||
|
|
Loading…
Reference in New Issue