target-arm queue:
* fixes for various Coverity-spotted bugs * support new KVM device control API for VGIC * support KVM VGIC save/restore/migration * more AArch64 system mode foundations * support ARMv8 CRC instructions for A32/T32 * PL330 minor fixes and cleanup -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABCAAGBQJTDimkAAoJEDwlJe0UNgzeJckQAIgv++tjcGk9d5gl0QQMyHJJ GNb0raWPHSvVqsuGJzdzgfd5dt0FN3fvqKs4ThtstLjcmkiIqDoIaDuDDFdU/Zt0 YAMA07cQ7xZNoVB8x8zqMD/yrLV99CKZlC5/Cr4yq8SPNrOKZkpnFViSC2wgrrDt JyCRjc6GIv3SofkOP8sCF3Fg7JMi0J9qFCCw2fGWrRBq1TEMYlaCn5rSO4O9jiW3 g8YMVUeYFjKkLUnoNOA93NQhhkrQNl5xkXmCo+JF7TPL9w2lH/G76RWmLtaPGLuh IkwfG+7/AVbtoK8Njp9scsmu56hau1RkNip/yquLM1ONb2H7h94AkpRCGQXOXBW3 rEocDKR3cR7quvLHtQ54blR5/TJMhFZfchX5cvGYT4pH2+8yxyP0UeFpNVJawoLO Bb1zYTswjXfAfe0jk/nr91P5lSNa6lkk42mh7k7Shc7bvCjkaLQDbNRDBB1B6gLr 0b0vj73U8+Gz+5WoWbu0GJhuR5NdWxnAiuTYXEJ8roTaZL2AP6fuef4OZ+Nnpw/K dBRoO6NWrKT04ZaYu6tB/q31sa6ff6S+hRpqr8lp/S8PGFWC0Pni8Pz0rYLpyhVh yZRqkY7C6FRE5EmXSWt0ZkpSkykwnibL/xXigHIvmGEBiohR0f4SGSIDLqTGkYqQ HWtue+3jyM/1kirp2QLx =1X50 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20140226' into staging target-arm queue: * fixes for various Coverity-spotted bugs * support new KVM device control API for VGIC * support KVM VGIC save/restore/migration * more AArch64 system mode foundations * support ARMv8 CRC instructions for A32/T32 * PL330 minor fixes and cleanup # gpg: Signature made Wed 26 Feb 2014 17:51:32 GMT using RSA key ID 14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" * remotes/pmaydell/tags/pull-target-arm-20140226: (45 commits) dma/pl330: implement dmaadnh instruction dma/pl330: Fix buffer depth dma/pl330: Add event debugging printfs dma/pl330: Rename parent_obj dma/pl330: printf format type sweep. dma/pl330: Fix misleading type dma/pl330: Delete overly verbose debug printf target-arm: Add support for AArch32 ARMv8 CRC32 instructions include/qemu/crc32c.h: Rename include guards to match filename target-arm: Add utility function for checking AA32/64 state of an EL target-arm: Implement AArch64 view of CPACR target-arm: A64: Implement MSR (immediate) instructions target-arm: Store AIF bits in env->pstate for AArch32 target-arm: A64: Implement WFI target-arm: Get MMU index information correct for A64 code target-arm: Implement AArch64 OSLAR_EL1 sysreg as WI target-arm: Implement AArch64 dummy breakpoint and watchpoint registers target-arm: Implement AArch64 ID and feature registers target-arm: Implement AArch64 generic timers target-arm: Implement AArch64 MPIDR ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
2ce5868ca1
2
configure
vendored
2
configure
vendored
|
@ -1657,7 +1657,7 @@ EOF
|
||||||
"Make sure to have the zlib libs and headers installed."
|
"Make sure to have the zlib libs and headers installed."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
libs_softmmu="$libs_softmmu -lz"
|
LIBS="$LIBS -lz"
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# libseccomp check
|
# libseccomp check
|
||||||
|
|
|
@ -477,7 +477,7 @@ int cpu_exec(CPUArchState *env)
|
||||||
}
|
}
|
||||||
#elif defined(TARGET_ARM)
|
#elif defined(TARGET_ARM)
|
||||||
if (interrupt_request & CPU_INTERRUPT_FIQ
|
if (interrupt_request & CPU_INTERRUPT_FIQ
|
||||||
&& !(env->uncached_cpsr & CPSR_F)) {
|
&& !(env->daif & PSTATE_F)) {
|
||||||
env->exception_index = EXCP_FIQ;
|
env->exception_index = EXCP_FIQ;
|
||||||
cc->do_interrupt(cpu);
|
cc->do_interrupt(cpu);
|
||||||
next_tb = 0;
|
next_tb = 0;
|
||||||
|
@ -493,7 +493,7 @@ int cpu_exec(CPUArchState *env)
|
||||||
pc contains a magic address. */
|
pc contains a magic address. */
|
||||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||||
&& ((IS_M(env) && env->regs[15] < 0xfffffff0)
|
&& ((IS_M(env) && env->regs[15] < 0xfffffff0)
|
||||||
|| !(env->uncached_cpsr & CPSR_I))) {
|
|| !(env->daif & PSTATE_I))) {
|
||||||
env->exception_index = EXCP_IRQ;
|
env->exception_index = EXCP_IRQ;
|
||||||
cc->do_interrupt(cpu);
|
cc->do_interrupt(cpu);
|
||||||
next_tb = 0;
|
next_tb = 0;
|
||||||
|
|
|
@ -92,8 +92,6 @@
|
||||||
#define MP_ETH_CRDP3 0x4AC
|
#define MP_ETH_CRDP3 0x4AC
|
||||||
#define MP_ETH_CTDP0 0x4E0
|
#define MP_ETH_CTDP0 0x4E0
|
||||||
#define MP_ETH_CTDP1 0x4E4
|
#define MP_ETH_CTDP1 0x4E4
|
||||||
#define MP_ETH_CTDP2 0x4E8
|
|
||||||
#define MP_ETH_CTDP3 0x4EC
|
|
||||||
|
|
||||||
/* MII PHY access */
|
/* MII PHY access */
|
||||||
#define MP_ETH_SMIR_DATA 0x0000FFFF
|
#define MP_ETH_SMIR_DATA 0x0000FFFF
|
||||||
|
@ -308,7 +306,7 @@ static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
|
||||||
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
|
case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
|
||||||
return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
|
return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
|
||||||
|
|
||||||
case MP_ETH_CTDP0 ... MP_ETH_CTDP3:
|
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
|
||||||
return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
|
return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -362,7 +360,7 @@ static void mv88w8618_eth_write(void *opaque, hwaddr offset,
|
||||||
s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
|
s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MP_ETH_CTDP0 ... MP_ETH_CTDP3:
|
case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
|
||||||
s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
|
s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,11 +272,11 @@ static void pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
goto message;
|
goto message;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
s->cpu->env.uncached_cpsr =
|
s->cpu->env.uncached_cpsr = ARM_CPU_MODE_SVC;
|
||||||
ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
|
s->cpu->env.daif = PSTATE_A | PSTATE_F | PSTATE_I;
|
||||||
s->cpu->env.cp15.c1_sys = 0;
|
s->cpu->env.cp15.c1_sys = 0;
|
||||||
s->cpu->env.cp15.c1_coproc = 0;
|
s->cpu->env.cp15.c1_coproc = 0;
|
||||||
s->cpu->env.cp15.c2_base0 = 0;
|
s->cpu->env.cp15.ttbr0_el1 = 0;
|
||||||
s->cpu->env.cp15.c3 = 0;
|
s->cpu->env.cp15.c3 = 0;
|
||||||
s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
|
s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
|
||||||
s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
|
s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
|
||||||
|
|
|
@ -227,7 +227,8 @@ static const VMStateDescription vmstate_pl330_queue = {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PL330State {
|
struct PL330State {
|
||||||
SysBusDevice busdev;
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
MemoryRegion iomem;
|
MemoryRegion iomem;
|
||||||
qemu_irq irq_abort;
|
qemu_irq irq_abort;
|
||||||
qemu_irq *irq;
|
qemu_irq *irq;
|
||||||
|
@ -577,7 +578,7 @@ static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag)
|
||||||
|
|
||||||
static inline void pl330_fault(PL330Chan *ch, uint32_t flags)
|
static inline void pl330_fault(PL330Chan *ch, uint32_t flags)
|
||||||
{
|
{
|
||||||
DB_PRINT("ch: %p, flags: %x\n", ch, flags);
|
DB_PRINT("ch: %p, flags: %" PRIx32 "\n", ch, flags);
|
||||||
ch->fault_type |= flags;
|
ch->fault_type |= flags;
|
||||||
if (ch->state == pl330_chan_fault) {
|
if (ch->state == pl330_chan_fault) {
|
||||||
return;
|
return;
|
||||||
|
@ -600,10 +601,12 @@ static inline void pl330_fault(PL330Chan *ch, uint32_t flags)
|
||||||
* LEN - number of elements in ARGS array
|
* LEN - number of elements in ARGS array
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
|
static void pl330_dmaadxh(PL330Chan *ch, uint8_t *args, bool ra, bool neg)
|
||||||
{
|
{
|
||||||
uint16_t im = (((uint16_t)args[1]) << 8) | ((uint16_t)args[0]);
|
uint32_t im = (args[1] << 8) | args[0];
|
||||||
uint8_t ra = (opcode >> 1) & 1;
|
if (neg) {
|
||||||
|
im |= 0xffffu << 16;
|
||||||
|
}
|
||||||
|
|
||||||
if (ch->is_manager) {
|
if (ch->is_manager) {
|
||||||
pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
|
pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
|
||||||
|
@ -616,6 +619,16 @@ static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
|
||||||
|
{
|
||||||
|
pl330_dmaadxh(ch, args, extract32(opcode, 1, 1), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pl330_dmaadnh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
|
||||||
|
{
|
||||||
|
pl330_dmaadxh(ch, args, extract32(opcode, 1, 1), true);
|
||||||
|
}
|
||||||
|
|
||||||
static void pl330_dmaend(PL330Chan *ch, uint8_t opcode,
|
static void pl330_dmaend(PL330Chan *ch, uint8_t opcode,
|
||||||
uint8_t *args, int len)
|
uint8_t *args, int len)
|
||||||
{
|
{
|
||||||
|
@ -723,7 +736,8 @@ static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
|
||||||
ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src,
|
ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src,
|
||||||
size, num, inc, 0, ch->tag);
|
size, num, inc, 0, ch->tag);
|
||||||
if (!ch->stall) {
|
if (!ch->stall) {
|
||||||
DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n",
|
DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32
|
||||||
|
" num:%" PRId32 " %c\n",
|
||||||
ch->tag, ch->src, size, num, inc ? 'Y' : 'N');
|
ch->tag, ch->src, size, num, inc ? 'Y' : 'N');
|
||||||
ch->src += inc ? size * num - (ch->src & (size - 1)) : 0;
|
ch->src += inc ? size * num - (ch->src & (size - 1)) : 0;
|
||||||
}
|
}
|
||||||
|
@ -868,9 +882,10 @@ static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
|
||||||
}
|
}
|
||||||
if (ch->parent->inten & (1 << ev_id)) {
|
if (ch->parent->inten & (1 << ev_id)) {
|
||||||
ch->parent->int_status |= (1 << ev_id);
|
ch->parent->int_status |= (1 << ev_id);
|
||||||
DB_PRINT("event interrupt raised %d\n", ev_id);
|
DB_PRINT("event interrupt raised %" PRId8 "\n", ev_id);
|
||||||
qemu_irq_raise(ch->parent->irq[ev_id]);
|
qemu_irq_raise(ch->parent->irq[ev_id]);
|
||||||
}
|
}
|
||||||
|
DB_PRINT("event raised %" PRId8 "\n", ev_id);
|
||||||
ch->parent->ev_status |= (1 << ev_id);
|
ch->parent->ev_status |= (1 << ev_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -895,7 +910,8 @@ static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
|
||||||
ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst,
|
ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst,
|
||||||
size, num, inc, 0, ch->tag);
|
size, num, inc, 0, ch->tag);
|
||||||
if (!ch->stall) {
|
if (!ch->stall) {
|
||||||
DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n",
|
DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32
|
||||||
|
" num:%" PRId32 " %c\n",
|
||||||
ch->tag, ch->dst, size, num, inc ? 'Y' : 'N');
|
ch->tag, ch->dst, size, num, inc ? 'Y' : 'N');
|
||||||
ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0;
|
ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0;
|
||||||
}
|
}
|
||||||
|
@ -972,6 +988,7 @@ static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ch->parent->ev_status &= ~(1 << ev_id);
|
ch->parent->ev_status &= ~(1 << ev_id);
|
||||||
|
DB_PRINT("event lowered %" PRIx8 "\n", ev_id);
|
||||||
} else {
|
} else {
|
||||||
ch->stall = 1;
|
ch->stall = 1;
|
||||||
}
|
}
|
||||||
|
@ -1037,6 +1054,7 @@ static void pl330_dmawmb(PL330Chan *ch, uint8_t opcode,
|
||||||
/* NULL terminated array of the instruction descriptions. */
|
/* NULL terminated array of the instruction descriptions. */
|
||||||
static const PL330InsnDesc insn_desc[] = {
|
static const PL330InsnDesc insn_desc[] = {
|
||||||
{ .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, },
|
{ .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, },
|
||||||
|
{ .opcode = 0x5c, .opmask = 0xFD, .size = 3, .exec = pl330_dmaadnh, },
|
||||||
{ .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, },
|
{ .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, },
|
||||||
{ .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, },
|
{ .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, },
|
||||||
{ .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, },
|
{ .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, },
|
||||||
|
@ -1108,7 +1126,6 @@ static int pl330_chan_exec(PL330Chan *ch)
|
||||||
ch->state != pl330_chan_waiting_periph &&
|
ch->state != pl330_chan_waiting_periph &&
|
||||||
ch->state != pl330_chan_at_barrier &&
|
ch->state != pl330_chan_at_barrier &&
|
||||||
ch->state != pl330_chan_waiting_event) {
|
ch->state != pl330_chan_waiting_event) {
|
||||||
DB_PRINT("%d\n", ch->state);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ch->stall = 0;
|
ch->stall = 0;
|
||||||
|
@ -1155,7 +1172,7 @@ static int pl330_exec_cycle(PL330Chan *channel)
|
||||||
|
|
||||||
dma_memory_read(&address_space_memory, q->addr, buf, len);
|
dma_memory_read(&address_space_memory, q->addr, buf, len);
|
||||||
if (PL330_ERR_DEBUG > 1) {
|
if (PL330_ERR_DEBUG > 1) {
|
||||||
DB_PRINT("PL330 read from memory @%08x (size = %08x):\n",
|
DB_PRINT("PL330 read from memory @%08" PRIx32 " (size = %08x):\n",
|
||||||
q->addr, len);
|
q->addr, len);
|
||||||
qemu_hexdump((char *)buf, stderr, "", len);
|
qemu_hexdump((char *)buf, stderr, "", len);
|
||||||
}
|
}
|
||||||
|
@ -1187,8 +1204,8 @@ static int pl330_exec_cycle(PL330Chan *channel)
|
||||||
if (fifo_res == PL330_FIFO_OK || q->z) {
|
if (fifo_res == PL330_FIFO_OK || q->z) {
|
||||||
dma_memory_write(&address_space_memory, q->addr, buf, len);
|
dma_memory_write(&address_space_memory, q->addr, buf, len);
|
||||||
if (PL330_ERR_DEBUG > 1) {
|
if (PL330_ERR_DEBUG > 1) {
|
||||||
DB_PRINT("PL330 read from memory @%08x (size = %08x):\n",
|
DB_PRINT("PL330 read from memory @%08" PRIx32
|
||||||
q->addr, len);
|
" (size = %08x):\n", q->addr, len);
|
||||||
qemu_hexdump((char *)buf, stderr, "", len);
|
qemu_hexdump((char *)buf, stderr, "", len);
|
||||||
}
|
}
|
||||||
if (q->inc) {
|
if (q->inc) {
|
||||||
|
@ -1277,7 +1294,7 @@ static void pl330_debug_exec(PL330State *s)
|
||||||
args[2] = (s->dbg[1] >> 8) & 0xff;
|
args[2] = (s->dbg[1] >> 8) & 0xff;
|
||||||
args[3] = (s->dbg[1] >> 16) & 0xff;
|
args[3] = (s->dbg[1] >> 16) & 0xff;
|
||||||
args[4] = (s->dbg[1] >> 24) & 0xff;
|
args[4] = (s->dbg[1] >> 24) & 0xff;
|
||||||
DB_PRINT("chan id: %d\n", chan_id);
|
DB_PRINT("chan id: %" PRIx8 "\n", chan_id);
|
||||||
if (s->dbg[0] & 1) {
|
if (s->dbg[0] & 1) {
|
||||||
ch = &s->chan[chan_id];
|
ch = &s->chan[chan_id];
|
||||||
} else {
|
} else {
|
||||||
|
@ -1311,7 +1328,7 @@ static void pl330_iomem_write(void *opaque, hwaddr offset,
|
||||||
uint64_t value, unsigned size)
|
uint64_t value, unsigned size)
|
||||||
{
|
{
|
||||||
PL330State *s = (PL330State *) opaque;
|
PL330State *s = (PL330State *) opaque;
|
||||||
uint32_t i;
|
int i;
|
||||||
|
|
||||||
DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value);
|
DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value);
|
||||||
|
|
||||||
|
@ -1467,8 +1484,8 @@ static inline uint32_t pl330_iomem_read_imp(void *opaque,
|
||||||
static uint64_t pl330_iomem_read(void *opaque, hwaddr offset,
|
static uint64_t pl330_iomem_read(void *opaque, hwaddr offset,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
int ret = pl330_iomem_read_imp(opaque, offset);
|
uint32_t ret = pl330_iomem_read_imp(opaque, offset);
|
||||||
DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, ret);
|
DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1554,7 +1571,7 @@ static void pl330_realize(DeviceState *dev, Error **errp)
|
||||||
s->cfg[1] |= 5;
|
s->cfg[1] |= 5;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_setg(errp, "Bad value for i-cache_len property: %d\n",
|
error_setg(errp, "Bad value for i-cache_len property: %" PRIx8 "\n",
|
||||||
s->i_cache_len);
|
s->i_cache_len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1589,7 +1606,7 @@ static void pl330_realize(DeviceState *dev, Error **errp)
|
||||||
s->cfg[CFG_CRD] |= 0x4;
|
s->cfg[CFG_CRD] |= 0x4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error_setg(errp, "Bad value for data_width property: %d\n",
|
error_setg(errp, "Bad value for data_width property: %" PRIx8 "\n",
|
||||||
s->data_width);
|
s->data_width);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1602,7 +1619,7 @@ static void pl330_realize(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
pl330_queue_init(&s->read_queue, s->rd_q_dep, s);
|
pl330_queue_init(&s->read_queue, s->rd_q_dep, s);
|
||||||
pl330_queue_init(&s->write_queue, s->wr_q_dep, s);
|
pl330_queue_init(&s->write_queue, s->wr_q_dep, s);
|
||||||
pl330_fifo_init(&s->fifo, s->data_buffer_dep);
|
pl330_fifo_init(&s->fifo, s->data_width / 4 * s->data_buffer_dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Property pl330_properties[] = {
|
static Property pl330_properties[] = {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (c) 2012 Linaro Limited
|
* Copyright (c) 2012 Linaro Limited
|
||||||
* Written by Peter Maydell
|
* Written by Peter Maydell
|
||||||
|
* Save/Restore logic added by Christoffer Dall.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -23,6 +24,20 @@
|
||||||
#include "kvm_arm.h"
|
#include "kvm_arm.h"
|
||||||
#include "gic_internal.h"
|
#include "gic_internal.h"
|
||||||
|
|
||||||
|
//#define DEBUG_GIC_KVM
|
||||||
|
|
||||||
|
#ifdef DEBUG_GIC_KVM
|
||||||
|
static const int debug_gic_kvm = 1;
|
||||||
|
#else
|
||||||
|
static const int debug_gic_kvm = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DPRINTF(fmt, ...) do { \
|
||||||
|
if (debug_gic_kvm) { \
|
||||||
|
printf("arm_gic: " fmt , ## __VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TYPE_KVM_ARM_GIC "kvm-arm-gic"
|
#define TYPE_KVM_ARM_GIC "kvm-arm-gic"
|
||||||
#define KVM_ARM_GIC(obj) \
|
#define KVM_ARM_GIC(obj) \
|
||||||
OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC)
|
OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC)
|
||||||
|
@ -72,14 +87,419 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
|
||||||
kvm_set_irq(kvm_state, kvm_irq, !!level);
|
kvm_set_irq(kvm_state, kvm_irq, !!level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool kvm_arm_gic_can_save_restore(GICState *s)
|
||||||
|
{
|
||||||
|
return s->dev_fd >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_gic_access(GICState *s, int group, int offset,
|
||||||
|
int cpu, uint32_t *val, bool write)
|
||||||
|
{
|
||||||
|
struct kvm_device_attr attr;
|
||||||
|
int type;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
cpu = cpu & 0xff;
|
||||||
|
|
||||||
|
attr.flags = 0;
|
||||||
|
attr.group = group;
|
||||||
|
attr.attr = (((uint64_t)cpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) &
|
||||||
|
KVM_DEV_ARM_VGIC_CPUID_MASK) |
|
||||||
|
(((uint64_t)offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) &
|
||||||
|
KVM_DEV_ARM_VGIC_OFFSET_MASK);
|
||||||
|
attr.addr = (uintptr_t)val;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
type = KVM_SET_DEVICE_ATTR;
|
||||||
|
} else {
|
||||||
|
type = KVM_GET_DEVICE_ATTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kvm_device_ioctl(s->dev_fd, type, &attr);
|
||||||
|
if (err < 0) {
|
||||||
|
fprintf(stderr, "KVM_{SET/GET}_DEVICE_ATTR failed: %s\n",
|
||||||
|
strerror(-err));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_gicd_access(GICState *s, int offset, int cpu,
|
||||||
|
uint32_t *val, bool write)
|
||||||
|
{
|
||||||
|
kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||||
|
offset, cpu, val, write);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_gicc_access(GICState *s, int offset, int cpu,
|
||||||
|
uint32_t *val, bool write)
|
||||||
|
{
|
||||||
|
kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
|
||||||
|
offset, cpu, val, write);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define for_each_irq_reg(_ctr, _max_irq, _field_width) \
|
||||||
|
for (_ctr = 0; _ctr < ((_max_irq) / (32 / (_field_width))); _ctr++)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Translate from the in-kernel field for an IRQ value to/from the qemu
|
||||||
|
* representation.
|
||||||
|
*/
|
||||||
|
typedef void (*vgic_translate_fn)(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel);
|
||||||
|
|
||||||
|
/* synthetic translate function used for clear/set registers to completely
|
||||||
|
* clear a setting using a clear-register before setting the remaing bits
|
||||||
|
* using a set-register */
|
||||||
|
static void translate_clear(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel)
|
||||||
|
{
|
||||||
|
if (to_kernel) {
|
||||||
|
*field = ~0;
|
||||||
|
} else {
|
||||||
|
/* does not make sense: qemu model doesn't use set/clear regs */
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void translate_enabled(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel)
|
||||||
|
{
|
||||||
|
int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
|
||||||
|
|
||||||
|
if (to_kernel) {
|
||||||
|
*field = GIC_TEST_ENABLED(irq, cm);
|
||||||
|
} else {
|
||||||
|
if (*field & 1) {
|
||||||
|
GIC_SET_ENABLED(irq, cm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void translate_pending(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel)
|
||||||
|
{
|
||||||
|
int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
|
||||||
|
|
||||||
|
if (to_kernel) {
|
||||||
|
*field = gic_test_pending(s, irq, cm);
|
||||||
|
} else {
|
||||||
|
if (*field & 1) {
|
||||||
|
GIC_SET_PENDING(irq, cm);
|
||||||
|
/* TODO: Capture is level-line is held high in the kernel */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void translate_active(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel)
|
||||||
|
{
|
||||||
|
int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
|
||||||
|
|
||||||
|
if (to_kernel) {
|
||||||
|
*field = GIC_TEST_ACTIVE(irq, cm);
|
||||||
|
} else {
|
||||||
|
if (*field & 1) {
|
||||||
|
GIC_SET_ACTIVE(irq, cm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void translate_trigger(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel)
|
||||||
|
{
|
||||||
|
if (to_kernel) {
|
||||||
|
*field = (GIC_TEST_EDGE_TRIGGER(irq)) ? 0x2 : 0x0;
|
||||||
|
} else {
|
||||||
|
if (*field & 0x2) {
|
||||||
|
GIC_SET_EDGE_TRIGGER(irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void translate_priority(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel)
|
||||||
|
{
|
||||||
|
if (to_kernel) {
|
||||||
|
*field = GIC_GET_PRIORITY(irq, cpu) & 0xff;
|
||||||
|
} else {
|
||||||
|
gic_set_priority(s, cpu, irq, *field & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void translate_targets(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel)
|
||||||
|
{
|
||||||
|
if (to_kernel) {
|
||||||
|
*field = s->irq_target[irq] & 0xff;
|
||||||
|
} else {
|
||||||
|
s->irq_target[irq] = *field & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void translate_sgisource(GICState *s, int irq, int cpu,
|
||||||
|
uint32_t *field, bool to_kernel)
|
||||||
|
{
|
||||||
|
if (to_kernel) {
|
||||||
|
*field = s->sgi_pending[irq][cpu] & 0xff;
|
||||||
|
} else {
|
||||||
|
s->sgi_pending[irq][cpu] = *field & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a register group from the kernel VGIC */
|
||||||
|
static void kvm_dist_get(GICState *s, uint32_t offset, int width,
|
||||||
|
int maxirq, vgic_translate_fn translate_fn)
|
||||||
|
{
|
||||||
|
uint32_t reg;
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
int irq;
|
||||||
|
int cpu;
|
||||||
|
int regsz = 32 / width; /* irqs per kernel register */
|
||||||
|
uint32_t field;
|
||||||
|
|
||||||
|
for_each_irq_reg(i, maxirq, width) {
|
||||||
|
irq = i * regsz;
|
||||||
|
cpu = 0;
|
||||||
|
while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) {
|
||||||
|
kvm_gicd_access(s, offset, cpu, ®, false);
|
||||||
|
for (j = 0; j < regsz; j++) {
|
||||||
|
field = extract32(reg, j * width, width);
|
||||||
|
translate_fn(s, irq + j, cpu, &field, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu++;
|
||||||
|
}
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a register group to the kernel VGIC */
|
||||||
|
static void kvm_dist_put(GICState *s, uint32_t offset, int width,
|
||||||
|
int maxirq, vgic_translate_fn translate_fn)
|
||||||
|
{
|
||||||
|
uint32_t reg;
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
int irq;
|
||||||
|
int cpu;
|
||||||
|
int regsz = 32 / width; /* irqs per kernel register */
|
||||||
|
uint32_t field;
|
||||||
|
|
||||||
|
for_each_irq_reg(i, maxirq, width) {
|
||||||
|
irq = i * regsz;
|
||||||
|
cpu = 0;
|
||||||
|
while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) {
|
||||||
|
reg = 0;
|
||||||
|
for (j = 0; j < regsz; j++) {
|
||||||
|
translate_fn(s, irq + j, cpu, &field, true);
|
||||||
|
reg = deposit32(reg, j * width, width, field);
|
||||||
|
}
|
||||||
|
kvm_gicd_access(s, offset, cpu, ®, true);
|
||||||
|
|
||||||
|
cpu++;
|
||||||
|
}
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void kvm_arm_gic_put(GICState *s)
|
static void kvm_arm_gic_put(GICState *s)
|
||||||
{
|
{
|
||||||
/* TODO: there isn't currently a kernel interface to set the GIC state */
|
uint32_t reg;
|
||||||
|
int i;
|
||||||
|
int cpu;
|
||||||
|
int num_cpu;
|
||||||
|
int num_irq;
|
||||||
|
|
||||||
|
if (!kvm_arm_gic_can_save_restore(s)) {
|
||||||
|
DPRINTF("Cannot put kernel gic state, no kernel interface");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: We do the restore in a slightly different order than the save
|
||||||
|
* (where the order doesn't matter and is simply ordered according to the
|
||||||
|
* register offset values */
|
||||||
|
|
||||||
|
/*****************************************************************
|
||||||
|
* Distributor State
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* s->enabled -> GICD_CTLR */
|
||||||
|
reg = s->enabled;
|
||||||
|
kvm_gicd_access(s, 0x0, 0, ®, true);
|
||||||
|
|
||||||
|
/* Sanity checking on GICD_TYPER and s->num_irq, s->num_cpu */
|
||||||
|
kvm_gicd_access(s, 0x4, 0, ®, false);
|
||||||
|
num_irq = ((reg & 0x1f) + 1) * 32;
|
||||||
|
num_cpu = ((reg & 0xe0) >> 5) + 1;
|
||||||
|
|
||||||
|
if (num_irq < s->num_irq) {
|
||||||
|
fprintf(stderr, "Restoring %u IRQs, but kernel supports max %d\n",
|
||||||
|
s->num_irq, num_irq);
|
||||||
|
abort();
|
||||||
|
} else if (num_cpu != s->num_cpu) {
|
||||||
|
fprintf(stderr, "Restoring %u CPU interfaces, kernel only has %d\n",
|
||||||
|
s->num_cpu, num_cpu);
|
||||||
|
/* Did we not create the VCPUs in the kernel yet? */
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Consider checking compatibility with the IIDR ? */
|
||||||
|
|
||||||
|
/* irq_state[n].enabled -> GICD_ISENABLERn */
|
||||||
|
kvm_dist_put(s, 0x180, 1, s->num_irq, translate_clear);
|
||||||
|
kvm_dist_put(s, 0x100, 1, s->num_irq, translate_enabled);
|
||||||
|
|
||||||
|
/* s->irq_target[irq] -> GICD_ITARGETSRn
|
||||||
|
* (restore targets before pending to ensure the pending state is set on
|
||||||
|
* the appropriate CPU interfaces in the kernel) */
|
||||||
|
kvm_dist_put(s, 0x800, 8, s->num_irq, translate_targets);
|
||||||
|
|
||||||
|
/* irq_state[n].pending + irq_state[n].level -> GICD_ISPENDRn */
|
||||||
|
kvm_dist_put(s, 0x280, 1, s->num_irq, translate_clear);
|
||||||
|
kvm_dist_put(s, 0x200, 1, s->num_irq, translate_pending);
|
||||||
|
|
||||||
|
/* irq_state[n].active -> GICD_ISACTIVERn */
|
||||||
|
kvm_dist_put(s, 0x380, 1, s->num_irq, translate_clear);
|
||||||
|
kvm_dist_put(s, 0x300, 1, s->num_irq, translate_active);
|
||||||
|
|
||||||
|
/* irq_state[n].trigger -> GICD_ICFRn */
|
||||||
|
kvm_dist_put(s, 0xc00, 2, s->num_irq, translate_trigger);
|
||||||
|
|
||||||
|
/* s->priorityX[irq] -> ICD_IPRIORITYRn */
|
||||||
|
kvm_dist_put(s, 0x400, 8, s->num_irq, translate_priority);
|
||||||
|
|
||||||
|
/* s->sgi_pending -> ICD_CPENDSGIRn */
|
||||||
|
kvm_dist_put(s, 0xf10, 8, GIC_NR_SGIS, translate_clear);
|
||||||
|
kvm_dist_put(s, 0xf20, 8, GIC_NR_SGIS, translate_sgisource);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************
|
||||||
|
* CPU Interface(s) State
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (cpu = 0; cpu < s->num_cpu; cpu++) {
|
||||||
|
/* s->cpu_enabled[cpu] -> GICC_CTLR */
|
||||||
|
reg = s->cpu_enabled[cpu];
|
||||||
|
kvm_gicc_access(s, 0x00, cpu, ®, true);
|
||||||
|
|
||||||
|
/* s->priority_mask[cpu] -> GICC_PMR */
|
||||||
|
reg = (s->priority_mask[cpu] & 0xff);
|
||||||
|
kvm_gicc_access(s, 0x04, cpu, ®, true);
|
||||||
|
|
||||||
|
/* s->bpr[cpu] -> GICC_BPR */
|
||||||
|
reg = (s->bpr[cpu] & 0x7);
|
||||||
|
kvm_gicc_access(s, 0x08, cpu, ®, true);
|
||||||
|
|
||||||
|
/* s->abpr[cpu] -> GICC_ABPR */
|
||||||
|
reg = (s->abpr[cpu] & 0x7);
|
||||||
|
kvm_gicc_access(s, 0x1c, cpu, ®, true);
|
||||||
|
|
||||||
|
/* s->apr[n][cpu] -> GICC_APRn */
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
reg = s->apr[i][cpu];
|
||||||
|
kvm_gicc_access(s, 0xd0 + i * 4, cpu, ®, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_arm_gic_get(GICState *s)
|
static void kvm_arm_gic_get(GICState *s)
|
||||||
{
|
{
|
||||||
/* TODO: there isn't currently a kernel interface to get the GIC state */
|
uint32_t reg;
|
||||||
|
int i;
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
if (!kvm_arm_gic_can_save_restore(s)) {
|
||||||
|
DPRINTF("Cannot get kernel gic state, no kernel interface");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************
|
||||||
|
* Distributor State
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* GICD_CTLR -> s->enabled */
|
||||||
|
kvm_gicd_access(s, 0x0, 0, ®, false);
|
||||||
|
s->enabled = reg & 1;
|
||||||
|
|
||||||
|
/* Sanity checking on GICD_TYPER -> s->num_irq, s->num_cpu */
|
||||||
|
kvm_gicd_access(s, 0x4, 0, ®, false);
|
||||||
|
s->num_irq = ((reg & 0x1f) + 1) * 32;
|
||||||
|
s->num_cpu = ((reg & 0xe0) >> 5) + 1;
|
||||||
|
|
||||||
|
if (s->num_irq > GIC_MAXIRQ) {
|
||||||
|
fprintf(stderr, "Too many IRQs reported from the kernel: %d\n",
|
||||||
|
s->num_irq);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GICD_IIDR -> ? */
|
||||||
|
kvm_gicd_access(s, 0x8, 0, ®, false);
|
||||||
|
|
||||||
|
/* Verify no GROUP 1 interrupts configured in the kernel */
|
||||||
|
for_each_irq_reg(i, s->num_irq, 1) {
|
||||||
|
kvm_gicd_access(s, 0x80 + (i * 4), 0, ®, false);
|
||||||
|
if (reg != 0) {
|
||||||
|
fprintf(stderr, "Unsupported GICD_IGROUPRn value: %08x\n",
|
||||||
|
reg);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear all the IRQ settings */
|
||||||
|
for (i = 0; i < s->num_irq; i++) {
|
||||||
|
memset(&s->irq_state[i], 0, sizeof(s->irq_state[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GICD_ISENABLERn -> irq_state[n].enabled */
|
||||||
|
kvm_dist_get(s, 0x100, 1, s->num_irq, translate_enabled);
|
||||||
|
|
||||||
|
/* GICD_ISPENDRn -> irq_state[n].pending + irq_state[n].level */
|
||||||
|
kvm_dist_get(s, 0x200, 1, s->num_irq, translate_pending);
|
||||||
|
|
||||||
|
/* GICD_ISACTIVERn -> irq_state[n].active */
|
||||||
|
kvm_dist_get(s, 0x300, 1, s->num_irq, translate_active);
|
||||||
|
|
||||||
|
/* GICD_ICFRn -> irq_state[n].trigger */
|
||||||
|
kvm_dist_get(s, 0xc00, 2, s->num_irq, translate_trigger);
|
||||||
|
|
||||||
|
/* GICD_IPRIORITYRn -> s->priorityX[irq] */
|
||||||
|
kvm_dist_get(s, 0x400, 8, s->num_irq, translate_priority);
|
||||||
|
|
||||||
|
/* GICD_ITARGETSRn -> s->irq_target[irq] */
|
||||||
|
kvm_dist_get(s, 0x800, 8, s->num_irq, translate_targets);
|
||||||
|
|
||||||
|
/* GICD_CPENDSGIRn -> s->sgi_pending */
|
||||||
|
kvm_dist_get(s, 0xf10, 8, GIC_NR_SGIS, translate_sgisource);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************
|
||||||
|
* CPU Interface(s) State
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (cpu = 0; cpu < s->num_cpu; cpu++) {
|
||||||
|
/* GICC_CTLR -> s->cpu_enabled[cpu] */
|
||||||
|
kvm_gicc_access(s, 0x00, cpu, ®, false);
|
||||||
|
s->cpu_enabled[cpu] = (reg & 1);
|
||||||
|
|
||||||
|
/* GICC_PMR -> s->priority_mask[cpu] */
|
||||||
|
kvm_gicc_access(s, 0x04, cpu, ®, false);
|
||||||
|
s->priority_mask[cpu] = (reg & 0xff);
|
||||||
|
|
||||||
|
/* GICC_BPR -> s->bpr[cpu] */
|
||||||
|
kvm_gicc_access(s, 0x08, cpu, ®, false);
|
||||||
|
s->bpr[cpu] = (reg & 0x7);
|
||||||
|
|
||||||
|
/* GICC_ABPR -> s->abpr[cpu] */
|
||||||
|
kvm_gicc_access(s, 0x1c, cpu, ®, false);
|
||||||
|
s->abpr[cpu] = (reg & 0x7);
|
||||||
|
|
||||||
|
/* GICC_APRn -> s->apr[n][cpu] */
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
kvm_gicc_access(s, 0xd0 + i * 4, cpu, ®, false);
|
||||||
|
s->apr[i][cpu] = reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_arm_gic_reset(DeviceState *dev)
|
static void kvm_arm_gic_reset(DeviceState *dev)
|
||||||
|
@ -97,6 +517,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
|
||||||
GICState *s = KVM_ARM_GIC(dev);
|
GICState *s = KVM_ARM_GIC(dev);
|
||||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
|
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
|
||||||
|
int ret;
|
||||||
|
|
||||||
kgc->parent_realize(dev, errp);
|
kgc->parent_realize(dev, errp);
|
||||||
if (error_is_set(errp)) {
|
if (error_is_set(errp)) {
|
||||||
|
@ -119,13 +540,27 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
|
||||||
for (i = 0; i < s->num_cpu; i++) {
|
for (i = 0; i < s->num_cpu; i++) {
|
||||||
sysbus_init_irq(sbd, &s->parent_irq[i]);
|
sysbus_init_irq(sbd, &s->parent_irq[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try to create the device via the device control API */
|
||||||
|
s->dev_fd = -1;
|
||||||
|
ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
|
||||||
|
if (ret >= 0) {
|
||||||
|
s->dev_fd = ret;
|
||||||
|
} else if (ret != -ENODEV && ret != -ENOTSUP) {
|
||||||
|
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Distributor */
|
/* Distributor */
|
||||||
memory_region_init_reservation(&s->iomem, OBJECT(s),
|
memory_region_init_reservation(&s->iomem, OBJECT(s),
|
||||||
"kvm-gic_dist", 0x1000);
|
"kvm-gic_dist", 0x1000);
|
||||||
sysbus_init_mmio(sbd, &s->iomem);
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
kvm_arm_register_device(&s->iomem,
|
kvm_arm_register_device(&s->iomem,
|
||||||
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
|
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
|
||||||
| KVM_VGIC_V2_ADDR_TYPE_DIST);
|
| KVM_VGIC_V2_ADDR_TYPE_DIST,
|
||||||
|
KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
|
KVM_VGIC_V2_ADDR_TYPE_DIST,
|
||||||
|
s->dev_fd);
|
||||||
/* CPU interface for current core. Unlike arm_gic, we don't
|
/* CPU interface for current core. Unlike arm_gic, we don't
|
||||||
* provide the "interface for core #N" memory regions, because
|
* provide the "interface for core #N" memory regions, because
|
||||||
* cores with a VGIC don't have those.
|
* cores with a VGIC don't have those.
|
||||||
|
@ -135,7 +570,10 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
|
||||||
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
|
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
|
||||||
kvm_arm_register_device(&s->cpuiomem[0],
|
kvm_arm_register_device(&s->cpuiomem[0],
|
||||||
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
|
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
|
||||||
| KVM_VGIC_V2_ADDR_TYPE_CPU);
|
| KVM_VGIC_V2_ADDR_TYPE_CPU,
|
||||||
|
KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
|
KVM_VGIC_V2_ADDR_TYPE_CPU,
|
||||||
|
s->dev_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
|
static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
|
||||||
|
|
|
@ -418,7 +418,7 @@ static int exynos4210_combiner_init(SysBusDevice *sbd)
|
||||||
qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ);
|
qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ);
|
||||||
|
|
||||||
/* Connect SysBusDev irqs to device specific irqs */
|
/* Connect SysBusDev irqs to device specific irqs */
|
||||||
for (i = 0; i < IIC_NIRQ; i++) {
|
for (i = 0; i < IIC_NGRP; i++) {
|
||||||
sysbus_init_irq(sbd, &s->output_irq[i]);
|
sysbus_init_irq(sbd, &s->output_irq[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = true
|
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = true
|
||||||
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = false
|
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = false
|
||||||
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
|
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
|
||||||
#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
|
#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level |= (cm)
|
||||||
#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
|
#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
|
||||||
#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
|
#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
|
||||||
#define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true
|
#define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true
|
||||||
|
|
|
@ -276,7 +276,7 @@ static bool vexpress_cfgctrl_read(arm_sysctl_state *s, unsigned int dcc,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SYS_CFG_OSC:
|
case SYS_CFG_OSC:
|
||||||
if (site == SYS_CFG_SITE_MB && device < sizeof(s->mb_clock)) {
|
if (site == SYS_CFG_SITE_MB && device < ARRAY_SIZE(s->mb_clock)) {
|
||||||
/* motherboard clock */
|
/* motherboard clock */
|
||||||
*val = s->mb_clock[device];
|
*val = s->mb_clock[device];
|
||||||
return true;
|
return true;
|
||||||
|
@ -324,7 +324,7 @@ static bool vexpress_cfgctrl_write(arm_sysctl_state *s, unsigned int dcc,
|
||||||
|
|
||||||
switch (function) {
|
switch (function) {
|
||||||
case SYS_CFG_OSC:
|
case SYS_CFG_OSC:
|
||||||
if (site == SYS_CFG_SITE_MB && device < sizeof(s->mb_clock)) {
|
if (site == SYS_CFG_SITE_MB && device < ARRAY_SIZE(s->mb_clock)) {
|
||||||
/* motherboard clock */
|
/* motherboard clock */
|
||||||
s->mb_clock[device] = val;
|
s->mb_clock[device] = val;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -176,7 +176,8 @@ static uint64_t stellaris_enet_read(void *opaque, hwaddr offset,
|
||||||
return val;
|
return val;
|
||||||
case 0x14: /* IA0 */
|
case 0x14: /* IA0 */
|
||||||
return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
|
return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
|
||||||
| (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24);
|
| (s->conf.macaddr.a[2] << 16)
|
||||||
|
| ((uint32_t)s->conf.macaddr.a[3] << 24);
|
||||||
case 0x18: /* IA1 */
|
case 0x18: /* IA1 */
|
||||||
return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
|
return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
|
||||||
case 0x1c: /* THR */
|
case 0x1c: /* THR */
|
||||||
|
|
|
@ -320,6 +320,7 @@ static uint64_t icp_pit_read(void *opaque, hwaddr offset,
|
||||||
n = offset >> 8;
|
n = offset >> 8;
|
||||||
if (n > 2) {
|
if (n > 2) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return arm_timer_read(s->timer[n], offset & 0xff);
|
return arm_timer_read(s->timer[n], offset & 0xff);
|
||||||
|
@ -334,6 +335,7 @@ static void icp_pit_write(void *opaque, hwaddr offset,
|
||||||
n = offset >> 8;
|
n = offset >> 8;
|
||||||
if (n > 2) {
|
if (n > 2) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
arm_timer_write(s->timer[n], offset & 0xff, value);
|
arm_timer_write(s->timer[n], offset & 0xff, value);
|
||||||
|
|
|
@ -104,6 +104,7 @@ typedef struct GICState {
|
||||||
MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */
|
MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */
|
||||||
uint32_t num_irq;
|
uint32_t num_irq;
|
||||||
uint32_t revision;
|
uint32_t revision;
|
||||||
|
int dev_fd; /* kvm device fd if backed by kvm vgic support */
|
||||||
} GICState;
|
} GICState;
|
||||||
|
|
||||||
#define TYPE_ARM_GIC_COMMON "arm_gic_common"
|
#define TYPE_ARM_GIC_COMMON "arm_gic_common"
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef QEMU_CRC32_H
|
#ifndef QEMU_CRC32C_H
|
||||||
#define QEMU_CRC32_H
|
#define QEMU_CRC32C_H
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
|
||||||
|
|
|
@ -194,6 +194,28 @@ int kvm_vm_ioctl(KVMState *s, int type, ...);
|
||||||
|
|
||||||
int kvm_vcpu_ioctl(CPUState *cpu, int type, ...);
|
int kvm_vcpu_ioctl(CPUState *cpu, int type, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_device_ioctl - call an ioctl on a kvm device
|
||||||
|
* @fd: The KVM device file descriptor as returned from KVM_CREATE_DEVICE
|
||||||
|
* @type: The device-ctrl ioctl number
|
||||||
|
*
|
||||||
|
* Returns: -errno on error, nonnegative on success
|
||||||
|
*/
|
||||||
|
int kvm_device_ioctl(int fd, int type, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_create_device - create a KVM device for the device control API
|
||||||
|
* @KVMState: The KVMState pointer
|
||||||
|
* @type: The KVM device type (see Documentation/virtual/kvm/devices in the
|
||||||
|
* kernel source)
|
||||||
|
* @test: If true, only test if device can be created, but don't actually
|
||||||
|
* create the device.
|
||||||
|
*
|
||||||
|
* Returns: -errno on error, nonnegative on success: @test ? 0 : device fd;
|
||||||
|
*/
|
||||||
|
int kvm_create_device(KVMState *s, uint64_t type, bool test);
|
||||||
|
|
||||||
|
|
||||||
/* Arch specific hooks */
|
/* Arch specific hooks */
|
||||||
|
|
||||||
extern const KVMCapabilityInfo kvm_arch_required_capabilities[];
|
extern const KVMCapabilityInfo kvm_arch_required_capabilities[];
|
||||||
|
@ -319,4 +341,16 @@ int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq);
|
||||||
void kvm_pc_gsi_handler(void *opaque, int n, int level);
|
void kvm_pc_gsi_handler(void *opaque, int n, int level);
|
||||||
void kvm_pc_setup_irq_routing(bool pci_enabled);
|
void kvm_pc_setup_irq_routing(bool pci_enabled);
|
||||||
void kvm_init_irq_routing(KVMState *s);
|
void kvm_init_irq_routing(KVMState *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kvm_arch_irqchip_create:
|
||||||
|
* @KVMState: The KVMState pointer
|
||||||
|
*
|
||||||
|
* Allow architectures to create an in-kernel irq chip themselves.
|
||||||
|
*
|
||||||
|
* Returns: < 0: error
|
||||||
|
* 0: irq chip was not created
|
||||||
|
* > 0: irq chip was created
|
||||||
|
*/
|
||||||
|
int kvm_arch_irqchip_create(KVMState *s);
|
||||||
#endif
|
#endif
|
||||||
|
|
46
kvm-all.c
46
kvm-all.c
|
@ -1298,11 +1298,18 @@ static int kvm_irqchip_create(KVMState *s)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* First probe and see if there's a arch-specific hook to create the
|
||||||
|
* in-kernel irqchip for us */
|
||||||
|
ret = kvm_arch_irqchip_create(s);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
} else if (ret == 0) {
|
||||||
ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP);
|
ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Create kernel irqchip failed\n");
|
fprintf(stderr, "Create kernel irqchip failed\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kvm_kernel_irqchip = true;
|
kvm_kernel_irqchip = true;
|
||||||
/* If we have an in-kernel IRQ chip then we must have asynchronous
|
/* If we have an in-kernel IRQ chip then we must have asynchronous
|
||||||
|
@ -1777,6 +1784,24 @@ int kvm_vcpu_ioctl(CPUState *cpu, int type, ...)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_device_ioctl(int fd, int type, ...)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
void *arg;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, type);
|
||||||
|
arg = va_arg(ap, void *);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
trace_kvm_device_ioctl(fd, type, arg);
|
||||||
|
ret = ioctl(fd, type, arg);
|
||||||
|
if (ret == -1) {
|
||||||
|
ret = -errno;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_has_sync_mmu(void)
|
int kvm_has_sync_mmu(void)
|
||||||
{
|
{
|
||||||
return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
||||||
|
@ -2058,3 +2083,24 @@ int kvm_on_sigbus(int code, void *addr)
|
||||||
{
|
{
|
||||||
return kvm_arch_on_sigbus(code, addr);
|
return kvm_arch_on_sigbus(code, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_create_device(KVMState *s, uint64_t type, bool test)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct kvm_create_device create_dev;
|
||||||
|
|
||||||
|
create_dev.type = type;
|
||||||
|
create_dev.fd = -1;
|
||||||
|
create_dev.flags = test ? KVM_CREATE_DEVICE_TEST : 0;
|
||||||
|
|
||||||
|
if (!kvm_check_extension(s, KVM_CAP_DEVICE_CTRL)) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = kvm_vm_ioctl(s, KVM_CREATE_DEVICE, &create_dev);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return test ? 0 : create_dev.fd;
|
||||||
|
}
|
||||||
|
|
|
@ -119,6 +119,26 @@ struct kvm_arch_memory_slot {
|
||||||
#define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800
|
#define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800
|
||||||
#define KVM_REG_ARM_32_CRN_SHIFT 11
|
#define KVM_REG_ARM_32_CRN_SHIFT 11
|
||||||
|
|
||||||
|
#define ARM_CP15_REG_SHIFT_MASK(x,n) \
|
||||||
|
(((x) << KVM_REG_ARM_ ## n ## _SHIFT) & KVM_REG_ARM_ ## n ## _MASK)
|
||||||
|
|
||||||
|
#define __ARM_CP15_REG(op1,crn,crm,op2) \
|
||||||
|
(KVM_REG_ARM | (15 << KVM_REG_ARM_COPROC_SHIFT) | \
|
||||||
|
ARM_CP15_REG_SHIFT_MASK(op1, OPC1) | \
|
||||||
|
ARM_CP15_REG_SHIFT_MASK(crn, 32_CRN) | \
|
||||||
|
ARM_CP15_REG_SHIFT_MASK(crm, CRM) | \
|
||||||
|
ARM_CP15_REG_SHIFT_MASK(op2, 32_OPC2))
|
||||||
|
|
||||||
|
#define ARM_CP15_REG32(...) (__ARM_CP15_REG(__VA_ARGS__) | KVM_REG_SIZE_U32)
|
||||||
|
|
||||||
|
#define __ARM_CP15_REG64(op1,crm) \
|
||||||
|
(__ARM_CP15_REG(op1, 0, crm, 0) | KVM_REG_SIZE_U64)
|
||||||
|
#define ARM_CP15_REG64(...) __ARM_CP15_REG64(__VA_ARGS__)
|
||||||
|
|
||||||
|
#define KVM_REG_ARM_TIMER_CTL ARM_CP15_REG32(0, 14, 3, 1)
|
||||||
|
#define KVM_REG_ARM_TIMER_CNT ARM_CP15_REG64(1, 14)
|
||||||
|
#define KVM_REG_ARM_TIMER_CVAL ARM_CP15_REG64(3, 14)
|
||||||
|
|
||||||
/* Normal registers are mapped as coprocessor 16. */
|
/* Normal registers are mapped as coprocessor 16. */
|
||||||
#define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT)
|
#define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT)
|
||||||
#define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4)
|
#define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4)
|
||||||
|
@ -143,6 +163,14 @@ struct kvm_arch_memory_slot {
|
||||||
#define KVM_REG_ARM_VFP_FPINST 0x1009
|
#define KVM_REG_ARM_VFP_FPINST 0x1009
|
||||||
#define KVM_REG_ARM_VFP_FPINST2 0x100A
|
#define KVM_REG_ARM_VFP_FPINST2 0x100A
|
||||||
|
|
||||||
|
/* Device Control API: ARM VGIC */
|
||||||
|
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
|
||||||
|
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
|
||||||
|
#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS 2
|
||||||
|
#define KVM_DEV_ARM_VGIC_CPUID_SHIFT 32
|
||||||
|
#define KVM_DEV_ARM_VGIC_CPUID_MASK (0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
|
||||||
|
#define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0
|
||||||
|
#define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
|
||||||
|
|
||||||
/* KVM_IRQ_LINE irq field index values */
|
/* KVM_IRQ_LINE irq field index values */
|
||||||
#define KVM_ARM_IRQ_TYPE_SHIFT 24
|
#define KVM_ARM_IRQ_TYPE_SHIFT 24
|
||||||
|
|
|
@ -55,8 +55,9 @@ struct kvm_regs {
|
||||||
#define KVM_ARM_TARGET_AEM_V8 0
|
#define KVM_ARM_TARGET_AEM_V8 0
|
||||||
#define KVM_ARM_TARGET_FOUNDATION_V8 1
|
#define KVM_ARM_TARGET_FOUNDATION_V8 1
|
||||||
#define KVM_ARM_TARGET_CORTEX_A57 2
|
#define KVM_ARM_TARGET_CORTEX_A57 2
|
||||||
|
#define KVM_ARM_TARGET_XGENE_POTENZA 3
|
||||||
|
|
||||||
#define KVM_ARM_NUM_TARGETS 3
|
#define KVM_ARM_NUM_TARGETS 4
|
||||||
|
|
||||||
/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */
|
/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */
|
||||||
#define KVM_ARM_DEVICE_TYPE_SHIFT 0
|
#define KVM_ARM_DEVICE_TYPE_SHIFT 0
|
||||||
|
@ -129,6 +130,33 @@ struct kvm_arch_memory_slot {
|
||||||
#define KVM_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007
|
#define KVM_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007
|
||||||
#define KVM_REG_ARM64_SYSREG_OP2_SHIFT 0
|
#define KVM_REG_ARM64_SYSREG_OP2_SHIFT 0
|
||||||
|
|
||||||
|
#define ARM64_SYS_REG_SHIFT_MASK(x,n) \
|
||||||
|
(((x) << KVM_REG_ARM64_SYSREG_ ## n ## _SHIFT) & \
|
||||||
|
KVM_REG_ARM64_SYSREG_ ## n ## _MASK)
|
||||||
|
|
||||||
|
#define __ARM64_SYS_REG(op0,op1,crn,crm,op2) \
|
||||||
|
(KVM_REG_ARM64 | KVM_REG_ARM64_SYSREG | \
|
||||||
|
ARM64_SYS_REG_SHIFT_MASK(op0, OP0) | \
|
||||||
|
ARM64_SYS_REG_SHIFT_MASK(op1, OP1) | \
|
||||||
|
ARM64_SYS_REG_SHIFT_MASK(crn, CRN) | \
|
||||||
|
ARM64_SYS_REG_SHIFT_MASK(crm, CRM) | \
|
||||||
|
ARM64_SYS_REG_SHIFT_MASK(op2, OP2))
|
||||||
|
|
||||||
|
#define ARM64_SYS_REG(...) (__ARM64_SYS_REG(__VA_ARGS__) | KVM_REG_SIZE_U64)
|
||||||
|
|
||||||
|
#define KVM_REG_ARM_TIMER_CTL ARM64_SYS_REG(3, 3, 14, 3, 1)
|
||||||
|
#define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2)
|
||||||
|
#define KVM_REG_ARM_TIMER_CVAL ARM64_SYS_REG(3, 3, 14, 0, 2)
|
||||||
|
|
||||||
|
/* Device Control API: ARM VGIC */
|
||||||
|
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
|
||||||
|
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
|
||||||
|
#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS 2
|
||||||
|
#define KVM_DEV_ARM_VGIC_CPUID_SHIFT 32
|
||||||
|
#define KVM_DEV_ARM_VGIC_CPUID_MASK (0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
|
||||||
|
#define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0
|
||||||
|
#define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
|
||||||
|
|
||||||
/* KVM_IRQ_LINE irq field index values */
|
/* KVM_IRQ_LINE irq field index values */
|
||||||
#define KVM_ARM_IRQ_TYPE_SHIFT 24
|
#define KVM_ARM_IRQ_TYPE_SHIFT 24
|
||||||
#define KVM_ARM_IRQ_TYPE_MASK 0xff
|
#define KVM_ARM_IRQ_TYPE_MASK 0xff
|
||||||
|
|
|
@ -545,6 +545,7 @@ struct kvm_get_htab_header {
|
||||||
#define KVM_REG_PPC_TCSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb1)
|
#define KVM_REG_PPC_TCSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb1)
|
||||||
#define KVM_REG_PPC_PID (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb2)
|
#define KVM_REG_PPC_PID (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb2)
|
||||||
#define KVM_REG_PPC_ACOP (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb3)
|
#define KVM_REG_PPC_ACOP (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb3)
|
||||||
|
#define KVM_REG_PPC_WORT (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb4)
|
||||||
|
|
||||||
#define KVM_REG_PPC_VRSAVE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb4)
|
#define KVM_REG_PPC_VRSAVE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb4)
|
||||||
#define KVM_REG_PPC_LPCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb5)
|
#define KVM_REG_PPC_LPCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb5)
|
||||||
|
@ -553,6 +554,8 @@ struct kvm_get_htab_header {
|
||||||
/* Architecture compatibility level */
|
/* Architecture compatibility level */
|
||||||
#define KVM_REG_PPC_ARCH_COMPAT (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb7)
|
#define KVM_REG_PPC_ARCH_COMPAT (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb7)
|
||||||
|
|
||||||
|
#define KVM_REG_PPC_DABRX (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb8)
|
||||||
|
|
||||||
/* Transactional Memory checkpointed state:
|
/* Transactional Memory checkpointed state:
|
||||||
* This is all GPRs, all VSX regs and a subset of SPRs
|
* This is all GPRs, all VSX regs and a subset of SPRs
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/
|
/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/
|
||||||
#define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1)
|
#define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1)
|
||||||
|
|
||||||
|
/* A partition's reference time stamp counter (TSC) page */
|
||||||
|
#define HV_X64_MSR_REFERENCE_TSC 0x40000021
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There is a single feature flag that signifies the presence of the MSR
|
* There is a single feature flag that signifies the presence of the MSR
|
||||||
* that can be used to retrieve both the local APIC Timer frequency as
|
* that can be used to retrieve both the local APIC Timer frequency as
|
||||||
|
@ -149,9 +152,6 @@
|
||||||
/* MSR used to read the per-partition time reference counter */
|
/* MSR used to read the per-partition time reference counter */
|
||||||
#define HV_X64_MSR_TIME_REF_COUNT 0x40000020
|
#define HV_X64_MSR_TIME_REF_COUNT 0x40000020
|
||||||
|
|
||||||
/* A partition's reference time stamp counter (TSC) page */
|
|
||||||
#define HV_X64_MSR_REFERENCE_TSC 0x40000021
|
|
||||||
|
|
||||||
/* MSR used to retrieve the TSC frequency */
|
/* MSR used to retrieve the TSC frequency */
|
||||||
#define HV_X64_MSR_TSC_FREQUENCY 0x40000022
|
#define HV_X64_MSR_TSC_FREQUENCY 0x40000022
|
||||||
|
|
||||||
|
@ -201,6 +201,9 @@
|
||||||
#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK \
|
#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK \
|
||||||
(~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1))
|
(~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1))
|
||||||
|
|
||||||
|
#define HV_X64_MSR_TSC_REFERENCE_ENABLE 0x00000001
|
||||||
|
#define HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT 12
|
||||||
|
|
||||||
#define HV_PROCESSOR_POWER_STATE_C0 0
|
#define HV_PROCESSOR_POWER_STATE_C0 0
|
||||||
#define HV_PROCESSOR_POWER_STATE_C1 1
|
#define HV_PROCESSOR_POWER_STATE_C1 1
|
||||||
#define HV_PROCESSOR_POWER_STATE_C2 2
|
#define HV_PROCESSOR_POWER_STATE_C2 2
|
||||||
|
@ -213,4 +216,11 @@
|
||||||
#define HV_STATUS_INVALID_ALIGNMENT 4
|
#define HV_STATUS_INVALID_ALIGNMENT 4
|
||||||
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
|
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
|
||||||
|
|
||||||
|
typedef struct _HV_REFERENCE_TSC_PAGE {
|
||||||
|
__u32 tsc_sequence;
|
||||||
|
__u32 res1;
|
||||||
|
__u64 tsc_scale;
|
||||||
|
__s64 tsc_offset;
|
||||||
|
} HV_REFERENCE_TSC_PAGE, *PHV_REFERENCE_TSC_PAGE;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -854,6 +854,7 @@ struct kvm_device_attr {
|
||||||
#define KVM_DEV_VFIO_GROUP 1
|
#define KVM_DEV_VFIO_GROUP 1
|
||||||
#define KVM_DEV_VFIO_GROUP_ADD 1
|
#define KVM_DEV_VFIO_GROUP_ADD 1
|
||||||
#define KVM_DEV_VFIO_GROUP_DEL 2
|
#define KVM_DEV_VFIO_GROUP_DEL 2
|
||||||
|
#define KVM_DEV_TYPE_ARM_VGIC_V2 5
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ioctls for VM fds
|
* ioctls for VM fds
|
||||||
|
|
|
@ -27,3 +27,4 @@ stub-obj-y += vm-stop.o
|
||||||
stub-obj-y += vmstate.o
|
stub-obj-y += vmstate.o
|
||||||
stub-obj-$(CONFIG_WIN32) += fd-register.o
|
stub-obj-$(CONFIG_WIN32) += fd-register.o
|
||||||
stub-obj-y += cpus.o
|
stub-obj-y += cpus.o
|
||||||
|
stub-obj-y += kvm.o
|
||||||
|
|
7
stubs/kvm.c
Normal file
7
stubs/kvm.c
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
|
||||||
|
int kvm_arch_irqchip_create(KVMState *s)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -132,6 +132,16 @@ typedef struct ARMCPU {
|
||||||
uint32_t id_isar3;
|
uint32_t id_isar3;
|
||||||
uint32_t id_isar4;
|
uint32_t id_isar4;
|
||||||
uint32_t id_isar5;
|
uint32_t id_isar5;
|
||||||
|
uint64_t id_aa64pfr0;
|
||||||
|
uint64_t id_aa64pfr1;
|
||||||
|
uint64_t id_aa64dfr0;
|
||||||
|
uint64_t id_aa64dfr1;
|
||||||
|
uint64_t id_aa64afr0;
|
||||||
|
uint64_t id_aa64afr1;
|
||||||
|
uint64_t id_aa64isar0;
|
||||||
|
uint64_t id_aa64isar1;
|
||||||
|
uint64_t id_aa64mmfr0;
|
||||||
|
uint64_t id_aa64mmfr1;
|
||||||
uint32_t clidr;
|
uint32_t clidr;
|
||||||
/* The elements of this array are the CCSIDR values for each cache,
|
/* The elements of this array are the CCSIDR values for each cache,
|
||||||
* in the order L1DCache, L1ICache, L2DCache, L2ICache, etc.
|
* in the order L1DCache, L1ICache, L2DCache, L2ICache, etc.
|
||||||
|
|
|
@ -60,7 +60,7 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ri->type & ARM_CP_64BIT) {
|
if (cpreg_field_is_64bit(ri)) {
|
||||||
CPREG_FIELD64(&cpu->env, ri) = ri->resetvalue;
|
CPREG_FIELD64(&cpu->env, ri) = ri->resetvalue;
|
||||||
} else {
|
} else {
|
||||||
CPREG_FIELD32(&cpu->env, ri) = ri->resetvalue;
|
CPREG_FIELD32(&cpu->env, ri) = ri->resetvalue;
|
||||||
|
@ -91,9 +91,10 @@ static void arm_cpu_reset(CPUState *s)
|
||||||
env->aarch64 = 1;
|
env->aarch64 = 1;
|
||||||
#if defined(CONFIG_USER_ONLY)
|
#if defined(CONFIG_USER_ONLY)
|
||||||
env->pstate = PSTATE_MODE_EL0t;
|
env->pstate = PSTATE_MODE_EL0t;
|
||||||
|
/* Userspace expects access to CTL_EL0 and the cache ops */
|
||||||
|
env->cp15.c1_sys |= SCTLR_UCT | SCTLR_UCI;
|
||||||
#else
|
#else
|
||||||
env->pstate = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F
|
env->pstate = PSTATE_MODE_EL1h;
|
||||||
| PSTATE_MODE_EL1h;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,13 +109,14 @@ static void arm_cpu_reset(CPUState *s)
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* SVC mode with interrupts disabled. */
|
/* SVC mode with interrupts disabled. */
|
||||||
env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
|
env->uncached_cpsr = ARM_CPU_MODE_SVC;
|
||||||
|
env->daif = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F;
|
||||||
/* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is
|
/* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is
|
||||||
clear at reset. Initial SP and PC are loaded from ROM. */
|
clear at reset. Initial SP and PC are loaded from ROM. */
|
||||||
if (IS_M(env)) {
|
if (IS_M(env)) {
|
||||||
uint32_t pc;
|
uint32_t pc;
|
||||||
uint8_t *rom;
|
uint8_t *rom;
|
||||||
env->uncached_cpsr &= ~CPSR_I;
|
env->daif &= ~PSTATE_I;
|
||||||
rom = rom_ptr(0);
|
rom = rom_ptr(0);
|
||||||
if (rom) {
|
if (rom) {
|
||||||
/* We should really use ldl_phys here, in case the guest
|
/* We should really use ldl_phys here, in case the guest
|
||||||
|
@ -922,6 +924,7 @@ static void arm_any_initfn(Object *obj)
|
||||||
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
|
set_feature(&cpu->env, ARM_FEATURE_THUMB2EE);
|
||||||
set_feature(&cpu->env, ARM_FEATURE_ARM_DIV);
|
set_feature(&cpu->env, ARM_FEATURE_ARM_DIV);
|
||||||
set_feature(&cpu->env, ARM_FEATURE_V7MP);
|
set_feature(&cpu->env, ARM_FEATURE_V7MP);
|
||||||
|
set_feature(&cpu->env, ARM_FEATURE_CRC);
|
||||||
#ifdef TARGET_AARCH64
|
#ifdef TARGET_AARCH64
|
||||||
set_feature(&cpu->env, ARM_FEATURE_AARCH64);
|
set_feature(&cpu->env, ARM_FEATURE_AARCH64);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -74,8 +74,10 @@
|
||||||
*/
|
*/
|
||||||
#ifdef HOST_WORDS_BIGENDIAN
|
#ifdef HOST_WORDS_BIGENDIAN
|
||||||
#define offsetoflow32(S, M) (offsetof(S, M) + sizeof(uint32_t))
|
#define offsetoflow32(S, M) (offsetof(S, M) + sizeof(uint32_t))
|
||||||
|
#define offsetofhigh32(S, M) offsetof(S, M)
|
||||||
#else
|
#else
|
||||||
#define offsetoflow32(S, M) offsetof(S, M)
|
#define offsetoflow32(S, M) offsetof(S, M)
|
||||||
|
#define offsetofhigh32(S, M) (offsetof(S, M) + sizeof(uint32_t))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Meanings of the ARMCPU object's two inbound GPIO lines */
|
/* Meanings of the ARMCPU object's two inbound GPIO lines */
|
||||||
|
@ -102,7 +104,7 @@ struct arm_boot_info;
|
||||||
/* CPU state for each instance of a generic timer (in cp15 c14) */
|
/* CPU state for each instance of a generic timer (in cp15 c14) */
|
||||||
typedef struct ARMGenericTimer {
|
typedef struct ARMGenericTimer {
|
||||||
uint64_t cval; /* Timer CompareValue register */
|
uint64_t cval; /* Timer CompareValue register */
|
||||||
uint32_t ctl; /* Timer Control register */
|
uint64_t ctl; /* Timer Control register */
|
||||||
} ARMGenericTimer;
|
} ARMGenericTimer;
|
||||||
|
|
||||||
#define GTIMER_PHYS 0
|
#define GTIMER_PHYS 0
|
||||||
|
@ -133,6 +135,7 @@ typedef struct CPUARMState {
|
||||||
* NZCV are kept in the split out env->CF/VF/NF/ZF, (which have the same
|
* NZCV are kept in the split out env->CF/VF/NF/ZF, (which have the same
|
||||||
* semantics as for AArch32, as described in the comments on each field)
|
* semantics as for AArch32, as described in the comments on each field)
|
||||||
* nRW (also known as M[4]) is kept, inverted, in env->aarch64
|
* nRW (also known as M[4]) is kept, inverted, in env->aarch64
|
||||||
|
* DAIF (exception masks) are kept in env->daif
|
||||||
* all other bits are stored in their correct places in env->pstate
|
* all other bits are stored in their correct places in env->pstate
|
||||||
*/
|
*/
|
||||||
uint32_t pstate;
|
uint32_t pstate;
|
||||||
|
@ -162,20 +165,19 @@ typedef struct CPUARMState {
|
||||||
uint32_t GE; /* cpsr[19:16] */
|
uint32_t GE; /* cpsr[19:16] */
|
||||||
uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */
|
uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */
|
||||||
uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */
|
uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */
|
||||||
|
uint32_t daif; /* exception masks, in the bits they are in in PSTATE */
|
||||||
|
|
||||||
/* System control coprocessor (cp15) */
|
/* System control coprocessor (cp15) */
|
||||||
struct {
|
struct {
|
||||||
uint32_t c0_cpuid;
|
uint32_t c0_cpuid;
|
||||||
uint32_t c0_cssel; /* Cache size selection. */
|
uint64_t c0_cssel; /* Cache size selection. */
|
||||||
uint32_t c1_sys; /* System control register. */
|
uint64_t c1_sys; /* System control register. */
|
||||||
uint32_t c1_coproc; /* Coprocessor access register. */
|
uint64_t c1_coproc; /* Coprocessor access register. */
|
||||||
uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */
|
uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */
|
||||||
uint32_t c1_scr; /* secure config register. */
|
uint32_t c1_scr; /* secure config register. */
|
||||||
uint32_t c2_base0; /* MMU translation table base 0. */
|
uint64_t ttbr0_el1; /* MMU translation table base 0. */
|
||||||
uint32_t c2_base0_hi; /* MMU translation table base 0, high 32 bits */
|
uint64_t ttbr1_el1; /* MMU translation table base 1. */
|
||||||
uint32_t c2_base1; /* MMU translation table base 0. */
|
uint64_t c2_control; /* MMU translation table base control. */
|
||||||
uint32_t c2_base1_hi; /* MMU translation table base 1, high 32 bits */
|
|
||||||
uint32_t c2_control; /* MMU translation table base control. */
|
|
||||||
uint32_t c2_mask; /* MMU translation table base selection mask. */
|
uint32_t c2_mask; /* MMU translation table base selection mask. */
|
||||||
uint32_t c2_base_mask; /* MMU translation table base 0 mask. */
|
uint32_t c2_base_mask; /* MMU translation table base 0 mask. */
|
||||||
uint32_t c2_data; /* MPU data cachable bits. */
|
uint32_t c2_data; /* MPU data cachable bits. */
|
||||||
|
@ -197,14 +199,15 @@ typedef struct CPUARMState {
|
||||||
uint32_t c9_pmxevtyper; /* perf monitor event type */
|
uint32_t c9_pmxevtyper; /* perf monitor event type */
|
||||||
uint32_t c9_pmuserenr; /* perf monitor user enable */
|
uint32_t c9_pmuserenr; /* perf monitor user enable */
|
||||||
uint32_t c9_pminten; /* perf monitor interrupt enables */
|
uint32_t c9_pminten; /* perf monitor interrupt enables */
|
||||||
uint32_t c12_vbar; /* vector base address register */
|
uint64_t mair_el1;
|
||||||
|
uint64_t c12_vbar; /* vector base address register */
|
||||||
uint32_t c13_fcse; /* FCSE PID. */
|
uint32_t c13_fcse; /* FCSE PID. */
|
||||||
uint32_t c13_context; /* Context ID. */
|
uint32_t c13_context; /* Context ID. */
|
||||||
uint64_t tpidr_el0; /* User RW Thread register. */
|
uint64_t tpidr_el0; /* User RW Thread register. */
|
||||||
uint64_t tpidrro_el0; /* User RO Thread register. */
|
uint64_t tpidrro_el0; /* User RO Thread register. */
|
||||||
uint64_t tpidr_el1; /* Privileged Thread register. */
|
uint64_t tpidr_el1; /* Privileged Thread register. */
|
||||||
uint32_t c14_cntfrq; /* Counter Frequency register */
|
uint64_t c14_cntfrq; /* Counter Frequency register */
|
||||||
uint32_t c14_cntkctl; /* Timer Control register */
|
uint64_t c14_cntkctl; /* Timer Control register */
|
||||||
ARMGenericTimer c14_timer[NUM_GTIMERS];
|
ARMGenericTimer c14_timer[NUM_GTIMERS];
|
||||||
uint32_t c15_cpar; /* XScale Coprocessor Access Register */
|
uint32_t c15_cpar; /* XScale Coprocessor Access Register */
|
||||||
uint32_t c15_ticonfig; /* TI925T configuration byte. */
|
uint32_t c15_ticonfig; /* TI925T configuration byte. */
|
||||||
|
@ -215,6 +218,10 @@ typedef struct CPUARMState {
|
||||||
uint32_t c15_diagnostic; /* diagnostic register */
|
uint32_t c15_diagnostic; /* diagnostic register */
|
||||||
uint32_t c15_power_diagnostic;
|
uint32_t c15_power_diagnostic;
|
||||||
uint32_t c15_power_control; /* power control */
|
uint32_t c15_power_control; /* power control */
|
||||||
|
uint64_t dbgbvr[16]; /* breakpoint value registers */
|
||||||
|
uint64_t dbgbcr[16]; /* breakpoint control registers */
|
||||||
|
uint64_t dbgwvr[16]; /* watchpoint value registers */
|
||||||
|
uint64_t dbgwcr[16]; /* watchpoint control registers */
|
||||||
} cp15;
|
} cp15;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -401,9 +408,11 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
|
||||||
#define CPSR_Z (1U << 30)
|
#define CPSR_Z (1U << 30)
|
||||||
#define CPSR_N (1U << 31)
|
#define CPSR_N (1U << 31)
|
||||||
#define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V)
|
#define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V)
|
||||||
|
#define CPSR_AIF (CPSR_A | CPSR_I | CPSR_F)
|
||||||
|
|
||||||
#define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7)
|
#define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7)
|
||||||
#define CACHED_CPSR_BITS (CPSR_T | CPSR_GE | CPSR_IT | CPSR_Q | CPSR_NZCV)
|
#define CACHED_CPSR_BITS (CPSR_T | CPSR_AIF | CPSR_GE | CPSR_IT | CPSR_Q \
|
||||||
|
| CPSR_NZCV)
|
||||||
/* Bits writable in user mode. */
|
/* Bits writable in user mode. */
|
||||||
#define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE)
|
#define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE)
|
||||||
/* Execution state bits. MRS read as zero, MSR writes ignored. */
|
/* Execution state bits. MRS read as zero, MSR writes ignored. */
|
||||||
|
@ -426,7 +435,8 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
|
||||||
#define PSTATE_Z (1U << 30)
|
#define PSTATE_Z (1U << 30)
|
||||||
#define PSTATE_N (1U << 31)
|
#define PSTATE_N (1U << 31)
|
||||||
#define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
|
#define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
|
||||||
#define CACHED_PSTATE_BITS (PSTATE_NZCV)
|
#define PSTATE_DAIF (PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F)
|
||||||
|
#define CACHED_PSTATE_BITS (PSTATE_NZCV | PSTATE_DAIF)
|
||||||
/* Mode values for AArch64 */
|
/* Mode values for AArch64 */
|
||||||
#define PSTATE_MODE_EL3h 13
|
#define PSTATE_MODE_EL3h 13
|
||||||
#define PSTATE_MODE_EL3t 12
|
#define PSTATE_MODE_EL3t 12
|
||||||
|
@ -447,7 +457,7 @@ static inline uint32_t pstate_read(CPUARMState *env)
|
||||||
ZF = (env->ZF == 0);
|
ZF = (env->ZF == 0);
|
||||||
return (env->NF & 0x80000000) | (ZF << 30)
|
return (env->NF & 0x80000000) | (ZF << 30)
|
||||||
| (env->CF << 29) | ((env->VF & 0x80000000) >> 3)
|
| (env->CF << 29) | ((env->VF & 0x80000000) >> 3)
|
||||||
| env->pstate;
|
| env->pstate | env->daif;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pstate_write(CPUARMState *env, uint32_t val)
|
static inline void pstate_write(CPUARMState *env, uint32_t val)
|
||||||
|
@ -456,6 +466,7 @@ static inline void pstate_write(CPUARMState *env, uint32_t val)
|
||||||
env->NF = val;
|
env->NF = val;
|
||||||
env->CF = (val >> 29) & 1;
|
env->CF = (val >> 29) & 1;
|
||||||
env->VF = (val << 3) & 0x80000000;
|
env->VF = (val << 3) & 0x80000000;
|
||||||
|
env->daif = val & PSTATE_DAIF;
|
||||||
env->pstate = val & ~CACHED_PSTATE_BITS;
|
env->pstate = val & ~CACHED_PSTATE_BITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,6 +626,7 @@ enum arm_features {
|
||||||
ARM_FEATURE_AARCH64, /* supports 64 bit mode */
|
ARM_FEATURE_AARCH64, /* supports 64 bit mode */
|
||||||
ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */
|
ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */
|
||||||
ARM_FEATURE_CBAR, /* has cp15 CBAR */
|
ARM_FEATURE_CBAR, /* has cp15 CBAR */
|
||||||
|
ARM_FEATURE_CRC, /* ARMv8 CRC instructions */
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int arm_feature(CPUARMState *env, int feature)
|
static inline int arm_feature(CPUARMState *env, int feature)
|
||||||
|
@ -622,6 +634,22 @@ static inline int arm_feature(CPUARMState *env, int feature)
|
||||||
return (env->features & (1ULL << feature)) != 0;
|
return (env->features & (1ULL << feature)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true if the specified exception level is running in AArch64 state. */
|
||||||
|
static inline bool arm_el_is_aa64(CPUARMState *env, int el)
|
||||||
|
{
|
||||||
|
/* We don't currently support EL2 or EL3, and this isn't valid for EL0
|
||||||
|
* (if we're in EL0, is_a64() is what you want, and if we're not in EL0
|
||||||
|
* then the state of EL0 isn't well defined.)
|
||||||
|
*/
|
||||||
|
assert(el == 1);
|
||||||
|
/* AArch64-capable CPUs always run with EL1 in AArch64 mode. This
|
||||||
|
* is a QEMU-imposed simplification which we may wish to change later.
|
||||||
|
* If we in future support EL2 and/or EL3, then the state of lower
|
||||||
|
* exception levels is controlled by the HCR.RW and SCR.RW bits.
|
||||||
|
*/
|
||||||
|
return arm_feature(env, ARM_FEATURE_AARCH64);
|
||||||
|
}
|
||||||
|
|
||||||
void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
||||||
|
|
||||||
/* Interface between CPU and Interrupt controller. */
|
/* Interface between CPU and Interrupt controller. */
|
||||||
|
@ -731,7 +759,8 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
|
||||||
#define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8))
|
#define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8))
|
||||||
#define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8))
|
#define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8))
|
||||||
#define ARM_CP_NZCV (ARM_CP_SPECIAL | (3 << 8))
|
#define ARM_CP_NZCV (ARM_CP_SPECIAL | (3 << 8))
|
||||||
#define ARM_LAST_SPECIAL ARM_CP_NZCV
|
#define ARM_CP_CURRENTEL (ARM_CP_SPECIAL | (4 << 8))
|
||||||
|
#define ARM_LAST_SPECIAL ARM_CP_CURRENTEL
|
||||||
/* Used only as a terminator for ARMCPRegInfo lists */
|
/* Used only as a terminator for ARMCPRegInfo lists */
|
||||||
#define ARM_CP_SENTINEL 0xffff
|
#define ARM_CP_SENTINEL 0xffff
|
||||||
/* Mask of only the flag bits in a type field */
|
/* Mask of only the flag bits in a type field */
|
||||||
|
@ -959,6 +988,14 @@ uint64_t arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri);
|
||||||
*/
|
*/
|
||||||
void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque);
|
void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque);
|
||||||
|
|
||||||
|
/* Return true if this reginfo struct's field in the cpu state struct
|
||||||
|
* is 64 bits wide.
|
||||||
|
*/
|
||||||
|
static inline bool cpreg_field_is_64bit(const ARMCPRegInfo *ri)
|
||||||
|
{
|
||||||
|
return (ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool cp_access_ok(int current_pl,
|
static inline bool cp_access_ok(int current_pl,
|
||||||
const ARMCPRegInfo *ri, int isread)
|
const ARMCPRegInfo *ri, int isread)
|
||||||
{
|
{
|
||||||
|
@ -1043,7 +1080,7 @@ static inline CPUARMState *cpu_init(const char *cpu_model)
|
||||||
#define MMU_USER_IDX 1
|
#define MMU_USER_IDX 1
|
||||||
static inline int cpu_mmu_index (CPUARMState *env)
|
static inline int cpu_mmu_index (CPUARMState *env)
|
||||||
{
|
{
|
||||||
return (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR ? 1 : 0;
|
return arm_current_pl(env) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "exec/cpu-all.h"
|
#include "exec/cpu-all.h"
|
||||||
|
@ -1070,7 +1107,9 @@ static inline int cpu_mmu_index (CPUARMState *env)
|
||||||
#define ARM_TBFLAG_BSWAP_CODE_SHIFT 16
|
#define ARM_TBFLAG_BSWAP_CODE_SHIFT 16
|
||||||
#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT)
|
#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT)
|
||||||
|
|
||||||
/* Bit usage when in AArch64 state: currently no bits defined */
|
/* Bit usage when in AArch64 state */
|
||||||
|
#define ARM_TBFLAG_AA64_EL_SHIFT 0
|
||||||
|
#define ARM_TBFLAG_AA64_EL_MASK (0x3 << ARM_TBFLAG_AA64_EL_SHIFT)
|
||||||
|
|
||||||
/* some convenience accessor macros */
|
/* some convenience accessor macros */
|
||||||
#define ARM_TBFLAG_AARCH64_STATE(F) \
|
#define ARM_TBFLAG_AARCH64_STATE(F) \
|
||||||
|
@ -1089,13 +1128,16 @@ static inline int cpu_mmu_index (CPUARMState *env)
|
||||||
(((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT)
|
(((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT)
|
||||||
#define ARM_TBFLAG_BSWAP_CODE(F) \
|
#define ARM_TBFLAG_BSWAP_CODE(F) \
|
||||||
(((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT)
|
(((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT)
|
||||||
|
#define ARM_TBFLAG_AA64_EL(F) \
|
||||||
|
(((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT)
|
||||||
|
|
||||||
static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
|
static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
|
||||||
target_ulong *cs_base, int *flags)
|
target_ulong *cs_base, int *flags)
|
||||||
{
|
{
|
||||||
if (is_a64(env)) {
|
if (is_a64(env)) {
|
||||||
*pc = env->pc;
|
*pc = env->pc;
|
||||||
*flags = ARM_TBFLAG_AARCH64_STATE_MASK;
|
*flags = ARM_TBFLAG_AARCH64_STATE_MASK
|
||||||
|
| (arm_current_pl(env) << ARM_TBFLAG_AA64_EL_SHIFT);
|
||||||
} else {
|
} else {
|
||||||
int privmode;
|
int privmode;
|
||||||
*pc = env->regs[15];
|
*pc = env->regs[15];
|
||||||
|
|
|
@ -45,6 +45,7 @@ static void aarch64_any_initfn(Object *obj)
|
||||||
set_feature(&cpu->env, ARM_FEATURE_ARM_DIV);
|
set_feature(&cpu->env, ARM_FEATURE_ARM_DIV);
|
||||||
set_feature(&cpu->env, ARM_FEATURE_V7MP);
|
set_feature(&cpu->env, ARM_FEATURE_V7MP);
|
||||||
set_feature(&cpu->env, ARM_FEATURE_AARCH64);
|
set_feature(&cpu->env, ARM_FEATURE_AARCH64);
|
||||||
|
cpu->ctr = 0x80030003; /* 32 byte I and D cacheline size, VIPT icache */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include "sysemu/arch_init.h"
|
#include "sysemu/arch_init.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
|
#include "qemu/crc32c.h"
|
||||||
|
#include <zlib.h> /* For crc32 */
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
static inline int get_phys_addr(CPUARMState *env, uint32_t address,
|
static inline int get_phys_addr(CPUARMState *env, uint32_t address,
|
||||||
|
@ -109,7 +111,7 @@ static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
|
||||||
|
|
||||||
static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
{
|
{
|
||||||
if (ri->type & ARM_CP_64BIT) {
|
if (cpreg_field_is_64bit(ri)) {
|
||||||
return CPREG_FIELD64(env, ri);
|
return CPREG_FIELD64(env, ri);
|
||||||
} else {
|
} else {
|
||||||
return CPREG_FIELD32(env, ri);
|
return CPREG_FIELD32(env, ri);
|
||||||
|
@ -119,7 +121,7 @@ static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
static void raw_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
static void raw_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
uint64_t value)
|
uint64_t value)
|
||||||
{
|
{
|
||||||
if (ri->type & ARM_CP_64BIT) {
|
if (cpreg_field_is_64bit(ri)) {
|
||||||
CPREG_FIELD64(env, ri) = value;
|
CPREG_FIELD64(env, ri) = value;
|
||||||
} else {
|
} else {
|
||||||
CPREG_FIELD32(env, ri) = value;
|
CPREG_FIELD32(env, ri) = value;
|
||||||
|
@ -458,7 +460,8 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
|
||||||
*/
|
*/
|
||||||
{ .name = "WFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1,
|
{ .name = "WFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1,
|
||||||
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, },
|
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, },
|
||||||
{ .name = "CPACR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2,
|
{ .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3,
|
||||||
|
.crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2,
|
||||||
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_coproc),
|
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_coproc),
|
||||||
.resetvalue = 0, .writefn = cpacr_write },
|
.resetvalue = 0, .writefn = cpacr_write },
|
||||||
REGINFO_SENTINEL
|
REGINFO_SENTINEL
|
||||||
|
@ -533,6 +536,12 @@ static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
uint64_t value)
|
uint64_t value)
|
||||||
{
|
{
|
||||||
|
/* Note that even though the AArch64 view of this register has bits
|
||||||
|
* [10:0] all RES0 we can only mask the bottom 5, to comply with the
|
||||||
|
* architectural requirements for bits which are RES0 only in some
|
||||||
|
* contexts. (ARMv8 would permit us to do no masking at all, but ARMv7
|
||||||
|
* requires the bottom five bits to be RAZ/WI because they're UNK/SBZP.)
|
||||||
|
*/
|
||||||
env->cp15.c12_vbar = value & ~0x1Ful;
|
env->cp15.c12_vbar = value & ~0x1Ful;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,16 +631,19 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
|
||||||
.access = PL1_RW, .type = ARM_CP_NO_MIGRATE,
|
.access = PL1_RW, .type = ARM_CP_NO_MIGRATE,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
|
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
|
||||||
.resetvalue = 0, .writefn = pmintenclr_write, },
|
.resetvalue = 0, .writefn = pmintenclr_write, },
|
||||||
{ .name = "VBAR", .cp = 15, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
|
{ .name = "VBAR", .state = ARM_CP_STATE_BOTH,
|
||||||
|
.opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||||
.access = PL1_RW, .writefn = vbar_write,
|
.access = PL1_RW, .writefn = vbar_write,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c12_vbar),
|
.fieldoffset = offsetof(CPUARMState, cp15.c12_vbar),
|
||||||
.resetvalue = 0 },
|
.resetvalue = 0 },
|
||||||
{ .name = "SCR", .cp = 15, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0,
|
{ .name = "SCR", .cp = 15, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0,
|
||||||
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_scr),
|
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_scr),
|
||||||
.resetvalue = 0, },
|
.resetvalue = 0, },
|
||||||
{ .name = "CCSIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0,
|
{ .name = "CCSIDR", .state = ARM_CP_STATE_BOTH,
|
||||||
|
.opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0,
|
||||||
.access = PL1_R, .readfn = ccsidr_read, .type = ARM_CP_NO_MIGRATE },
|
.access = PL1_R, .readfn = ccsidr_read, .type = ARM_CP_NO_MIGRATE },
|
||||||
{ .name = "CSSELR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0,
|
{ .name = "CSSELR", .state = ARM_CP_STATE_BOTH,
|
||||||
|
.opc0 = 3, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0,
|
||||||
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c0_cssel),
|
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c0_cssel),
|
||||||
.writefn = csselr_write, .resetvalue = 0 },
|
.writefn = csselr_write, .resetvalue = 0 },
|
||||||
/* Auxiliary ID register: this actually has an IMPDEF value but for now
|
/* Auxiliary ID register: this actually has an IMPDEF value but for now
|
||||||
|
@ -639,6 +651,26 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
|
||||||
*/
|
*/
|
||||||
{ .name = "AIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 7,
|
{ .name = "AIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 7,
|
||||||
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
|
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||||
|
/* MAIR can just read-as-written because we don't implement caches
|
||||||
|
* and so don't need to care about memory attributes.
|
||||||
|
*/
|
||||||
|
{ .name = "MAIR_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0,
|
||||||
|
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el1),
|
||||||
|
.resetvalue = 0 },
|
||||||
|
/* For non-long-descriptor page tables these are PRRR and NMRR;
|
||||||
|
* regardless they still act as reads-as-written for QEMU.
|
||||||
|
* The override is necessary because of the overly-broad TLB_LOCKDOWN
|
||||||
|
* definition.
|
||||||
|
*/
|
||||||
|
{ .name = "MAIR0", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE,
|
||||||
|
.cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW,
|
||||||
|
.fieldoffset = offsetoflow32(CPUARMState, cp15.mair_el1),
|
||||||
|
.resetfn = arm_cp_reset_ignore },
|
||||||
|
{ .name = "MAIR1", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE,
|
||||||
|
.cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, .access = PL1_RW,
|
||||||
|
.fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el1),
|
||||||
|
.resetfn = arm_cp_reset_ignore },
|
||||||
REGINFO_SENTINEL
|
REGINFO_SENTINEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -872,30 +904,55 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||||
* Our reset value matches the fixed frequency we implement the timer at.
|
* Our reset value matches the fixed frequency we implement the timer at.
|
||||||
*/
|
*/
|
||||||
{ .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0,
|
{ .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||||
.access = PL1_RW | PL0_R,
|
.type = ARM_CP_NO_MIGRATE,
|
||||||
|
.access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access,
|
||||||
|
.fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq),
|
||||||
|
.resetfn = arm_cp_reset_ignore,
|
||||||
|
},
|
||||||
|
{ .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0,
|
||||||
|
.access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq),
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq),
|
||||||
.resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE,
|
.resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE,
|
||||||
.accessfn = gt_cntfrq_access,
|
|
||||||
},
|
},
|
||||||
/* overall control: mostly access permissions */
|
/* overall control: mostly access permissions */
|
||||||
{ .name = "CNTKCTL", .cp = 15, .crn = 14, .crm = 1, .opc1 = 0, .opc2 = 0,
|
{ .name = "CNTKCTL", .state = ARM_CP_STATE_BOTH,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 14, .crm = 1, .opc2 = 0,
|
||||||
.access = PL1_RW,
|
.access = PL1_RW,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl),
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl),
|
||||||
.resetvalue = 0,
|
.resetvalue = 0,
|
||||||
},
|
},
|
||||||
/* per-timer control */
|
/* per-timer control */
|
||||||
{ .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1,
|
{ .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1,
|
||||||
|
.type = ARM_CP_IO | ARM_CP_NO_MIGRATE, .access = PL1_RW | PL0_R,
|
||||||
|
.accessfn = gt_ptimer_access,
|
||||||
|
.fieldoffset = offsetoflow32(CPUARMState,
|
||||||
|
cp15.c14_timer[GTIMER_PHYS].ctl),
|
||||||
|
.resetfn = arm_cp_reset_ignore,
|
||||||
|
.writefn = gt_ctl_write, .raw_writefn = raw_write,
|
||||||
|
},
|
||||||
|
{ .name = "CNTP_CTL_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1,
|
||||||
.type = ARM_CP_IO, .access = PL1_RW | PL0_R,
|
.type = ARM_CP_IO, .access = PL1_RW | PL0_R,
|
||||||
|
.accessfn = gt_ptimer_access,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
|
||||||
.resetvalue = 0,
|
.resetvalue = 0,
|
||||||
.accessfn = gt_ptimer_access,
|
|
||||||
.writefn = gt_ctl_write, .raw_writefn = raw_write,
|
.writefn = gt_ctl_write, .raw_writefn = raw_write,
|
||||||
},
|
},
|
||||||
{ .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1,
|
{ .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1,
|
||||||
|
.type = ARM_CP_IO | ARM_CP_NO_MIGRATE, .access = PL1_RW | PL0_R,
|
||||||
|
.accessfn = gt_vtimer_access,
|
||||||
|
.fieldoffset = offsetoflow32(CPUARMState,
|
||||||
|
cp15.c14_timer[GTIMER_VIRT].ctl),
|
||||||
|
.resetfn = arm_cp_reset_ignore,
|
||||||
|
.writefn = gt_ctl_write, .raw_writefn = raw_write,
|
||||||
|
},
|
||||||
|
{ .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1,
|
||||||
.type = ARM_CP_IO, .access = PL1_RW | PL0_R,
|
.type = ARM_CP_IO, .access = PL1_RW | PL0_R,
|
||||||
|
.accessfn = gt_vtimer_access,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
|
||||||
.resetvalue = 0,
|
.resetvalue = 0,
|
||||||
.accessfn = gt_vtimer_access,
|
|
||||||
.writefn = gt_ctl_write, .raw_writefn = raw_write,
|
.writefn = gt_ctl_write, .raw_writefn = raw_write,
|
||||||
},
|
},
|
||||||
/* TimerValue views: a 32 bit downcounting view of the underlying state */
|
/* TimerValue views: a 32 bit downcounting view of the underlying state */
|
||||||
|
@ -904,37 +961,73 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||||
.accessfn = gt_ptimer_access,
|
.accessfn = gt_ptimer_access,
|
||||||
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
||||||
},
|
},
|
||||||
|
{ .name = "CNTP_TVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0,
|
||||||
|
.type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R,
|
||||||
|
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
||||||
|
},
|
||||||
{ .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0,
|
{ .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0,
|
||||||
.type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R,
|
.type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R,
|
||||||
.accessfn = gt_vtimer_access,
|
.accessfn = gt_vtimer_access,
|
||||||
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
||||||
},
|
},
|
||||||
|
{ .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0,
|
||||||
|
.type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R,
|
||||||
|
.readfn = gt_tval_read, .writefn = gt_tval_write,
|
||||||
|
},
|
||||||
/* The counter itself */
|
/* The counter itself */
|
||||||
{ .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0,
|
{ .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0,
|
||||||
.access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO,
|
.access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO,
|
||||||
.accessfn = gt_pct_access,
|
.accessfn = gt_pct_access,
|
||||||
|
.readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore,
|
||||||
|
},
|
||||||
|
{ .name = "CNTPCT_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 1,
|
||||||
|
.access = PL0_R, .type = ARM_CP_NO_MIGRATE | ARM_CP_IO,
|
||||||
|
.accessfn = gt_pct_access,
|
||||||
.readfn = gt_cnt_read, .resetfn = gt_cnt_reset,
|
.readfn = gt_cnt_read, .resetfn = gt_cnt_reset,
|
||||||
},
|
},
|
||||||
{ .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1,
|
{ .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1,
|
||||||
.access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO,
|
.access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO,
|
||||||
.accessfn = gt_vct_access,
|
.accessfn = gt_vct_access,
|
||||||
|
.readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore,
|
||||||
|
},
|
||||||
|
{ .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2,
|
||||||
|
.access = PL0_R, .type = ARM_CP_NO_MIGRATE | ARM_CP_IO,
|
||||||
|
.accessfn = gt_vct_access,
|
||||||
.readfn = gt_cnt_read, .resetfn = gt_cnt_reset,
|
.readfn = gt_cnt_read, .resetfn = gt_cnt_reset,
|
||||||
},
|
},
|
||||||
/* Comparison value, indicating when the timer goes off */
|
/* Comparison value, indicating when the timer goes off */
|
||||||
{ .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2,
|
{ .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2,
|
||||||
.access = PL1_RW | PL0_R,
|
.access = PL1_RW | PL0_R,
|
||||||
.type = ARM_CP_64BIT | ARM_CP_IO,
|
.type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_MIGRATE,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
||||||
.resetvalue = 0,
|
.accessfn = gt_ptimer_access, .resetfn = arm_cp_reset_ignore,
|
||||||
.accessfn = gt_ptimer_access,
|
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
||||||
|
},
|
||||||
|
{ .name = "CNTP_CVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2,
|
||||||
|
.access = PL1_RW | PL0_R,
|
||||||
|
.type = ARM_CP_IO,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
||||||
|
.resetvalue = 0, .accessfn = gt_vtimer_access,
|
||||||
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
||||||
},
|
},
|
||||||
{ .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3,
|
{ .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3,
|
||||||
.access = PL1_RW | PL0_R,
|
.access = PL1_RW | PL0_R,
|
||||||
.type = ARM_CP_64BIT | ARM_CP_IO,
|
.type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_MIGRATE,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
|
||||||
.resetvalue = 0,
|
.accessfn = gt_vtimer_access, .resetfn = arm_cp_reset_ignore,
|
||||||
.accessfn = gt_vtimer_access,
|
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
||||||
|
},
|
||||||
|
{ .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2,
|
||||||
|
.access = PL1_RW | PL0_R,
|
||||||
|
.type = ARM_CP_IO,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
|
||||||
|
.resetvalue = 0, .accessfn = gt_vtimer_access,
|
||||||
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
.writefn = gt_cval_write, .raw_writefn = raw_write,
|
||||||
},
|
},
|
||||||
REGINFO_SENTINEL
|
REGINFO_SENTINEL
|
||||||
|
@ -1031,8 +1124,8 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||||
env->cp15.c7_par = phys_addr & 0xfffff000;
|
env->cp15.c7_par = phys_addr & 0xfffff000;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
env->cp15.c7_par = ((ret & (10 << 1)) >> 5) |
|
env->cp15.c7_par = ((ret & (1 << 10)) >> 5) |
|
||||||
((ret & (12 << 1)) >> 6) |
|
((ret & (1 << 12)) >> 6) |
|
||||||
((ret & 0xf) << 1) | 1;
|
((ret & 0xf) << 1) | 1;
|
||||||
}
|
}
|
||||||
env->cp15.c7_par_hi = 0;
|
env->cp15.c7_par_hi = 0;
|
||||||
|
@ -1193,6 +1286,26 @@ static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
env->cp15.c2_mask = 0;
|
env->cp15.c2_mask = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
/* For AArch64 the A1 bit could result in a change of ASID, so TLB flush. */
|
||||||
|
tlb_flush(env, 1);
|
||||||
|
env->cp15.c2_control = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
/* 64 bit accesses to the TTBRs can change the ASID and so we
|
||||||
|
* must flush the TLB.
|
||||||
|
*/
|
||||||
|
if (cpreg_field_is_64bit(ri)) {
|
||||||
|
tlb_flush(env, 1);
|
||||||
|
}
|
||||||
|
raw_write(env, ri, value);
|
||||||
|
}
|
||||||
|
|
||||||
static const ARMCPRegInfo vmsa_cp_reginfo[] = {
|
static const ARMCPRegInfo vmsa_cp_reginfo[] = {
|
||||||
{ .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
|
{ .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||||
.access = PL1_RW,
|
.access = PL1_RW,
|
||||||
|
@ -1200,16 +1313,23 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
|
||||||
{ .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
|
{ .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
|
||||||
.access = PL1_RW,
|
.access = PL1_RW,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, },
|
.fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, },
|
||||||
{ .name = "TTBR0", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
|
{ .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH,
|
||||||
.access = PL1_RW,
|
.opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, },
|
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1),
|
||||||
{ .name = "TTBR1", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
|
.writefn = vmsa_ttbr_write, .resetvalue = 0 },
|
||||||
.access = PL1_RW,
|
{ .name = "TTBR1_EL1", .state = ARM_CP_STATE_BOTH,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c2_base1), .resetvalue = 0, },
|
.opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
|
||||||
{ .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
|
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1),
|
||||||
.access = PL1_RW, .writefn = vmsa_ttbcr_write,
|
.writefn = vmsa_ttbr_write, .resetvalue = 0 },
|
||||||
.resetfn = vmsa_ttbcr_reset, .raw_writefn = vmsa_ttbcr_raw_write,
|
{ .name = "TCR_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
|
||||||
|
.access = PL1_RW, .writefn = vmsa_tcr_el1_write,
|
||||||
|
.resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c2_control) },
|
.fieldoffset = offsetof(CPUARMState, cp15.c2_control) },
|
||||||
|
{ .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
|
||||||
|
.access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .writefn = vmsa_ttbcr_write,
|
||||||
|
.resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write,
|
||||||
|
.fieldoffset = offsetoflow32(CPUARMState, cp15.c2_control) },
|
||||||
{ .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
|
{ .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||||
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data),
|
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data),
|
||||||
.resetvalue = 0, },
|
.resetvalue = 0, },
|
||||||
|
@ -1379,7 +1499,8 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(arm_env_get_cpu(env));
|
CPUState *cs = CPU(arm_env_get_cpu(env));
|
||||||
uint32_t mpidr = cs->cpu_index;
|
uint32_t mpidr = cs->cpu_index;
|
||||||
/* We don't support setting cluster ID ([8..11])
|
/* We don't support setting cluster ID ([8..11]) (known as Aff1
|
||||||
|
* in later ARM ARM versions), or any of the higher affinity level fields,
|
||||||
* so these bits always RAZ.
|
* so these bits always RAZ.
|
||||||
*/
|
*/
|
||||||
if (arm_feature(env, ARM_FEATURE_V7MP)) {
|
if (arm_feature(env, ARM_FEATURE_V7MP)) {
|
||||||
|
@ -1394,7 +1515,8 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const ARMCPRegInfo mpidr_cp_reginfo[] = {
|
static const ARMCPRegInfo mpidr_cp_reginfo[] = {
|
||||||
{ .name = "MPIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5,
|
{ .name = "MPIDR", .state = ARM_CP_STATE_BOTH,
|
||||||
|
.opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5,
|
||||||
.access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_MIGRATE },
|
.access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_MIGRATE },
|
||||||
REGINFO_SENTINEL
|
REGINFO_SENTINEL
|
||||||
};
|
};
|
||||||
|
@ -1417,57 +1539,15 @@ static void par64_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
env->cp15.c7_par = 0;
|
env->cp15.c7_par = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t ttbr064_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
|
||||||
{
|
|
||||||
return ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ttbr064_raw_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
||||||
uint64_t value)
|
|
||||||
{
|
|
||||||
env->cp15.c2_base0_hi = value >> 32;
|
|
||||||
env->cp15.c2_base0 = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ttbr064_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
||||||
uint64_t value)
|
|
||||||
{
|
|
||||||
/* Writes to the 64 bit format TTBRs may change the ASID */
|
|
||||||
tlb_flush(env, 1);
|
|
||||||
ttbr064_raw_write(env, ri, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ttbr064_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
|
||||||
{
|
|
||||||
env->cp15.c2_base0_hi = 0;
|
|
||||||
env->cp15.c2_base0 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t ttbr164_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
|
||||||
{
|
|
||||||
return ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ttbr164_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
||||||
uint64_t value)
|
|
||||||
{
|
|
||||||
env->cp15.c2_base1_hi = value >> 32;
|
|
||||||
env->cp15.c2_base1 = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ttbr164_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
|
||||||
{
|
|
||||||
env->cp15.c2_base1_hi = 0;
|
|
||||||
env->cp15.c2_base1 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const ARMCPRegInfo lpae_cp_reginfo[] = {
|
static const ARMCPRegInfo lpae_cp_reginfo[] = {
|
||||||
/* NOP AMAIR0/1: the override is because these clash with the rather
|
/* NOP AMAIR0/1: the override is because these clash with the rather
|
||||||
* broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo.
|
* broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo.
|
||||||
*/
|
*/
|
||||||
{ .name = "AMAIR0", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0,
|
{ .name = "AMAIR0", .state = ARM_CP_STATE_BOTH,
|
||||||
|
.opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0,
|
||||||
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
|
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
|
||||||
.resetvalue = 0 },
|
.resetvalue = 0 },
|
||||||
|
/* AMAIR1 is mapped to AMAIR_EL1[63:32] */
|
||||||
{ .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1,
|
{ .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1,
|
||||||
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
|
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
|
||||||
.resetvalue = 0 },
|
.resetvalue = 0 },
|
||||||
|
@ -1480,12 +1560,13 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = {
|
||||||
.access = PL1_RW, .type = ARM_CP_64BIT,
|
.access = PL1_RW, .type = ARM_CP_64BIT,
|
||||||
.readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset },
|
.readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset },
|
||||||
{ .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0,
|
{ .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0,
|
||||||
.access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr064_read,
|
.access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE,
|
||||||
.writefn = ttbr064_write, .raw_writefn = ttbr064_raw_write,
|
.fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1),
|
||||||
.resetfn = ttbr064_reset },
|
.writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore },
|
||||||
{ .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1,
|
{ .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1,
|
||||||
.access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr164_read,
|
.access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE,
|
||||||
.writefn = ttbr164_write, .resetfn = ttbr164_reset },
|
.fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1),
|
||||||
|
.writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore },
|
||||||
REGINFO_SENTINEL
|
REGINFO_SENTINEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1511,6 +1592,42 @@ static void aa64_fpsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
vfp_set_fpsr(env, value);
|
vfp_set_fpsr(env, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CPAccessResult aa64_cacheop_access(CPUARMState *env,
|
||||||
|
const ARMCPRegInfo *ri)
|
||||||
|
{
|
||||||
|
/* Cache invalidate/clean: NOP, but EL0 must UNDEF unless
|
||||||
|
* SCTLR_EL1.UCI is set.
|
||||||
|
*/
|
||||||
|
if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UCI)) {
|
||||||
|
return CP_ACCESS_TRAP;
|
||||||
|
}
|
||||||
|
return CP_ACCESS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tlbi_aa64_va_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
/* Invalidate by VA (AArch64 version) */
|
||||||
|
uint64_t pageaddr = value << 12;
|
||||||
|
tlb_flush_page(env, pageaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tlbi_aa64_vaa_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
/* Invalidate by VA, all ASIDs (AArch64 version) */
|
||||||
|
uint64_t pageaddr = value << 12;
|
||||||
|
tlb_flush_page(env, pageaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tlbi_aa64_asid_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
/* Invalidate by ASID (AArch64 version) */
|
||||||
|
int asid = extract64(value, 48, 16);
|
||||||
|
tlb_flush(env, asid == 0);
|
||||||
|
}
|
||||||
|
|
||||||
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.
|
||||||
|
@ -1524,13 +1641,6 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
|
||||||
{ .name = "FPSR", .state = ARM_CP_STATE_AA64,
|
{ .name = "FPSR", .state = ARM_CP_STATE_AA64,
|
||||||
.opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 4,
|
.opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 4,
|
||||||
.access = PL0_RW, .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write },
|
.access = PL0_RW, .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write },
|
||||||
/* This claims a 32 byte cacheline size for icache and dcache, VIPT icache.
|
|
||||||
* It will eventually need to have a CPU-specified reset value.
|
|
||||||
*/
|
|
||||||
{ .name = "CTR_EL0", .state = ARM_CP_STATE_AA64,
|
|
||||||
.opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0,
|
|
||||||
.access = PL0_R, .type = ARM_CP_CONST,
|
|
||||||
.resetvalue = 0x80030003 },
|
|
||||||
/* Prohibit use of DC ZVA. OPTME: implement DC ZVA and allow its use.
|
/* Prohibit use of DC ZVA. OPTME: implement DC ZVA and allow its use.
|
||||||
* For system mode the DZP bit here will need to be computed, not constant.
|
* For system mode the DZP bit here will need to be computed, not constant.
|
||||||
*/
|
*/
|
||||||
|
@ -1538,6 +1648,103 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
|
||||||
.opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0,
|
.opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0,
|
||||||
.access = PL0_R, .type = ARM_CP_CONST,
|
.access = PL0_R, .type = ARM_CP_CONST,
|
||||||
.resetvalue = 0x10 },
|
.resetvalue = 0x10 },
|
||||||
|
{ .name = "CURRENTEL", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .opc2 = 2, .crn = 4, .crm = 2,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CURRENTEL },
|
||||||
|
/* Cache ops: all NOPs since we don't emulate caches */
|
||||||
|
{ .name = "IC_IALLUIS", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NOP },
|
||||||
|
{ .name = "IC_IALLU", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 0,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NOP },
|
||||||
|
{ .name = "IC_IVAU", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 3, .crn = 7, .crm = 5, .opc2 = 1,
|
||||||
|
.access = PL0_W, .type = ARM_CP_NOP,
|
||||||
|
.accessfn = aa64_cacheop_access },
|
||||||
|
{ .name = "DC_IVAC", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 1,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NOP },
|
||||||
|
{ .name = "DC_ISW", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 2,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NOP },
|
||||||
|
{ .name = "DC_CVAC", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 1,
|
||||||
|
.access = PL0_W, .type = ARM_CP_NOP,
|
||||||
|
.accessfn = aa64_cacheop_access },
|
||||||
|
{ .name = "DC_CSW", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 2,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NOP },
|
||||||
|
{ .name = "DC_CVAU", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 3, .crn = 7, .crm = 11, .opc2 = 1,
|
||||||
|
.access = PL0_W, .type = ARM_CP_NOP,
|
||||||
|
.accessfn = aa64_cacheop_access },
|
||||||
|
{ .name = "DC_CIVAC", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 1,
|
||||||
|
.access = PL0_W, .type = ARM_CP_NOP,
|
||||||
|
.accessfn = aa64_cacheop_access },
|
||||||
|
{ .name = "DC_CISW", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NOP },
|
||||||
|
/* TLBI operations */
|
||||||
|
{ .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 0,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbiall_write },
|
||||||
|
{ .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 1,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_va_write },
|
||||||
|
{ .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 2,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_asid_write },
|
||||||
|
{ .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 3,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_vaa_write },
|
||||||
|
{ .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 5,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_va_write },
|
||||||
|
{ .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 7,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_vaa_write },
|
||||||
|
{ .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 0,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbiall_write },
|
||||||
|
{ .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 1,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_va_write },
|
||||||
|
{ .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 2,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_asid_write },
|
||||||
|
{ .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 3,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_vaa_write },
|
||||||
|
{ .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 5,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_va_write },
|
||||||
|
{ .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 7,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NO_MIGRATE,
|
||||||
|
.writefn = tlbi_aa64_vaa_write },
|
||||||
|
/* Dummy implementation of monitor debug system control register:
|
||||||
|
* we don't support debug.
|
||||||
|
*/
|
||||||
|
{ .name = "MDSCR_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2,
|
||||||
|
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||||
|
/* We define a dummy WI OSLAR_EL1, because Linux writes to it. */
|
||||||
|
{ .name = "OSLAR_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4,
|
||||||
|
.access = PL1_W, .type = ARM_CP_NOP },
|
||||||
REGINFO_SENTINEL
|
REGINFO_SENTINEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1550,6 +1757,48 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
tlb_flush(env, 1);
|
tlb_flush(env, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
|
{
|
||||||
|
/* Only accessible in EL0 if SCTLR.UCT is set (and only in AArch64,
|
||||||
|
* but the AArch32 CTR has its own reginfo struct)
|
||||||
|
*/
|
||||||
|
if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UCT)) {
|
||||||
|
return CP_ACCESS_TRAP;
|
||||||
|
}
|
||||||
|
return CP_ACCESS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void define_aarch64_debug_regs(ARMCPU *cpu)
|
||||||
|
{
|
||||||
|
/* Define breakpoint and watchpoint registers. These do nothing
|
||||||
|
* but read as written, for now.
|
||||||
|
*/
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
ARMCPRegInfo dbgregs[] = {
|
||||||
|
{ .name = "DBGBVR", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4,
|
||||||
|
.access = PL1_RW,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]) },
|
||||||
|
{ .name = "DBGBCR", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5,
|
||||||
|
.access = PL1_RW,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]) },
|
||||||
|
{ .name = "DBGWVR", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6,
|
||||||
|
.access = PL1_RW,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]) },
|
||||||
|
{ .name = "DBGWCR", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7,
|
||||||
|
.access = PL1_RW,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]) },
|
||||||
|
REGINFO_SENTINEL
|
||||||
|
};
|
||||||
|
define_arm_cp_regs(cpu, dbgregs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void register_cp_regs_for_features(ARMCPU *cpu)
|
void register_cp_regs_for_features(ARMCPU *cpu)
|
||||||
{
|
{
|
||||||
/* Register all the coprocessor registers based on feature bits */
|
/* Register all the coprocessor registers based on feature bits */
|
||||||
|
@ -1634,7 +1883,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||||
.raw_writefn = raw_write,
|
.raw_writefn = raw_write,
|
||||||
};
|
};
|
||||||
ARMCPRegInfo clidr = {
|
ARMCPRegInfo clidr = {
|
||||||
.name = "CLIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,
|
.name = "CLIDR", .state = ARM_CP_STATE_BOTH,
|
||||||
|
.opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,
|
||||||
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->clidr
|
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->clidr
|
||||||
};
|
};
|
||||||
define_one_arm_cp_reg(cpu, &pmcr);
|
define_one_arm_cp_reg(cpu, &pmcr);
|
||||||
|
@ -1644,7 +1894,53 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||||
define_arm_cp_regs(cpu, not_v7_cp_reginfo);
|
define_arm_cp_regs(cpu, not_v7_cp_reginfo);
|
||||||
}
|
}
|
||||||
if (arm_feature(env, ARM_FEATURE_V8)) {
|
if (arm_feature(env, ARM_FEATURE_V8)) {
|
||||||
|
/* AArch64 ID registers, which all have impdef reset values */
|
||||||
|
ARMCPRegInfo v8_idregs[] = {
|
||||||
|
{ .name = "ID_AA64PFR0_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 0,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64pfr0 },
|
||||||
|
{ .name = "ID_AA64PFR1_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64pfr1},
|
||||||
|
{ .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64dfr0 },
|
||||||
|
{ .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64dfr1 },
|
||||||
|
{ .name = "ID_AA64AFR0_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64afr0 },
|
||||||
|
{ .name = "ID_AA64AFR1_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 5,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64afr1 },
|
||||||
|
{ .name = "ID_AA64ISAR0_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 0,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64isar0 },
|
||||||
|
{ .name = "ID_AA64ISAR1_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64isar1 },
|
||||||
|
{ .name = "ID_AA64MMFR0_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64mmfr0 },
|
||||||
|
{ .name = "ID_AA64MMFR1_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1,
|
||||||
|
.access = PL1_R, .type = ARM_CP_CONST,
|
||||||
|
.resetvalue = cpu->id_aa64mmfr1 },
|
||||||
|
REGINFO_SENTINEL
|
||||||
|
};
|
||||||
|
define_arm_cp_regs(cpu, v8_idregs);
|
||||||
define_arm_cp_regs(cpu, v8_cp_reginfo);
|
define_arm_cp_regs(cpu, v8_cp_reginfo);
|
||||||
|
define_aarch64_debug_regs(cpu);
|
||||||
}
|
}
|
||||||
if (arm_feature(env, ARM_FEATURE_MPU)) {
|
if (arm_feature(env, ARM_FEATURE_MPU)) {
|
||||||
/* These are the MPU registers prior to PMSAv6. Any new
|
/* These are the MPU registers prior to PMSAv6. Any new
|
||||||
|
@ -1710,9 +2006,16 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||||
.writefn = arm_cp_write_ignore, .raw_writefn = raw_write,
|
.writefn = arm_cp_write_ignore, .raw_writefn = raw_write,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid),
|
.fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid),
|
||||||
.type = ARM_CP_OVERRIDE },
|
.type = ARM_CP_OVERRIDE },
|
||||||
|
{ .name = "MIDR_EL1", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 0, .opc2 = 0, .crn = 0, .crm = 0,
|
||||||
|
.access = PL1_R, .resetvalue = cpu->midr, .type = ARM_CP_CONST },
|
||||||
{ .name = "CTR",
|
{ .name = "CTR",
|
||||||
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1,
|
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1,
|
||||||
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr },
|
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr },
|
||||||
|
{ .name = "CTR_EL0", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0,
|
||||||
|
.access = PL0_R, .accessfn = ctr_el0_access,
|
||||||
|
.type = ARM_CP_CONST, .resetvalue = cpu->ctr },
|
||||||
{ .name = "TCMTR",
|
{ .name = "TCMTR",
|
||||||
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2,
|
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2,
|
||||||
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
|
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||||
|
@ -1783,7 +2086,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
|
||||||
/* Generic registers whose values depend on the implementation */
|
/* Generic registers whose values depend on the implementation */
|
||||||
{
|
{
|
||||||
ARMCPRegInfo sctlr = {
|
ARMCPRegInfo sctlr = {
|
||||||
.name = "SCTLR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0,
|
.name = "SCTLR", .state = ARM_CP_STATE_BOTH,
|
||||||
|
.opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||||
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_sys),
|
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_sys),
|
||||||
.writefn = sctlr_write, .resetvalue = cpu->reset_sctlr,
|
.writefn = sctlr_write, .resetvalue = cpu->reset_sctlr,
|
||||||
.raw_writefn = raw_write,
|
.raw_writefn = raw_write,
|
||||||
|
@ -1962,6 +2266,10 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
|
||||||
if (opaque) {
|
if (opaque) {
|
||||||
r2->opaque = opaque;
|
r2->opaque = opaque;
|
||||||
}
|
}
|
||||||
|
/* reginfo passed to helpers is correct for the actual access,
|
||||||
|
* and is never ARM_CP_STATE_BOTH:
|
||||||
|
*/
|
||||||
|
r2->state = state;
|
||||||
/* Make sure reginfo passed to helpers for wildcarded regs
|
/* Make sure reginfo passed to helpers for wildcarded regs
|
||||||
* has the correct crm/opc1/opc2 for this reg, not CP_ANY:
|
* has the correct crm/opc1/opc2 for this reg, not CP_ANY:
|
||||||
*/
|
*/
|
||||||
|
@ -2170,7 +2478,7 @@ uint32_t cpsr_read(CPUARMState *env)
|
||||||
(env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
|
(env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
|
||||||
| (env->thumb << 5) | ((env->condexec_bits & 3) << 25)
|
| (env->thumb << 5) | ((env->condexec_bits & 3) << 25)
|
||||||
| ((env->condexec_bits & 0xfc) << 8)
|
| ((env->condexec_bits & 0xfc) << 8)
|
||||||
| (env->GE << 16);
|
| (env->GE << 16) | env->daif;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
||||||
|
@ -2197,6 +2505,9 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
||||||
env->GE = (val >> 16) & 0xf;
|
env->GE = (val >> 16) & 0xf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
env->daif &= ~(CPSR_AIF & mask);
|
||||||
|
env->daif |= val & CPSR_AIF & mask;
|
||||||
|
|
||||||
if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
|
if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
|
||||||
if (bad_mode_switch(env, val & CPSR_M)) {
|
if (bad_mode_switch(env, val & CPSR_M)) {
|
||||||
/* Attempt to switch to an invalid mode: this is UNPREDICTABLE.
|
/* Attempt to switch to an invalid mode: this is UNPREDICTABLE.
|
||||||
|
@ -2658,7 +2969,7 @@ void arm_cpu_do_interrupt(CPUState *cs)
|
||||||
env->condexec_bits = 0;
|
env->condexec_bits = 0;
|
||||||
/* Switch to the new mode, and to the correct instruction set. */
|
/* Switch to the new mode, and to the correct instruction set. */
|
||||||
env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
|
env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
|
||||||
env->uncached_cpsr |= mask;
|
env->daif |= mask;
|
||||||
/* this is a lie, as the was no c1_sys on V4T/V5, but who cares
|
/* this is a lie, as the was no c1_sys on V4T/V5, but who cares
|
||||||
* and we should just guard the thumb mode on V4 */
|
* and we should just guard the thumb mode on V4 */
|
||||||
if (arm_feature(env, ARM_FEATURE_V4T)) {
|
if (arm_feature(env, ARM_FEATURE_V4T)) {
|
||||||
|
@ -2730,9 +3041,9 @@ static uint32_t get_level1_table_address(CPUARMState *env, uint32_t address)
|
||||||
uint32_t table;
|
uint32_t table;
|
||||||
|
|
||||||
if (address & env->cp15.c2_mask)
|
if (address & env->cp15.c2_mask)
|
||||||
table = env->cp15.c2_base1 & 0xffffc000;
|
table = env->cp15.ttbr1_el1 & 0xffffc000;
|
||||||
else
|
else
|
||||||
table = env->cp15.c2_base0 & env->cp15.c2_base_mask;
|
table = env->cp15.ttbr0_el1 & env->cp15.c2_base_mask;
|
||||||
|
|
||||||
table |= (address >> 18) & 0x3ffc;
|
table |= (address >> 18) & 0x3ffc;
|
||||||
return table;
|
return table;
|
||||||
|
@ -2798,7 +3109,7 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
|
||||||
break;
|
break;
|
||||||
case 2: /* 4k page. */
|
case 2: /* 4k page. */
|
||||||
phys_addr = (desc & 0xfffff000) | (address & 0xfff);
|
phys_addr = (desc & 0xfffff000) | (address & 0xfff);
|
||||||
ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
|
ap = (desc >> (4 + ((address >> 9) & 6))) & 3;
|
||||||
*page_size = 0x1000;
|
*page_size = 0x1000;
|
||||||
break;
|
break;
|
||||||
case 3: /* 1k page. */
|
case 3: /* 1k page. */
|
||||||
|
@ -3008,11 +3319,11 @@ static int get_phys_addr_lpae(CPUARMState *env, uint32_t address,
|
||||||
* we will always flush the TLB any time the ASID is changed).
|
* we will always flush the TLB any time the ASID is changed).
|
||||||
*/
|
*/
|
||||||
if (ttbr_select == 0) {
|
if (ttbr_select == 0) {
|
||||||
ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
|
ttbr = env->cp15.ttbr0_el1;
|
||||||
epd = extract32(env->cp15.c2_control, 7, 1);
|
epd = extract32(env->cp15.c2_control, 7, 1);
|
||||||
tsz = t0sz;
|
tsz = t0sz;
|
||||||
} else {
|
} else {
|
||||||
ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
|
ttbr = env->cp15.ttbr1_el1;
|
||||||
epd = extract32(env->cp15.c2_control, 23, 1);
|
epd = extract32(env->cp15.c2_control, 23, 1);
|
||||||
tsz = t1sz;
|
tsz = t1sz;
|
||||||
}
|
}
|
||||||
|
@ -3331,12 +3642,12 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
|
||||||
case 9: /* PSP */
|
case 9: /* PSP */
|
||||||
return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp;
|
return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp;
|
||||||
case 16: /* PRIMASK */
|
case 16: /* PRIMASK */
|
||||||
return (env->uncached_cpsr & CPSR_I) != 0;
|
return (env->daif & PSTATE_I) != 0;
|
||||||
case 17: /* BASEPRI */
|
case 17: /* BASEPRI */
|
||||||
case 18: /* BASEPRI_MAX */
|
case 18: /* BASEPRI_MAX */
|
||||||
return env->v7m.basepri;
|
return env->v7m.basepri;
|
||||||
case 19: /* FAULTMASK */
|
case 19: /* FAULTMASK */
|
||||||
return (env->uncached_cpsr & CPSR_F) != 0;
|
return (env->daif & PSTATE_F) != 0;
|
||||||
case 20: /* CONTROL */
|
case 20: /* CONTROL */
|
||||||
return env->v7m.control;
|
return env->v7m.control;
|
||||||
default:
|
default:
|
||||||
|
@ -3383,10 +3694,11 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val)
|
||||||
env->v7m.other_sp = val;
|
env->v7m.other_sp = val;
|
||||||
break;
|
break;
|
||||||
case 16: /* PRIMASK */
|
case 16: /* PRIMASK */
|
||||||
if (val & 1)
|
if (val & 1) {
|
||||||
env->uncached_cpsr |= CPSR_I;
|
env->daif |= PSTATE_I;
|
||||||
else
|
} else {
|
||||||
env->uncached_cpsr &= ~CPSR_I;
|
env->daif &= ~PSTATE_I;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 17: /* BASEPRI */
|
case 17: /* BASEPRI */
|
||||||
env->v7m.basepri = val & 0xff;
|
env->v7m.basepri = val & 0xff;
|
||||||
|
@ -3397,10 +3709,11 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val)
|
||||||
env->v7m.basepri = val;
|
env->v7m.basepri = val;
|
||||||
break;
|
break;
|
||||||
case 19: /* FAULTMASK */
|
case 19: /* FAULTMASK */
|
||||||
if (val & 1)
|
if (val & 1) {
|
||||||
env->uncached_cpsr |= CPSR_F;
|
env->daif |= PSTATE_F;
|
||||||
else
|
} else {
|
||||||
env->uncached_cpsr &= ~CPSR_F;
|
env->daif &= ~PSTATE_F;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 20: /* CONTROL */
|
case 20: /* CONTROL */
|
||||||
env->v7m.control = val & 3;
|
env->v7m.control = val & 3;
|
||||||
|
@ -4392,3 +4705,40 @@ int arm_rmode_to_sf(int rmode)
|
||||||
}
|
}
|
||||||
return rmode;
|
return rmode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void crc_init_buffer(uint8_t *buf, uint32_t val, uint32_t bytes)
|
||||||
|
{
|
||||||
|
memset(buf, 0, 4);
|
||||||
|
|
||||||
|
if (bytes == 1) {
|
||||||
|
buf[0] = val & 0xff;
|
||||||
|
} else if (bytes == 2) {
|
||||||
|
buf[0] = val & 0xff;
|
||||||
|
buf[1] = (val >> 8) & 0xff;
|
||||||
|
} else {
|
||||||
|
buf[0] = val & 0xff;
|
||||||
|
buf[1] = (val >> 8) & 0xff;
|
||||||
|
buf[2] = (val >> 16) & 0xff;
|
||||||
|
buf[3] = (val >> 24) & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t HELPER(crc32)(uint32_t acc, uint32_t val, uint32_t bytes)
|
||||||
|
{
|
||||||
|
uint8_t buf[4];
|
||||||
|
|
||||||
|
crc_init_buffer(buf, val, bytes);
|
||||||
|
|
||||||
|
/* zlib crc32 converts the accumulator and output to one's complement. */
|
||||||
|
return crc32(acc ^ 0xffffffff, buf, bytes) ^ 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes)
|
||||||
|
{
|
||||||
|
uint8_t buf[4];
|
||||||
|
|
||||||
|
crc_init_buffer(buf, val, bytes);
|
||||||
|
|
||||||
|
/* Linux crc32c converts the output to one's complement. */
|
||||||
|
return crc32c(acc, buf, bytes) ^ 0xffffffff;
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ DEF_HELPER_2(get_cp_reg, i32, env, ptr)
|
||||||
DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
|
DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
|
||||||
DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
|
DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
|
||||||
|
|
||||||
|
DEF_HELPER_3(msr_i_pstate, void, env, i32, i32)
|
||||||
|
|
||||||
DEF_HELPER_2(get_r13_banked, i32, env, i32)
|
DEF_HELPER_2(get_r13_banked, i32, env, i32)
|
||||||
DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
|
DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
|
||||||
|
|
||||||
|
@ -497,6 +499,9 @@ DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
|
||||||
DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32)
|
DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32)
|
||||||
DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32)
|
DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32)
|
||||||
|
|
||||||
|
DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32)
|
||||||
|
DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32)
|
||||||
|
|
||||||
#ifdef TARGET_AARCH64
|
#ifdef TARGET_AARCH64
|
||||||
#include "helper-a64.h"
|
#include "helper-a64.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -165,8 +165,10 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
|
||||||
*/
|
*/
|
||||||
typedef struct KVMDevice {
|
typedef struct KVMDevice {
|
||||||
struct kvm_arm_device_addr kda;
|
struct kvm_arm_device_addr kda;
|
||||||
|
struct kvm_device_attr kdattr;
|
||||||
MemoryRegion *mr;
|
MemoryRegion *mr;
|
||||||
QSLIST_ENTRY(KVMDevice) entries;
|
QSLIST_ENTRY(KVMDevice) entries;
|
||||||
|
int dev_fd;
|
||||||
} KVMDevice;
|
} KVMDevice;
|
||||||
|
|
||||||
static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head;
|
static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head;
|
||||||
|
@ -200,6 +202,29 @@ static MemoryListener devlistener = {
|
||||||
.region_del = kvm_arm_devlistener_del,
|
.region_del = kvm_arm_devlistener_del,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void kvm_arm_set_device_addr(KVMDevice *kd)
|
||||||
|
{
|
||||||
|
struct kvm_device_attr *attr = &kd->kdattr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* If the device control API is available and we have a device fd on the
|
||||||
|
* KVMDevice struct, let's use the newer API
|
||||||
|
*/
|
||||||
|
if (kd->dev_fd >= 0) {
|
||||||
|
uint64_t addr = kd->kda.addr;
|
||||||
|
attr->addr = (uintptr_t)&addr;
|
||||||
|
ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr);
|
||||||
|
} else {
|
||||||
|
ret = kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, &kd->kda);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Failed to set device address: %s\n",
|
||||||
|
strerror(-ret));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void kvm_arm_machine_init_done(Notifier *notifier, void *data)
|
static void kvm_arm_machine_init_done(Notifier *notifier, void *data)
|
||||||
{
|
{
|
||||||
KVMDevice *kd, *tkd;
|
KVMDevice *kd, *tkd;
|
||||||
|
@ -207,12 +232,7 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data)
|
||||||
memory_listener_unregister(&devlistener);
|
memory_listener_unregister(&devlistener);
|
||||||
QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) {
|
QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) {
|
||||||
if (kd->kda.addr != -1) {
|
if (kd->kda.addr != -1) {
|
||||||
if (kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR,
|
kvm_arm_set_device_addr(kd);
|
||||||
&kd->kda) < 0) {
|
|
||||||
fprintf(stderr, "KVM_ARM_SET_DEVICE_ADDRESS failed: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
memory_region_unref(kd->mr);
|
memory_region_unref(kd->mr);
|
||||||
g_free(kd);
|
g_free(kd);
|
||||||
|
@ -223,7 +243,8 @@ static Notifier notify = {
|
||||||
.notify = kvm_arm_machine_init_done,
|
.notify = kvm_arm_machine_init_done,
|
||||||
};
|
};
|
||||||
|
|
||||||
void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid)
|
void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
|
||||||
|
uint64_t attr, int dev_fd)
|
||||||
{
|
{
|
||||||
KVMDevice *kd;
|
KVMDevice *kd;
|
||||||
|
|
||||||
|
@ -239,6 +260,10 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid)
|
||||||
kd->mr = mr;
|
kd->mr = mr;
|
||||||
kd->kda.id = devid;
|
kd->kda.id = devid;
|
||||||
kd->kda.addr = -1;
|
kd->kda.addr = -1;
|
||||||
|
kd->kdattr.flags = 0;
|
||||||
|
kd->kdattr.group = group;
|
||||||
|
kd->kdattr.attr = attr;
|
||||||
|
kd->dev_fd = dev_fd;
|
||||||
QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries);
|
QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries);
|
||||||
memory_region_ref(kd->mr);
|
memory_region_ref(kd->mr);
|
||||||
}
|
}
|
||||||
|
@ -389,3 +414,19 @@ void kvm_arch_remove_all_hw_breakpoints(void)
|
||||||
void kvm_arch_init_irq_routing(KVMState *s)
|
void kvm_arch_init_irq_routing(KVMState *s)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_arch_irqchip_create(KVMState *s)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* If we can create the VGIC using the newer device control API, we
|
||||||
|
* let the device do this when it initializes itself, otherwise we
|
||||||
|
* fall back to the old API */
|
||||||
|
|
||||||
|
ret = kvm_create_device(s, KVM_DEV_TYPE_ARM_VGIC_V2, true);
|
||||||
|
if (ret == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -18,16 +18,21 @@
|
||||||
* kvm_arm_register_device:
|
* kvm_arm_register_device:
|
||||||
* @mr: memory region for this device
|
* @mr: memory region for this device
|
||||||
* @devid: the KVM device ID
|
* @devid: the KVM device ID
|
||||||
|
* @group: device control API group for setting addresses
|
||||||
|
* @attr: device control API address type
|
||||||
|
* @dev_fd: device control device file descriptor (or -1 if not supported)
|
||||||
*
|
*
|
||||||
* Remember the memory region @mr, and when it is mapped by the
|
* Remember the memory region @mr, and when it is mapped by the
|
||||||
* machine model, tell the kernel that base address using the
|
* machine model, tell the kernel that base address using the
|
||||||
* KVM_SET_DEVICE_ADDRESS ioctl. @devid should be the ID of
|
* KVM_ARM_SET_DEVICE_ADDRESS ioctl or the newer device control API. @devid
|
||||||
* the device as defined by KVM_SET_DEVICE_ADDRESS.
|
* should be the ID of the device as defined by KVM_ARM_SET_DEVICE_ADDRESS or
|
||||||
* The machine model may map and unmap the device multiple times;
|
* the arm-vgic device in the device control API.
|
||||||
* the kernel will only be told the final address at the point
|
* The machine model may map
|
||||||
* where machine init is complete.
|
* and unmap the device multiple times; the kernel will only be told the final
|
||||||
|
* address at the point where machine init is complete.
|
||||||
*/
|
*/
|
||||||
void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid);
|
void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
|
||||||
|
uint64_t attr, int dev_fd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* write_list_to_kvmstate:
|
* write_list_to_kvmstate:
|
||||||
|
|
|
@ -319,6 +319,31 @@ uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
|
||||||
return ri->readfn(env, ri);
|
return ri->readfn(env, ri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
|
||||||
|
{
|
||||||
|
/* MSR_i to update PSTATE. This is OK from EL0 only if UMA is set.
|
||||||
|
* Note that SPSel is never OK from EL0; we rely on handle_msr_i()
|
||||||
|
* to catch that case at translate time.
|
||||||
|
*/
|
||||||
|
if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UMA)) {
|
||||||
|
raise_exception(env, EXCP_UDEF);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case 0x05: /* SPSel */
|
||||||
|
env->pstate = deposit32(env->pstate, 0, 1, imm);
|
||||||
|
break;
|
||||||
|
case 0x1e: /* DAIFSet */
|
||||||
|
env->daif |= (imm << 6) & PSTATE_DAIF;
|
||||||
|
break;
|
||||||
|
case 0x1f: /* DAIFClear */
|
||||||
|
env->daif &= ~((imm << 6) & PSTATE_DAIF);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ??? Flag setting arithmetic is awkward because we need to do comparisons.
|
/* ??? Flag setting arithmetic is awkward because we need to do comparisons.
|
||||||
The only way to do that in TCG is a conditional branch, which clobbers
|
The only way to do that in TCG is a conditional branch, which clobbers
|
||||||
all our temporaries. For now implement these as helper functions. */
|
all our temporaries. For now implement these as helper functions. */
|
||||||
|
|
|
@ -1080,9 +1080,11 @@ static void handle_hint(DisasContext *s, uint32_t insn,
|
||||||
switch (selector) {
|
switch (selector) {
|
||||||
case 0: /* NOP */
|
case 0: /* NOP */
|
||||||
return;
|
return;
|
||||||
|
case 3: /* WFI */
|
||||||
|
s->is_jmp = DISAS_WFI;
|
||||||
|
return;
|
||||||
case 1: /* YIELD */
|
case 1: /* YIELD */
|
||||||
case 2: /* WFE */
|
case 2: /* WFE */
|
||||||
case 3: /* WFI */
|
|
||||||
case 4: /* SEV */
|
case 4: /* SEV */
|
||||||
case 5: /* SEVL */
|
case 5: /* SEVL */
|
||||||
/* we treat all as NOP at least for now */
|
/* we treat all as NOP at least for now */
|
||||||
|
@ -1126,7 +1128,30 @@ static void handle_sync(DisasContext *s, uint32_t insn,
|
||||||
static void handle_msr_i(DisasContext *s, uint32_t insn,
|
static void handle_msr_i(DisasContext *s, uint32_t insn,
|
||||||
unsigned int op1, unsigned int op2, unsigned int crm)
|
unsigned int op1, unsigned int op2, unsigned int crm)
|
||||||
{
|
{
|
||||||
unsupported_encoding(s, insn);
|
int op = op1 << 3 | op2;
|
||||||
|
switch (op) {
|
||||||
|
case 0x05: /* SPSel */
|
||||||
|
if (s->current_pl == 0) {
|
||||||
|
unallocated_encoding(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
case 0x1e: /* DAIFSet */
|
||||||
|
case 0x1f: /* DAIFClear */
|
||||||
|
{
|
||||||
|
TCGv_i32 tcg_imm = tcg_const_i32(crm);
|
||||||
|
TCGv_i32 tcg_op = tcg_const_i32(op);
|
||||||
|
gen_a64_set_pc_im(s->pc - 4);
|
||||||
|
gen_helper_msr_i_pstate(cpu_env, tcg_op, tcg_imm);
|
||||||
|
tcg_temp_free_i32(tcg_imm);
|
||||||
|
tcg_temp_free_i32(tcg_op);
|
||||||
|
s->is_jmp = DISAS_UPDATE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
unallocated_encoding(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_get_nzcv(TCGv_i64 tcg_rt)
|
static void gen_get_nzcv(TCGv_i64 tcg_rt)
|
||||||
|
@ -1231,6 +1256,13 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
||||||
gen_set_nzcv(tcg_rt);
|
gen_set_nzcv(tcg_rt);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
case ARM_CP_CURRENTEL:
|
||||||
|
/* Reads as current EL value from pstate, which is
|
||||||
|
* guaranteed to be constant by the tb flags.
|
||||||
|
*/
|
||||||
|
tcg_rt = cpu_reg(s, rt);
|
||||||
|
tcg_gen_movi_i64(tcg_rt, s->current_pl << 2);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9006,7 +9038,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
|
||||||
dc->condexec_mask = 0;
|
dc->condexec_mask = 0;
|
||||||
dc->condexec_cond = 0;
|
dc->condexec_cond = 0;
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
dc->user = 0;
|
dc->user = (ARM_TBFLAG_AA64_EL(tb->flags) == 0);
|
||||||
#endif
|
#endif
|
||||||
dc->vfp_enabled = 0;
|
dc->vfp_enabled = 0;
|
||||||
dc->vec_len = 0;
|
dc->vec_len = 0;
|
||||||
|
@ -9117,6 +9149,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
|
||||||
/* This is a special case because we don't want to just halt the CPU
|
/* This is a special case because we don't want to just halt the CPU
|
||||||
* if trying to debug across a WFI.
|
* if trying to debug across a WFI.
|
||||||
*/
|
*/
|
||||||
|
gen_a64_set_pc_im(dc->pc);
|
||||||
gen_helper_wfi(cpu_env);
|
gen_helper_wfi(cpu_env);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7561,6 +7561,36 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s)
|
||||||
store_reg(s, 14, tmp2);
|
store_reg(s, 14, tmp2);
|
||||||
gen_bx(s, tmp);
|
gen_bx(s, tmp);
|
||||||
break;
|
break;
|
||||||
|
case 0x4:
|
||||||
|
{
|
||||||
|
/* crc32/crc32c */
|
||||||
|
uint32_t c = extract32(insn, 8, 4);
|
||||||
|
|
||||||
|
/* Check this CPU supports ARMv8 CRC instructions.
|
||||||
|
* op1 == 3 is UNPREDICTABLE but handle as UNDEFINED.
|
||||||
|
* Bits 8, 10 and 11 should be zero.
|
||||||
|
*/
|
||||||
|
if (!arm_feature(env, ARM_FEATURE_CRC) || op1 == 0x3 ||
|
||||||
|
(c & 0xd) != 0) {
|
||||||
|
goto illegal_op;
|
||||||
|
}
|
||||||
|
|
||||||
|
rn = extract32(insn, 16, 4);
|
||||||
|
rd = extract32(insn, 12, 4);
|
||||||
|
|
||||||
|
tmp = load_reg(s, rn);
|
||||||
|
tmp2 = load_reg(s, rm);
|
||||||
|
tmp3 = tcg_const_i32(1 << op1);
|
||||||
|
if (c & 0x2) {
|
||||||
|
gen_helper_crc32c(tmp, tmp, tmp2, tmp3);
|
||||||
|
} else {
|
||||||
|
gen_helper_crc32(tmp, tmp, tmp2, tmp3);
|
||||||
|
}
|
||||||
|
tcg_temp_free_i32(tmp2);
|
||||||
|
tcg_temp_free_i32(tmp3);
|
||||||
|
store_reg(s, rd, tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0x5: /* saturating add/subtract */
|
case 0x5: /* saturating add/subtract */
|
||||||
ARCH(5TE);
|
ARCH(5TE);
|
||||||
rd = (insn >> 12) & 0xf;
|
rd = (insn >> 12) & 0xf;
|
||||||
|
@ -9145,6 +9175,32 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
|
||||||
case 0x18: /* clz */
|
case 0x18: /* clz */
|
||||||
gen_helper_clz(tmp, tmp);
|
gen_helper_clz(tmp, tmp);
|
||||||
break;
|
break;
|
||||||
|
case 0x20:
|
||||||
|
case 0x21:
|
||||||
|
case 0x22:
|
||||||
|
case 0x28:
|
||||||
|
case 0x29:
|
||||||
|
case 0x2a:
|
||||||
|
{
|
||||||
|
/* crc32/crc32c */
|
||||||
|
uint32_t sz = op & 0x3;
|
||||||
|
uint32_t c = op & 0x8;
|
||||||
|
|
||||||
|
if (!arm_feature(env, ARM_FEATURE_CRC)) {
|
||||||
|
goto illegal_op;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp2 = load_reg(s, rm);
|
||||||
|
tmp3 = tcg_const_i32(1 << sz);
|
||||||
|
if (c) {
|
||||||
|
gen_helper_crc32c(tmp, tmp, tmp2, tmp3);
|
||||||
|
} else {
|
||||||
|
gen_helper_crc32(tmp, tmp, tmp2, tmp3);
|
||||||
|
}
|
||||||
|
tcg_temp_free_i32(tmp2);
|
||||||
|
tcg_temp_free_i32(tmp3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
goto illegal_op;
|
goto illegal_op;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1170,6 +1170,7 @@ kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||||
kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||||
kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p"
|
kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p"
|
||||||
kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
|
kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
|
||||||
|
kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
|
||||||
|
|
||||||
# memory.c
|
# memory.c
|
||||||
memory_region_ops_read(void *mr, uint64_t addr, uint64_t value, unsigned size) "mr %p addr %#"PRIx64" value %#"PRIx64" size %u"
|
memory_region_ops_read(void *mr, uint64_t addr, uint64_t value, unsigned size) "mr %p addr %#"PRIx64" value %#"PRIx64" size %u"
|
||||||
|
|
Loading…
Reference in a new issue