提交 ee4402ea 编写于 作者: P Peter Maydell

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: NPeter Maydell <peter.maydell@linaro.org>
...@@ -81,36 +81,32 @@ static void sifive_plic_print_state(SiFivePLICState *plic) ...@@ -81,36 +81,32 @@ static void sifive_plic_print_state(SiFivePLICState *plic)
} }
} }
static static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value)
void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending)
{ {
qemu_mutex_lock(&plic->lock); uint32_t old, new, cmp = atomic_read(a);
uint32_t word = irq >> 5;
if (pending) { do {
plic->pending[word] |= (1 << (irq & 31)); old = cmp;
} else { new = (old & ~mask) | (value & mask);
plic->pending[word] &= ~(1 << (irq & 31)); cmp = atomic_cmpxchg(a, old, new);
} } while (old != cmp);
qemu_mutex_unlock(&plic->lock);
return old;
} }
static static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level)
void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed)
{ {
qemu_mutex_lock(&plic->lock); atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level);
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);
} }
static static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level)
int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
{ {
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++) { for (i = 0; i < plic->bitfield_words; i++) {
uint32_t pending_enabled_not_claimed = uint32_t pending_enabled_not_claimed =
(plic->pending[i] & ~plic->claimed[i]) & (plic->pending[i] & ~plic->claimed[i]) &
...@@ -123,11 +119,11 @@ int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid) ...@@ -123,11 +119,11 @@ int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
uint32_t prio = plic->source_priority[irq]; uint32_t prio = plic->source_priority[irq];
int enabled = pending_enabled_not_claimed & (1 << j); int enabled = pending_enabled_not_claimed & (1 << j);
if (enabled && prio > plic->target_priority[addrid]) { if (enabled && prio > plic->target_priority[addrid]) {
count++; return 1;
} }
} }
} }
return count; return 0;
} }
static void sifive_plic_update(SiFivePLICState *plic) static void sifive_plic_update(SiFivePLICState *plic)
...@@ -143,7 +139,7 @@ static void sifive_plic_update(SiFivePLICState *plic) ...@@ -143,7 +139,7 @@ static void sifive_plic_update(SiFivePLICState *plic)
if (!env) { if (!env) {
continue; continue;
} }
int level = sifive_plic_num_irqs_pending(plic, addrid) > 0; int level = sifive_plic_irqs_pending(plic, addrid);
switch (mode) { switch (mode) {
case PLICMode_M: case PLICMode_M:
riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level); riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
...@@ -439,7 +435,6 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) ...@@ -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, memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
TYPE_SIFIVE_PLIC, plic->aperture_size); TYPE_SIFIVE_PLIC, plic->aperture_size);
parse_hart_config(plic); parse_hart_config(plic);
qemu_mutex_init(&plic->lock);
plic->bitfield_words = (plic->num_sources + 31) >> 5; plic->bitfield_words = (plic->num_sources + 31) >> 5;
plic->source_priority = g_new0(uint32_t, plic->num_sources); plic->source_priority = g_new0(uint32_t, plic->num_sources);
plic->target_priority = g_new(uint32_t, plic->num_addrs); plic->target_priority = g_new(uint32_t, plic->num_addrs);
......
...@@ -90,7 +90,7 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, ...@@ -90,7 +90,7 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
qemu_fdt_add_subnode(fdt, "/soc"); qemu_fdt_add_subnode(fdt, "/soc");
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); 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", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/soc", "#address-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, ...@@ -121,7 +121,7 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
qemu_fdt_add_subnode(fdt, "/soc"); qemu_fdt_add_subnode(fdt, "/soc");
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); 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", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
......
...@@ -55,7 +55,6 @@ typedef struct SiFivePLICState { ...@@ -55,7 +55,6 @@ typedef struct SiFivePLICState {
uint32_t *pending; uint32_t *pending;
uint32_t *claimed; uint32_t *claimed;
uint32_t *enable; uint32_t *enable;
QemuMutex lock;
/* config */ /* config */
char *hart_config; char *hart_config;
......
...@@ -24,12 +24,12 @@ ...@@ -24,12 +24,12 @@
#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */ #define TARGET_PAGE_BITS 12 /* 4 KiB Pages */
#if defined(TARGET_RISCV64) #if defined(TARGET_RISCV64)
#define TARGET_LONG_BITS 64 #define TARGET_LONG_BITS 64
#define TARGET_PHYS_ADDR_SPACE_BITS 50 #define TARGET_PHYS_ADDR_SPACE_BITS 56 /* 44-bit PPN */
#define TARGET_VIRT_ADDR_SPACE_BITS 39 #define TARGET_VIRT_ADDR_SPACE_BITS 48 /* sv48 */
#elif defined(TARGET_RISCV32) #elif defined(TARGET_RISCV32)
#define TARGET_LONG_BITS 32 #define TARGET_LONG_BITS 32
#define TARGET_PHYS_ADDR_SPACE_BITS 34 #define TARGET_PHYS_ADDR_SPACE_BITS 34 /* 22-bit PPN */
#define TARGET_VIRT_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 /* sv32 */
#endif #endif
#define TCG_GUEST_DEFAULT_MO 0 #define TCG_GUEST_DEFAULT_MO 0
...@@ -251,7 +251,6 @@ int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, ...@@ -251,7 +251,6 @@ int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
char *riscv_isa_string(RISCVCPU *cpu); char *riscv_isa_string(RISCVCPU *cpu);
void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); 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_signal_handler cpu_riscv_signal_handler
#define cpu_list riscv_cpu_list #define cpu_list riscv_cpu_list
#define cpu_mmu_index riscv_cpu_mmu_index #define cpu_mmu_index riscv_cpu_mmu_index
......
...@@ -407,5 +407,3 @@ ...@@ -407,5 +407,3 @@
#define PTE_SOFT 0x300 /* Reserved for Software */ #define PTE_SOFT 0x300 /* Reserved for Software */
#define PTE_PPN_SHIFT 10 #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) ...@@ -35,28 +35,18 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
/* static int riscv_cpu_local_irq_pending(CPURISCVState *env)
* 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)
{ {
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 mie = get_field(env->mstatus, MSTATUS_MIE); target_ulong pending = atomic_read(&env->mip) & env->mie;
target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie); target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie);
target_ulong enabled_interrupts = pending_interrupts & target_ulong sie = env->priv < PRV_S || (env->priv == PRV_S && mstatus_sie);
~env->mideleg & -m_enabled; target_ulong irqs = (pending & ~env->mideleg & -mie) |
(pending & env->mideleg & -sie);
target_ulong sie = get_field(env->mstatus, MSTATUS_SIE);
target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie); if (irqs) {
enabled_interrupts |= pending_interrupts & env->mideleg & return ctz64(irqs); /* since non-zero */
-s_enabled;
if (enabled_interrupts) {
return ctz64(enabled_interrupts); /* since non-zero */
} else { } else {
return EXCP_NONE; /* indicates no pending interrupt */ return EXCP_NONE; /* indicates no pending interrupt */
} }
...@@ -69,7 +59,7 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) ...@@ -69,7 +59,7 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
if (interrupt_request & CPU_INTERRUPT_HARD) { if (interrupt_request & CPU_INTERRUPT_HARD) {
RISCVCPU *cpu = RISCV_CPU(cs); RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env; CPURISCVState *env = &cpu->env;
int interruptno = riscv_cpu_hw_interrupts_pending(env); int interruptno = riscv_cpu_local_irq_pending(env);
if (interruptno >= 0) { if (interruptno >= 0) {
cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno; cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno;
riscv_cpu_do_interrupt(cs); riscv_cpu_do_interrupt(cs);
...@@ -185,16 +175,39 @@ restart: ...@@ -185,16 +175,39 @@ restart:
#endif #endif
target_ulong ppn = pte >> PTE_PPN_SHIFT; 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; base = ppn << PGSHIFT;
} else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) { } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
break; /* Reserved leaf PTE flags: PTE_W */
} else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { return TRANSLATE_FAIL;
break; } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
} else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) : /* Reserved leaf PTE flags: PTE_W + PTE_X */
access_type == MMU_DATA_LOAD ? !(pte & PTE_R) && return TRANSLATE_FAIL;
!(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) { } else if ((pte & PTE_U) && ((mode != PRV_U) &&
break; (!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 { } else {
/* if necessary, set accessed and dirty bits. */ /* if necessary, set accessed and dirty bits. */
target_ulong updated_pte = pte | PTE_A | target_ulong updated_pte = pte | PTE_A |
...@@ -202,16 +215,19 @@ restart: ...@@ -202,16 +215,19 @@ restart:
/* Page table updates need to be atomic with MTTCG enabled */ /* Page table updates need to be atomic with MTTCG enabled */
if (updated_pte != pte) { 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 accessed or dirty bits need updating, and the PTE is
* if the PTE is in IO space, then it can't be updated. * in RAM, then we do so atomically with a compare and swap.
* if the PTE changed, then we must re-walk the page table * - if the PTE is in IO space or ROM, then it can't be updated
as the PTE is no longer valid */ * 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; MemoryRegion *mr;
hwaddr l = sizeof(target_ulong), addr1; hwaddr l = sizeof(target_ulong), addr1;
mr = address_space_translate(cs->as, pte_addr, mr = address_space_translate(cs->as, pte_addr,
&addr1, &l, false, MEMTXATTRS_UNSPECIFIED); &addr1, &l, false, MEMTXATTRS_UNSPECIFIED);
if (memory_access_is_direct(mr, true)) { if (memory_region_is_ram(mr)) {
target_ulong *pte_pa = target_ulong *pte_pa =
qemu_map_ram_ptr(mr->ram_block, addr1); qemu_map_ram_ptr(mr->ram_block, addr1);
#if TCG_OVERSIZED_GUEST #if TCG_OVERSIZED_GUEST
...@@ -239,15 +255,15 @@ restart: ...@@ -239,15 +255,15 @@ restart:
target_ulong vpn = addr >> PGSHIFT; target_ulong vpn = addr >> PGSHIFT;
*physical = (ppn | (vpn & ((1L << ptshift) - 1))) << 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; *prot |= PAGE_READ;
} }
if ((pte & PTE_X)) { if ((pte & PTE_X)) {
*prot |= PAGE_EXEC; *prot |= PAGE_EXEC;
} }
/* only add write permission on stores or if the page /* add write permission on stores or if the page is already dirty,
is already dirty, so that we don't miss further so that we TLB miss on later writes to update the dirty bit */
page table walks to update the dirty bit */
if ((pte & PTE_W) && if ((pte & PTE_W) &&
(access_type == MMU_DATA_STORE || (pte & PTE_D))) { (access_type == MMU_DATA_STORE || (pte & PTE_D))) {
*prot |= PAGE_WRITE; *prot |= PAGE_WRITE;
......
...@@ -135,7 +135,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) ...@@ -135,7 +135,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
if (ctx->base.singlestep_enabled) { if (ctx->base.singlestep_enabled) {
gen_exception_debug(); gen_exception_debug();
} else { } 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, ...@@ -548,7 +548,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
if (rd != 0) { if (rd != 0) {
tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); 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) { if (misaligned) {
gen_set_label(misaligned); gen_set_label(misaligned);
...@@ -1868,12 +1868,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) ...@@ -1868,12 +1868,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
switch (ctx->base.is_jmp) { switch (ctx->base.is_jmp) {
case DISAS_TOO_MANY: case DISAS_TOO_MANY:
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); gen_goto_tb(ctx, 0, ctx->base.pc_next);
if (ctx->base.singlestep_enabled) {
gen_exception_debug();
} else {
tcg_gen_exit_tb(NULL, 0);
}
break; break;
case DISAS_NORETURN: case DISAS_NORETURN:
break; break;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册