A misc collection of RISC-V related patches for 3.1.
-----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAluQU38ACgkQIeENKd+X cFRAXQgAlhNcwby+Jsk8sbLajMWXEtww9FIv+XESldPOJHmJyCkNDVZX8MuMM7+f 8NraD3YGDJvXP/BEcmyE5yPC6mx+OIi8ufzqP0rUML1x4+Tpxp8nZ7sBH197RtGg eImPA6oKvg4wyfNOrZ+hGa8HF/iMT03TqeKggUPf3dVAs8LV2iUwBIzrRLB4IhIN yFnhbcw8cW04tWUhYg4+viDY2k0q7fMrJZkASD/RjGMBjubJkwWvSYOdMIEWSpcG 2qLT5SohzUzHyKPONsoBKjSIP+nKgtyYR6IJh40FDd5S5RRMHe/n3q9jChIkHMma x1eSNvVd41++QlBKqDeAlA+gbdK/uw== =FJn/ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/alistair/tags/pull-riscv-pullreq-20180905' into staging A misc collection of RISC-V related patches for 3.1. # gpg: Signature made Wed 05 Sep 2018 23:06:55 BST # gpg: using RSA key 21E10D29DF977054 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: F6C4 AC46 D493 4868 D3B8 CE8F 21E1 0D29 DF97 7054 * remotes/alistair/tags/pull-riscv-pullreq-20180905: riscv: remove define cpu_init() hw/riscv/spike: Set the soc device tree node as a simple-bus hw/riscv/virtio: Set the soc device tree node as a simple-bus target/riscv: call gen_goto_tb on DISAS_TOO_MANY target/riscv: optimize indirect branches target/riscv: optimize cross-page direct jumps in softmmu RISC-V: Simplify riscv_cpu_local_irqs_pending RISC-V: Use atomic_cmpxchg to update PLIC bitmaps RISC-V: Improve page table walker spec compliance RISC-V: Update address bits to support sv39 and sv48 Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
ee4402eae1
@ -81,36 +81,32 @@ static void sifive_plic_print_state(SiFivePLICState *plic)
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending)
|
||||
static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value)
|
||||
{
|
||||
qemu_mutex_lock(&plic->lock);
|
||||
uint32_t word = irq >> 5;
|
||||
if (pending) {
|
||||
plic->pending[word] |= (1 << (irq & 31));
|
||||
} else {
|
||||
plic->pending[word] &= ~(1 << (irq & 31));
|
||||
}
|
||||
qemu_mutex_unlock(&plic->lock);
|
||||
uint32_t old, new, cmp = atomic_read(a);
|
||||
|
||||
do {
|
||||
old = cmp;
|
||||
new = (old & ~mask) | (value & mask);
|
||||
cmp = atomic_cmpxchg(a, old, new);
|
||||
} while (old != cmp);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
static
|
||||
void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed)
|
||||
static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level)
|
||||
{
|
||||
qemu_mutex_lock(&plic->lock);
|
||||
uint32_t word = irq >> 5;
|
||||
if (claimed) {
|
||||
plic->claimed[word] |= (1 << (irq & 31));
|
||||
} else {
|
||||
plic->claimed[word] &= ~(1 << (irq & 31));
|
||||
}
|
||||
qemu_mutex_unlock(&plic->lock);
|
||||
atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level);
|
||||
}
|
||||
|
||||
static
|
||||
int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
|
||||
static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level)
|
||||
{
|
||||
int i, j, count = 0;
|
||||
atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level);
|
||||
}
|
||||
|
||||
static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < plic->bitfield_words; i++) {
|
||||
uint32_t pending_enabled_not_claimed =
|
||||
(plic->pending[i] & ~plic->claimed[i]) &
|
||||
@ -123,11 +119,11 @@ int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
|
||||
uint32_t prio = plic->source_priority[irq];
|
||||
int enabled = pending_enabled_not_claimed & (1 << j);
|
||||
if (enabled && prio > plic->target_priority[addrid]) {
|
||||
count++;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sifive_plic_update(SiFivePLICState *plic)
|
||||
@ -143,7 +139,7 @@ static void sifive_plic_update(SiFivePLICState *plic)
|
||||
if (!env) {
|
||||
continue;
|
||||
}
|
||||
int level = sifive_plic_num_irqs_pending(plic, addrid) > 0;
|
||||
int level = sifive_plic_irqs_pending(plic, addrid);
|
||||
switch (mode) {
|
||||
case PLICMode_M:
|
||||
riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
|
||||
@ -439,7 +435,6 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
|
||||
TYPE_SIFIVE_PLIC, plic->aperture_size);
|
||||
parse_hart_config(plic);
|
||||
qemu_mutex_init(&plic->lock);
|
||||
plic->bitfield_words = (plic->num_sources + 31) >> 5;
|
||||
plic->source_priority = g_new0(uint32_t, plic->num_sources);
|
||||
plic->target_priority = g_new(uint32_t, plic->num_addrs);
|
||||
|
@ -90,7 +90,7 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/soc");
|
||||
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
|
||||
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc");
|
||||
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
|
||||
|
||||
|
@ -121,7 +121,7 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
|
||||
|
||||
qemu_fdt_add_subnode(fdt, "/soc");
|
||||
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
|
||||
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "riscv-virtio-soc");
|
||||
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
|
||||
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
|
||||
|
||||
|
@ -55,7 +55,6 @@ typedef struct SiFivePLICState {
|
||||
uint32_t *pending;
|
||||
uint32_t *claimed;
|
||||
uint32_t *enable;
|
||||
QemuMutex lock;
|
||||
|
||||
/* config */
|
||||
char *hart_config;
|
||||
|
@ -24,12 +24,12 @@
|
||||
#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */
|
||||
#if defined(TARGET_RISCV64)
|
||||
#define TARGET_LONG_BITS 64
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 50
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 39
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 56 /* 44-bit PPN */
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 48 /* sv48 */
|
||||
#elif defined(TARGET_RISCV32)
|
||||
#define TARGET_LONG_BITS 32
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 34
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 32
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 34 /* 22-bit PPN */
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 32 /* sv32 */
|
||||
#endif
|
||||
|
||||
#define TCG_GUEST_DEFAULT_MO 0
|
||||
@ -251,7 +251,6 @@ int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
|
||||
char *riscv_isa_string(RISCVCPU *cpu);
|
||||
void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
||||
|
||||
#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model)
|
||||
#define cpu_signal_handler cpu_riscv_signal_handler
|
||||
#define cpu_list riscv_cpu_list
|
||||
#define cpu_mmu_index riscv_cpu_mmu_index
|
||||
|
@ -407,5 +407,3 @@
|
||||
#define PTE_SOFT 0x300 /* Reserved for Software */
|
||||
|
||||
#define PTE_PPN_SHIFT 10
|
||||
|
||||
#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
|
||||
|
@ -35,28 +35,18 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/*
|
||||
* Return RISC-V IRQ number if an interrupt should be taken, else -1.
|
||||
* Used in cpu-exec.c
|
||||
*
|
||||
* Adapted from Spike's processor_t::take_interrupt()
|
||||
*/
|
||||
static int riscv_cpu_hw_interrupts_pending(CPURISCVState *env)
|
||||
static int riscv_cpu_local_irq_pending(CPURISCVState *env)
|
||||
{
|
||||
target_ulong pending_interrupts = atomic_read(&env->mip) & env->mie;
|
||||
target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
|
||||
target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE);
|
||||
target_ulong pending = atomic_read(&env->mip) & env->mie;
|
||||
target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie);
|
||||
target_ulong sie = env->priv < PRV_S || (env->priv == PRV_S && mstatus_sie);
|
||||
target_ulong irqs = (pending & ~env->mideleg & -mie) |
|
||||
(pending & env->mideleg & -sie);
|
||||
|
||||
target_ulong mie = get_field(env->mstatus, MSTATUS_MIE);
|
||||
target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie);
|
||||
target_ulong enabled_interrupts = pending_interrupts &
|
||||
~env->mideleg & -m_enabled;
|
||||
|
||||
target_ulong sie = get_field(env->mstatus, MSTATUS_SIE);
|
||||
target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie);
|
||||
enabled_interrupts |= pending_interrupts & env->mideleg &
|
||||
-s_enabled;
|
||||
|
||||
if (enabled_interrupts) {
|
||||
return ctz64(enabled_interrupts); /* since non-zero */
|
||||
if (irqs) {
|
||||
return ctz64(irqs); /* since non-zero */
|
||||
} else {
|
||||
return EXCP_NONE; /* indicates no pending interrupt */
|
||||
}
|
||||
@ -69,7 +59,7 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
int interruptno = riscv_cpu_hw_interrupts_pending(env);
|
||||
int interruptno = riscv_cpu_local_irq_pending(env);
|
||||
if (interruptno >= 0) {
|
||||
cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno;
|
||||
riscv_cpu_do_interrupt(cs);
|
||||
@ -185,16 +175,39 @@ restart:
|
||||
#endif
|
||||
target_ulong ppn = pte >> PTE_PPN_SHIFT;
|
||||
|
||||
if (PTE_TABLE(pte)) { /* next level of page table */
|
||||
if (!(pte & PTE_V)) {
|
||||
/* Invalid PTE */
|
||||
return TRANSLATE_FAIL;
|
||||
} else if (!(pte & (PTE_R | PTE_W | PTE_X))) {
|
||||
/* Inner PTE, continue walking */
|
||||
base = ppn << PGSHIFT;
|
||||
} else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) {
|
||||
break;
|
||||
} else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
|
||||
break;
|
||||
} else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) :
|
||||
access_type == MMU_DATA_LOAD ? !(pte & PTE_R) &&
|
||||
!(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) {
|
||||
break;
|
||||
} else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
|
||||
/* Reserved leaf PTE flags: PTE_W */
|
||||
return TRANSLATE_FAIL;
|
||||
} else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
|
||||
/* Reserved leaf PTE flags: PTE_W + PTE_X */
|
||||
return TRANSLATE_FAIL;
|
||||
} else if ((pte & PTE_U) && ((mode != PRV_U) &&
|
||||
(!sum || access_type == MMU_INST_FETCH))) {
|
||||
/* User PTE flags when not U mode and mstatus.SUM is not set,
|
||||
or the access type is an instruction fetch */
|
||||
return TRANSLATE_FAIL;
|
||||
} else if (!(pte & PTE_U) && (mode != PRV_S)) {
|
||||
/* Supervisor PTE flags when not S mode */
|
||||
return TRANSLATE_FAIL;
|
||||
} else if (ppn & ((1ULL << ptshift) - 1)) {
|
||||
/* Misaligned PPN */
|
||||
return TRANSLATE_FAIL;
|
||||
} else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) ||
|
||||
((pte & PTE_X) && mxr))) {
|
||||
/* Read access check failed */
|
||||
return TRANSLATE_FAIL;
|
||||
} else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) {
|
||||
/* Write access check failed */
|
||||
return TRANSLATE_FAIL;
|
||||
} else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) {
|
||||
/* Fetch access check failed */
|
||||
return TRANSLATE_FAIL;
|
||||
} else {
|
||||
/* if necessary, set accessed and dirty bits. */
|
||||
target_ulong updated_pte = pte | PTE_A |
|
||||
@ -202,16 +215,19 @@ restart:
|
||||
|
||||
/* Page table updates need to be atomic with MTTCG enabled */
|
||||
if (updated_pte != pte) {
|
||||
/* if accessed or dirty bits need updating, and the PTE is
|
||||
* in RAM, then we do so atomically with a compare and swap.
|
||||
* if the PTE is in IO space, then it can't be updated.
|
||||
* if the PTE changed, then we must re-walk the page table
|
||||
as the PTE is no longer valid */
|
||||
/*
|
||||
* - if accessed or dirty bits need updating, and the PTE is
|
||||
* in RAM, then we do so atomically with a compare and swap.
|
||||
* - if the PTE is in IO space or ROM, then it can't be updated
|
||||
* and we return TRANSLATE_FAIL.
|
||||
* - if the PTE changed by the time we went to update it, then
|
||||
* it is no longer valid and we must re-walk the page table.
|
||||
*/
|
||||
MemoryRegion *mr;
|
||||
hwaddr l = sizeof(target_ulong), addr1;
|
||||
mr = address_space_translate(cs->as, pte_addr,
|
||||
&addr1, &l, false, MEMTXATTRS_UNSPECIFIED);
|
||||
if (memory_access_is_direct(mr, true)) {
|
||||
if (memory_region_is_ram(mr)) {
|
||||
target_ulong *pte_pa =
|
||||
qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
@ -239,15 +255,15 @@ restart:
|
||||
target_ulong vpn = addr >> PGSHIFT;
|
||||
*physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT;
|
||||
|
||||
if ((pte & PTE_R)) {
|
||||
/* set permissions on the TLB entry */
|
||||
if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) {
|
||||
*prot |= PAGE_READ;
|
||||
}
|
||||
if ((pte & PTE_X)) {
|
||||
*prot |= PAGE_EXEC;
|
||||
}
|
||||
/* only add write permission on stores or if the page
|
||||
is already dirty, so that we don't miss further
|
||||
page table walks to update the dirty bit */
|
||||
/* add write permission on stores or if the page is already dirty,
|
||||
so that we TLB miss on later writes to update the dirty bit */
|
||||
if ((pte & PTE_W) &&
|
||||
(access_type == MMU_DATA_STORE || (pte & PTE_D))) {
|
||||
*prot |= PAGE_WRITE;
|
||||
|
@ -135,7 +135,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
||||
if (ctx->base.singlestep_enabled) {
|
||||
gen_exception_debug();
|
||||
} else {
|
||||
tcg_gen_exit_tb(NULL, 0);
|
||||
tcg_gen_lookup_and_goto_ptr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -548,7 +548,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
|
||||
if (rd != 0) {
|
||||
tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn);
|
||||
}
|
||||
tcg_gen_exit_tb(NULL, 0);
|
||||
tcg_gen_lookup_and_goto_ptr();
|
||||
|
||||
if (misaligned) {
|
||||
gen_set_label(misaligned);
|
||||
@ -1868,12 +1868,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
|
||||
|
||||
switch (ctx->base.is_jmp) {
|
||||
case DISAS_TOO_MANY:
|
||||
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
|
||||
if (ctx->base.singlestep_enabled) {
|
||||
gen_exception_debug();
|
||||
} else {
|
||||
tcg_gen_exit_tb(NULL, 0);
|
||||
}
|
||||
gen_goto_tb(ctx, 0, ctx->base.pc_next);
|
||||
break;
|
||||
case DISAS_NORETURN:
|
||||
break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user