From e67b3ca53a891413a33c45495ff20c2728d69722 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 10 Dec 2012 00:00:24 +0100 Subject: [PATCH 01/12] lm32_sys: increase test case name length limit The new MMU tests use longer names. Signed-off-by: Michael Walle --- hw/misc/lm32_sys.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/misc/lm32_sys.c b/hw/misc/lm32_sys.c index 9bdb78162f..16dc254f75 100644 --- a/hw/misc/lm32_sys.c +++ b/hw/misc/lm32_sys.c @@ -42,7 +42,7 @@ enum { R_MAX }; -#define MAX_TESTNAME_LEN 16 +#define MAX_TESTNAME_LEN 32 #define TYPE_LM32_SYS "lm32-sys" #define LM32_SYS(obj) OBJECT_CHECK(LM32SysState, (obj), TYPE_LM32_SYS) @@ -80,7 +80,8 @@ static void sys_write(void *opaque, hwaddr addr, case R_PASSFAIL: s->regs[addr] = value; testname = (char *)s->testname; - qemu_log("TC %-16s %s\n", testname, (value) ? "FAILED" : "OK"); + qemu_log("TC %-*s %s\n", MAX_TESTNAME_LEN, + testname, (value) ? "FAILED" : "OK"); break; case R_TESTNAME: s->regs[addr] = value; From f40c49ac9f89ee52b6015904c9005b734a73487a Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 10 Dec 2012 00:03:09 +0100 Subject: [PATCH 02/12] tests: lm32: new rule for single test cases Introduce new target "check_%" to run individual test caes, eg. make check_mmu Signed-off-by: Michael Walle Reviewed-by: Peter Maydell --- tests/tcg/lm32/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/tcg/lm32/Makefile b/tests/tcg/lm32/Makefile index 9a00ef7ea9..8e5d405459 100644 --- a/tests/tcg/lm32/Makefile +++ b/tests/tcg/lm32/Makefile @@ -96,10 +96,10 @@ all: build build: $(CRT) $(TESTCASES) -check: $(CRT) $(SYS) $(TESTCASES) - @for case in $(TESTCASES); do \ - $(SIM) $(SIMFLAGS) ./$$case; \ - done +check: $(TESTCASES:test_%.tst=check_%) + +check_%: test_%.tst $(CRT) $(SYS) + $(SIM) $(SIMFLAGS) $< clean: $(RM) -fr $(TESTCASES) $(CRT) From b2c623a3d9c88fb1d3bbbbe97364dc65bc6ab363 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Sat, 31 Aug 2013 21:22:39 +0400 Subject: [PATCH 03/12] milkymist-uart: use qemu_chr_fe_write_all() instead of qemu_chr_fe_write() qemu_chr_fe_write() is capable of returning 0 to indicate EAGAIN (and friends) and we don't handle this. Just change it to qemu_chr_fe_write_all() to fix. Reported-by: Peter Crosthwaite Acked-by: Peter Crosthwaite Signed-off-by: Antony Pavlov Signed-off-by: Michael Walle Reviewed-by: Peter Maydell --- hw/char/milkymist-uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index 2c52a0fa8e..da51f82eac 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -124,7 +124,7 @@ static void uart_write(void *opaque, hwaddr addr, uint64_t value, switch (addr) { case R_RXTX: if (s->chr) { - qemu_chr_fe_write(s->chr, &ch, 1); + qemu_chr_fe_write_all(s->chr, &ch, 1); } s->regs[R_STAT] |= STAT_TX_EVT; break; From 02d3bf7fe755e490b3641148eaef02475c58bdee Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 16 Sep 2013 18:29:32 +0200 Subject: [PATCH 04/12] lm32_uart/lm32_juart: use qemu_chr_fe_write_all() qemu_chr_fe_write() may return EAGAIN. Therefore, use qemu_chr_fe_write_all(). Signed-off-by: Michael Walle Reviewed-by: Peter Maydell --- hw/char/lm32_juart.c | 2 +- hw/char/lm32_uart.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c index 252fe46daf..380cb5dbea 100644 --- a/hw/char/lm32_juart.c +++ b/hw/char/lm32_juart.c @@ -75,7 +75,7 @@ void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx) s->jtx = jtx; if (s->chr) { - qemu_chr_fe_write(s->chr, &ch, 1); + qemu_chr_fe_write_all(s->chr, &ch, 1); } } diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c index 85d726508b..84c2549cb7 100644 --- a/hw/char/lm32_uart.c +++ b/hw/char/lm32_uart.c @@ -177,7 +177,7 @@ static void uart_write(void *opaque, hwaddr addr, switch (addr) { case R_RXTX: if (s->chr) { - qemu_chr_fe_write(s->chr, &ch, 1); + qemu_chr_fe_write_all(s->chr, &ch, 1); } break; case R_IER: From 2f453564274cde7efba77246726f3d0c51566ec0 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 16 Sep 2013 19:32:54 +0200 Subject: [PATCH 05/12] milkymist-vgafb: swap pixel data in source buffer In commit fc97bb5ba3e7239c0b6d24095df6784868dfebbf the lduw_raw() call was eliminated. But we are reading from the target buffer a 16-bit value, which is in big-endian format. Therefore, use lduw_be_p() to read the value. Cc: Paolo Bonzini Signed-off-by: Michael Walle --- hw/display/milkymist-vgafb_template.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/milkymist-vgafb_template.h b/hw/display/milkymist-vgafb_template.h index e0036e16cf..48837809eb 100644 --- a/hw/display/milkymist-vgafb_template.h +++ b/hw/display/milkymist-vgafb_template.h @@ -61,7 +61,7 @@ static void glue(draw_line_, BITS)(void *opaque, uint8_t *d, const uint8_t *s, uint8_t r, g, b; while (width--) { - memcpy(&rgb565, s, sizeof(rgb565)); + rgb565 = lduw_be_p(s); r = ((rgb565 >> 11) & 0x1f) << 3; g = ((rgb565 >> 5) & 0x3f) << 2; b = ((rgb565 >> 0) & 0x1f) << 3; From 3604a76fea6ff37738d4a8f596be38407be74a83 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 16 Sep 2013 20:13:05 +0200 Subject: [PATCH 06/12] target-lm32: kill cpu_abort() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of killing QEMU, translate instructions which are not available on the CPU model as a noop and issue a log message at translation time. On the real hardware CPU unknown opcodes results in undefined behaviour. These changes prepare the removal of CPULM32State from DisasContext. Signed-off-by: Michael Walle Reviewed-by: Andreas Färber --- target-lm32/translate.c | 72 +++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/target-lm32/translate.c b/target-lm32/translate.c index 6ea0ecd63b..e292e1c31b 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -80,7 +80,6 @@ typedef struct DisasContext { unsigned int tb_flags, synced_flags; /* tb dependent flags. */ int is_jmp; - int nr_nops; struct TranslationBlock *tb; int singlestep_enabled; } DisasContext; @@ -422,7 +421,8 @@ static void dec_divu(DisasContext *dc) LOG_DIS("divu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); if (!(dc->env->features & LM32_FEATURE_DIVIDE)) { - cpu_abort(dc->env, "hardware divider is not available\n"); + qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n"); + return; } l1 = gen_new_label(); @@ -500,7 +500,8 @@ static void dec_modu(DisasContext *dc) LOG_DIS("modu r%d, r%d, %d\n", dc->r2, dc->r0, dc->r1); if (!(dc->env->features & LM32_FEATURE_DIVIDE)) { - cpu_abort(dc->env, "hardware divider is not available\n"); + qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n"); + return; } l1 = gen_new_label(); @@ -521,7 +522,9 @@ static void dec_mul(DisasContext *dc) } if (!(dc->env->features & LM32_FEATURE_MULTIPLY)) { - cpu_abort(dc->env, "hardware multiplier is not available\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "hardware multiplier is not available\n"); + return; } if (dc->format == OP_FMT_RI) { @@ -590,7 +593,8 @@ static void dec_scall(DisasContext *dc) } else if (dc->imm5 == 2) { LOG_DIS("break\n"); } else { - cpu_abort(dc->env, "invalid opcode\n"); + qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode @0x%x", dc->pc); + return; } if (dc->imm5 == 7) { @@ -647,10 +651,10 @@ static void dec_rcsr(DisasContext *dc) case CSR_WP1: case CSR_WP2: case CSR_WP3: - cpu_abort(dc->env, "invalid read access csr=%x\n", dc->csr); + qemu_log_mask(LOG_GUEST_ERROR, "invalid read access csr=%x\n", dc->csr); break; default: - cpu_abort(dc->env, "read_csr: unknown csr=%x\n", dc->csr); + qemu_log_mask(LOG_GUEST_ERROR, "read_csr: unknown csr=%x\n", dc->csr); break; } } @@ -672,7 +676,9 @@ static void dec_sextb(DisasContext *dc) LOG_DIS("sextb r%d, r%d\n", dc->r2, dc->r0); if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) { - cpu_abort(dc->env, "hardware sign extender is not available\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "hardware sign extender is not available\n"); + return; } tcg_gen_ext8s_tl(cpu_R[dc->r2], cpu_R[dc->r0]); @@ -683,7 +689,9 @@ static void dec_sexth(DisasContext *dc) LOG_DIS("sexth r%d, r%d\n", dc->r2, dc->r0); if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) { - cpu_abort(dc->env, "hardware sign extender is not available\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "hardware sign extender is not available\n"); + return; } tcg_gen_ext16s_tl(cpu_R[dc->r2], cpu_R[dc->r0]); @@ -710,7 +718,8 @@ static void dec_sl(DisasContext *dc) } if (!(dc->env->features & LM32_FEATURE_SHIFT)) { - cpu_abort(dc->env, "hardware shifter is not available\n"); + qemu_log_mask(LOG_GUEST_ERROR, "hardware shifter is not available\n"); + return; } if (dc->format == OP_FMT_RI) { @@ -736,7 +745,9 @@ static void dec_sr(DisasContext *dc) /* TODO: check r1 == 1 during runtime */ } else { if (dc->imm5 != 1) { - cpu_abort(dc->env, "hardware shifter is not available\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "hardware shifter is not available\n"); + return; } } } @@ -764,7 +775,9 @@ static void dec_sru(DisasContext *dc) /* TODO: check r1 == 1 during runtime */ } else { if (dc->imm5 != 1) { - cpu_abort(dc->env, "hardware shifter is not available\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "hardware shifter is not available\n"); + return; } } } @@ -802,7 +815,7 @@ static void dec_user(DisasContext *dc) { LOG_DIS("user"); - cpu_abort(dc->env, "user insn undefined\n"); + qemu_log_mask(LOG_GUEST_ERROR, "user instruction undefined\n"); } static void dec_wcsr(DisasContext *dc) @@ -868,7 +881,9 @@ static void dec_wcsr(DisasContext *dc) case CSR_BP3: no = dc->csr - CSR_BP0; if (dc->env->num_bps <= no) { - cpu_abort(dc->env, "breakpoint #%i is not available\n", no); + qemu_log_mask(LOG_GUEST_ERROR, + "breakpoint #%i is not available\n", no); + break; } tcg_gen_mov_tl(cpu_bp[no], cpu_R[dc->r1]); break; @@ -878,16 +893,20 @@ static void dec_wcsr(DisasContext *dc) case CSR_WP3: no = dc->csr - CSR_WP0; if (dc->env->num_wps <= no) { - cpu_abort(dc->env, "watchpoint #%i is not available\n", no); + qemu_log_mask(LOG_GUEST_ERROR, + "watchpoint #%i is not available\n", no); + break; } tcg_gen_mov_tl(cpu_wp[no], cpu_R[dc->r1]); break; case CSR_CC: case CSR_CFG: - cpu_abort(dc->env, "invalid write access csr=%x\n", dc->csr); + qemu_log_mask(LOG_GUEST_ERROR, "invalid write access csr=%x\n", + dc->csr); break; default: - cpu_abort(dc->env, "write_csr unknown csr=%x\n", dc->csr); + qemu_log_mask(LOG_GUEST_ERROR, "write_csr: unknown csr=%x\n", + dc->csr); break; } } @@ -933,7 +952,7 @@ static void dec_xor(DisasContext *dc) static void dec_ill(DisasContext *dc) { - cpu_abort(dc->env, "unknown opcode 0x%02x\n", dc->opcode); + qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode 0x%02x\n", dc->opcode); } typedef void (*DecoderInfo)(DisasContext *dc); @@ -959,18 +978,6 @@ static inline void decode(DisasContext *dc, uint32_t ir) dc->ir = ir; LOG_DIS("%8.8x\t", dc->ir); - /* try guessing 'empty' instruction memory, although it may be a valid - * instruction sequence (eg. srui r0, r0, 0) */ - if (dc->ir) { - dc->nr_nops = 0; - } else { - LOG_DIS("nr_nops=%d\t", dc->nr_nops); - dc->nr_nops++; - if (dc->nr_nops > 4) { - cpu_abort(dc->env, "fetching nop sequence\n"); - } - } - dc->opcode = EXTRACT_FIELD(ir, 26, 31); dc->imm5 = EXTRACT_FIELD(ir, 0, 4); @@ -1034,10 +1041,11 @@ void gen_intermediate_code_internal(LM32CPU *cpu, dc->is_jmp = DISAS_NEXT; dc->pc = pc_start; dc->singlestep_enabled = cs->singlestep_enabled; - dc->nr_nops = 0; if (pc_start & 3) { - cpu_abort(env, "LM32: unaligned PC=%x\n", pc_start); + qemu_log_mask(LOG_GUEST_ERROR, + "unaligned PC=%x. Ignoring lowest bits.\n", pc_start); + pc_start &= ~3; } next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; From 34f4aa83f96722aa2c36fbe179108863ebe6e3e9 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 17 Sep 2013 18:33:16 +0200 Subject: [PATCH 07/12] target-lm32: move model features to LM32CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to completely remove CPULM32State from DisasContext. Instead, copy the fields we need to DisasContext. Reviewed-by: Andreas Färber Signed-off-by: Michael Walle --- target-lm32/cpu-qom.h | 6 ++ target-lm32/cpu.c | 187 +++++++++++++++++++++++++++++++++++++++- target-lm32/cpu.h | 7 +- target-lm32/helper.c | 128 +-------------------------- target-lm32/translate.c | 29 ++++--- 5 files changed, 215 insertions(+), 142 deletions(-) diff --git a/target-lm32/cpu-qom.h b/target-lm32/cpu-qom.h index 723f6049c3..9f15093879 100644 --- a/target-lm32/cpu-qom.h +++ b/target-lm32/cpu-qom.h @@ -60,6 +60,12 @@ typedef struct LM32CPU { /*< public >*/ CPULM32State env; + + uint32_t revision; + uint8_t num_interrupts; + uint8_t num_breakpoints; + uint8_t num_watchpoints; + uint32_t features; } LM32CPU; static inline LM32CPU *lm32_env_get_cpu(CPULM32State *env) diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c index 869878c04a..2b207adbd2 100644 --- a/target-lm32/cpu.c +++ b/target-lm32/cpu.c @@ -29,6 +29,87 @@ static void lm32_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } +/* Sort alphabetically by type name. */ +static gint lm32_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *class_a = (ObjectClass *)a; + ObjectClass *class_b = (ObjectClass *)b; + const char *name_a, *name_b; + + name_a = object_class_get_name(class_a); + name_b = object_class_get_name(class_b); + return strcmp(name_a, name_b); +} + +static void lm32_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUListState *s = user_data; + const char *typename = object_class_get_name(oc); + char *name; + + name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_LM32_CPU)); + (*s->cpu_fprintf)(s->file, " %s\n", name); + g_free(name); +} + + +void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + CPUListState s = { + .file = f, + .cpu_fprintf = cpu_fprintf, + }; + GSList *list; + + list = object_class_get_list(TYPE_LM32_CPU, false); + list = g_slist_sort(list, lm32_cpu_list_compare); + (*cpu_fprintf)(f, "Available CPUs:\n"); + g_slist_foreach(list, lm32_cpu_list_entry, &s); + g_slist_free(list); +} + +static void lm32_cpu_init_cfg_reg(LM32CPU *cpu) +{ + CPULM32State *env = &cpu->env; + uint32_t cfg = 0; + + if (cpu->features & LM32_FEATURE_MULTIPLY) { + cfg |= CFG_M; + } + + if (cpu->features & LM32_FEATURE_DIVIDE) { + cfg |= CFG_D; + } + + if (cpu->features & LM32_FEATURE_SHIFT) { + cfg |= CFG_S; + } + + if (cpu->features & LM32_FEATURE_SIGN_EXTEND) { + cfg |= CFG_X; + } + + if (cpu->features & LM32_FEATURE_I_CACHE) { + cfg |= CFG_IC; + } + + if (cpu->features & LM32_FEATURE_D_CACHE) { + cfg |= CFG_DC; + } + + if (cpu->features & LM32_FEATURE_CYCLE_COUNT) { + cfg |= CFG_CC; + } + + cfg |= (cpu->num_interrupts << CFG_INT_SHIFT); + cfg |= (cpu->num_breakpoints << CFG_BP_SHIFT); + cfg |= (cpu->num_watchpoints << CFG_WP_SHIFT); + cfg |= (cpu->revision << CFG_REV_SHIFT); + + env->cfg = cfg; +} + /* CPUClass::reset() */ static void lm32_cpu_reset(CPUState *s) { @@ -41,6 +122,7 @@ static void lm32_cpu_reset(CPUState *s) /* reset cpu state */ memset(env, 0, offsetof(CPULM32State, breakpoints)); + lm32_cpu_init_cfg_reg(cpu); tlb_flush(env, 1); } @@ -74,6 +156,91 @@ static void lm32_cpu_initfn(Object *obj) } } +static void lm32_basic_cpu_initfn(Object *obj) +{ + LM32CPU *cpu = LM32_CPU(obj); + + cpu->revision = 3; + cpu->num_interrupts = 32; + cpu->num_breakpoints = 4; + cpu->num_watchpoints = 4; + cpu->features = LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_CYCLE_COUNT; +} + +static void lm32_standard_cpu_initfn(Object *obj) +{ + LM32CPU *cpu = LM32_CPU(obj); + + cpu->revision = 3; + cpu->num_interrupts = 32; + cpu->num_breakpoints = 4; + cpu->num_watchpoints = 4; + cpu->features = LM32_FEATURE_MULTIPLY + | LM32_FEATURE_DIVIDE + | LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_I_CACHE + | LM32_FEATURE_CYCLE_COUNT; +} + +static void lm32_full_cpu_initfn(Object *obj) +{ + LM32CPU *cpu = LM32_CPU(obj); + + cpu->revision = 3; + cpu->num_interrupts = 32; + cpu->num_breakpoints = 4; + cpu->num_watchpoints = 4; + cpu->features = LM32_FEATURE_MULTIPLY + | LM32_FEATURE_DIVIDE + | LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_I_CACHE + | LM32_FEATURE_D_CACHE + | LM32_FEATURE_CYCLE_COUNT; +} + +typedef struct LM32CPUInfo { + const char *name; + void (*initfn)(Object *obj); +} LM32CPUInfo; + +static const LM32CPUInfo lm32_cpus[] = { + { + .name = "lm32-basic", + .initfn = lm32_basic_cpu_initfn, + }, + { + .name = "lm32-standard", + .initfn = lm32_standard_cpu_initfn, + }, + { + .name = "lm32-full", + .initfn = lm32_full_cpu_initfn, + }, +}; + +static ObjectClass *lm32_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (cpu_model == NULL) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_LM32_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_LM32_CPU) || + object_class_is_abstract(oc))) { + oc = NULL; + } + return oc; +} + static void lm32_cpu_class_init(ObjectClass *oc, void *data) { LM32CPUClass *lcc = LM32_CPU_CLASS(oc); @@ -86,6 +253,7 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data) lcc->parent_reset = cc->reset; cc->reset = lm32_cpu_reset; + cc->class_by_name = lm32_cpu_class_by_name; cc->do_interrupt = lm32_cpu_do_interrupt; cc->dump_state = lm32_cpu_dump_state; cc->set_pc = lm32_cpu_set_pc; @@ -98,19 +266,36 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 32 + 7; } +static void lm32_register_cpu_type(const LM32CPUInfo *info) +{ + TypeInfo type_info = { + .parent = TYPE_LM32_CPU, + .instance_init = info->initfn, + }; + + type_info.name = g_strdup_printf("%s-" TYPE_LM32_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); +} + static const TypeInfo lm32_cpu_type_info = { .name = TYPE_LM32_CPU, .parent = TYPE_CPU, .instance_size = sizeof(LM32CPU), .instance_init = lm32_cpu_initfn, - .abstract = false, + .abstract = true, .class_size = sizeof(LM32CPUClass), .class_init = lm32_cpu_class_init, }; static void lm32_cpu_register_types(void) { + int i; + type_register_static(&lm32_cpu_type_info); + for (i = 0; i < ARRAY_SIZE(lm32_cpus); i++) { + lm32_register_cpu_type(&lm32_cpus[i]); + } } type_init(lm32_cpu_register_types) diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h index dbfe043551..101df8045c 100644 --- a/target-lm32/cpu.h +++ b/target-lm32/cpu.h @@ -177,23 +177,20 @@ struct CPULM32State { DeviceState *juart_state; /* processor core features */ - uint32_t features; uint32_t flags; - uint8_t num_bps; - uint8_t num_wps; }; #include "cpu-qom.h" LM32CPU *cpu_lm32_init(const char *cpu_model); -void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf); int cpu_lm32_exec(CPULM32State *s); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero is returned if the signal was handled by the virtual CPU. */ int cpu_lm32_signal_handler(int host_signum, void *pinfo, void *puc); +void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf); void lm32_translate_init(void); void cpu_lm32_set_phys_msb_ignore(CPULM32State *env, int value); @@ -206,7 +203,7 @@ static inline CPULM32State *cpu_init(const char *cpu_model) return &cpu->env; } -#define cpu_list cpu_lm32_list +#define cpu_list lm32_cpu_list #define cpu_exec cpu_lm32_exec #define cpu_gen_code cpu_lm32_gen_code #define cpu_signal_handler cpu_lm32_signal_handler diff --git a/target-lm32/helper.c b/target-lm32/helper.c index 15bc61554d..f85ff2e8e3 100644 --- a/target-lm32/helper.c +++ b/target-lm32/helper.c @@ -90,136 +90,16 @@ void lm32_cpu_do_interrupt(CPUState *cs) } } -typedef struct { - const char *name; - uint32_t revision; - uint8_t num_interrupts; - uint8_t num_breakpoints; - uint8_t num_watchpoints; - uint32_t features; -} LM32Def; - -static const LM32Def lm32_defs[] = { - { - .name = "lm32-basic", - .revision = 3, - .num_interrupts = 32, - .num_breakpoints = 4, - .num_watchpoints = 4, - .features = (LM32_FEATURE_SHIFT - | LM32_FEATURE_SIGN_EXTEND - | LM32_FEATURE_CYCLE_COUNT), - }, - { - .name = "lm32-standard", - .revision = 3, - .num_interrupts = 32, - .num_breakpoints = 4, - .num_watchpoints = 4, - .features = (LM32_FEATURE_MULTIPLY - | LM32_FEATURE_DIVIDE - | LM32_FEATURE_SHIFT - | LM32_FEATURE_SIGN_EXTEND - | LM32_FEATURE_I_CACHE - | LM32_FEATURE_CYCLE_COUNT), - }, - { - .name = "lm32-full", - .revision = 3, - .num_interrupts = 32, - .num_breakpoints = 4, - .num_watchpoints = 4, - .features = (LM32_FEATURE_MULTIPLY - | LM32_FEATURE_DIVIDE - | LM32_FEATURE_SHIFT - | LM32_FEATURE_SIGN_EXTEND - | LM32_FEATURE_I_CACHE - | LM32_FEATURE_D_CACHE - | LM32_FEATURE_CYCLE_COUNT), - } -}; - -void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf) -{ - int i; - - cpu_fprintf(f, "Available CPUs:\n"); - for (i = 0; i < ARRAY_SIZE(lm32_defs); i++) { - cpu_fprintf(f, " %s\n", lm32_defs[i].name); - } -} - -static const LM32Def *cpu_lm32_find_by_name(const char *name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(lm32_defs); i++) { - if (strcasecmp(name, lm32_defs[i].name) == 0) { - return &lm32_defs[i]; - } - } - - return NULL; -} - -static uint32_t cfg_by_def(const LM32Def *def) -{ - uint32_t cfg = 0; - - if (def->features & LM32_FEATURE_MULTIPLY) { - cfg |= CFG_M; - } - - if (def->features & LM32_FEATURE_DIVIDE) { - cfg |= CFG_D; - } - - if (def->features & LM32_FEATURE_SHIFT) { - cfg |= CFG_S; - } - - if (def->features & LM32_FEATURE_SIGN_EXTEND) { - cfg |= CFG_X; - } - - if (def->features & LM32_FEATURE_I_CACHE) { - cfg |= CFG_IC; - } - - if (def->features & LM32_FEATURE_D_CACHE) { - cfg |= CFG_DC; - } - - if (def->features & LM32_FEATURE_CYCLE_COUNT) { - cfg |= CFG_CC; - } - - cfg |= (def->num_interrupts << CFG_INT_SHIFT); - cfg |= (def->num_breakpoints << CFG_BP_SHIFT); - cfg |= (def->num_watchpoints << CFG_WP_SHIFT); - cfg |= (def->revision << CFG_REV_SHIFT); - - return cfg; -} - LM32CPU *cpu_lm32_init(const char *cpu_model) { LM32CPU *cpu; - CPULM32State *env; - const LM32Def *def; + ObjectClass *oc; - def = cpu_lm32_find_by_name(cpu_model); - if (!def) { + oc = cpu_class_by_name(TYPE_LM32_CPU, cpu_model); + if (oc == NULL) { return NULL; } - - cpu = LM32_CPU(object_new(TYPE_LM32_CPU)); - env = &cpu->env; - - env->features = def->features; - env->num_bps = def->num_breakpoints; - env->num_wps = def->num_watchpoints; - env->cfg = cfg_by_def(def); + cpu = LM32_CPU(object_new(object_class_get_name(oc))); object_property_set_bool(OBJECT(cpu), true, "realized", NULL); diff --git a/target-lm32/translate.c b/target-lm32/translate.c index e292e1c31b..93075e4d7c 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -64,7 +64,6 @@ enum { /* This is the state at translation time. */ typedef struct DisasContext { - CPULM32State *env; target_ulong pc; /* Decoder. */ @@ -82,6 +81,10 @@ typedef struct DisasContext { struct TranslationBlock *tb; int singlestep_enabled; + + uint32_t features; + uint8_t num_breakpoints; + uint8_t num_watchpoints; } DisasContext; static const char *regnames[] = { @@ -420,7 +423,7 @@ static void dec_divu(DisasContext *dc) LOG_DIS("divu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); - if (!(dc->env->features & LM32_FEATURE_DIVIDE)) { + if (!(dc->features & LM32_FEATURE_DIVIDE)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n"); return; } @@ -499,7 +502,7 @@ static void dec_modu(DisasContext *dc) LOG_DIS("modu r%d, r%d, %d\n", dc->r2, dc->r0, dc->r1); - if (!(dc->env->features & LM32_FEATURE_DIVIDE)) { + if (!(dc->features & LM32_FEATURE_DIVIDE)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n"); return; } @@ -521,7 +524,7 @@ static void dec_mul(DisasContext *dc) LOG_DIS("mul r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); } - if (!(dc->env->features & LM32_FEATURE_MULTIPLY)) { + if (!(dc->features & LM32_FEATURE_MULTIPLY)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware multiplier is not available\n"); return; @@ -675,7 +678,7 @@ static void dec_sextb(DisasContext *dc) { LOG_DIS("sextb r%d, r%d\n", dc->r2, dc->r0); - if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) { + if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware sign extender is not available\n"); return; @@ -688,7 +691,7 @@ static void dec_sexth(DisasContext *dc) { LOG_DIS("sexth r%d, r%d\n", dc->r2, dc->r0); - if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) { + if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware sign extender is not available\n"); return; @@ -717,7 +720,7 @@ static void dec_sl(DisasContext *dc) LOG_DIS("sl r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); } - if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + if (!(dc->features & LM32_FEATURE_SHIFT)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware shifter is not available\n"); return; } @@ -740,7 +743,7 @@ static void dec_sr(DisasContext *dc) LOG_DIS("sr r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); } - if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + if (!(dc->features & LM32_FEATURE_SHIFT)) { if (dc->format == OP_FMT_RI) { /* TODO: check r1 == 1 during runtime */ } else { @@ -770,7 +773,7 @@ static void dec_sru(DisasContext *dc) LOG_DIS("sru r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); } - if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + if (!(dc->features & LM32_FEATURE_SHIFT)) { if (dc->format == OP_FMT_RI) { /* TODO: check r1 == 1 during runtime */ } else { @@ -880,7 +883,7 @@ static void dec_wcsr(DisasContext *dc) case CSR_BP2: case CSR_BP3: no = dc->csr - CSR_BP0; - if (dc->env->num_bps <= no) { + if (dc->num_breakpoints <= no) { qemu_log_mask(LOG_GUEST_ERROR, "breakpoint #%i is not available\n", no); break; @@ -892,7 +895,7 @@ static void dec_wcsr(DisasContext *dc) case CSR_WP2: case CSR_WP3: no = dc->csr - CSR_WP0; - if (dc->env->num_wps <= no) { + if (dc->num_watchpoints <= no) { qemu_log_mask(LOG_GUEST_ERROR, "watchpoint #%i is not available\n", no); break; @@ -1033,7 +1036,9 @@ void gen_intermediate_code_internal(LM32CPU *cpu, int max_insns; pc_start = tb->pc; - dc->env = env; + dc->features = cpu->features; + dc->num_breakpoints = cpu->num_breakpoints; + dc->num_watchpoints = cpu->num_watchpoints; dc->tb = tb; gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; From 3dd3a2b965a2d2f5b9c53ab86194b78a546a8fc5 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 18 Sep 2013 19:10:45 +0200 Subject: [PATCH 08/12] target-lm32: add breakpoint/watchpoint support This patch adds in-target breakpoint and watchpoint support. Signed-off-by: Michael Walle --- target-lm32/TODO | 2 - target-lm32/cpu.c | 1 + target-lm32/cpu.h | 27 ++++++++++++- target-lm32/helper.c | 90 +++++++++++++++++++++++++++++++++++++++++ target-lm32/helper.h | 3 ++ target-lm32/op_helper.c | 58 +++++++++++++++++++++++++- target-lm32/translate.c | 6 +-- 7 files changed, 179 insertions(+), 8 deletions(-) diff --git a/target-lm32/TODO b/target-lm32/TODO index b9ea0c8db9..e163c42ebe 100644 --- a/target-lm32/TODO +++ b/target-lm32/TODO @@ -1,3 +1 @@ -* disassembler (lm32-dis.c) * linux-user emulation -* native bp/wp emulation (?) diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c index 2b207adbd2..7e716fb336 100644 --- a/target-lm32/cpu.c +++ b/target-lm32/cpu.c @@ -153,6 +153,7 @@ static void lm32_cpu_initfn(Object *obj) if (tcg_enabled() && !tcg_initialized) { tcg_initialized = true; lm32_translate_init(); + cpu_set_debug_excp_handler(lm32_debug_excp_handler); } } diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h index 101df8045c..18cf3488f7 100644 --- a/target-lm32/cpu.h +++ b/target-lm32/cpu.h @@ -163,8 +163,11 @@ struct CPULM32State { /* debug registers */ uint32_t dc; /* debug control */ - uint32_t bp[4]; /* breakpoint addresses */ - uint32_t wp[4]; /* watchpoint addresses */ + uint32_t bp[4]; /* breakpoints */ + uint32_t wp[4]; /* watchpoints */ + + CPUBreakpoint * cpu_breakpoint[4]; + CPUWatchpoint * cpu_watchpoint[4]; CPU_COMMON @@ -181,6 +184,19 @@ struct CPULM32State { }; +typedef enum { + LM32_WP_DISABLED = 0, + LM32_WP_READ, + LM32_WP_WRITE, + LM32_WP_READ_WRITE, +} lm32_wp_t; + +static inline lm32_wp_t lm32_wp_type(uint32_t dc, int idx) +{ + assert(idx < 4); + return (dc >> (idx+1)*2) & 0x3; +} + #include "cpu-qom.h" LM32CPU *cpu_lm32_init(const char *cpu_model); @@ -193,6 +209,13 @@ int cpu_lm32_signal_handler(int host_signum, void *pinfo, void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf); void lm32_translate_init(void); void cpu_lm32_set_phys_msb_ignore(CPULM32State *env, int value); +void QEMU_NORETURN raise_exception(CPULM32State *env, int index); +void lm32_debug_excp_handler(CPULM32State *env); +void lm32_breakpoint_insert(CPULM32State *env, int index, target_ulong address); +void lm32_breakpoint_remove(CPULM32State *env, int index); +void lm32_watchpoint_insert(CPULM32State *env, int index, target_ulong address, + lm32_wp_t wp_type); +void lm32_watchpoint_remove(CPULM32State *env, int index); static inline CPULM32State *cpu_init(const char *cpu_model) { diff --git a/target-lm32/helper.c b/target-lm32/helper.c index f85ff2e8e3..eecb9f612e 100644 --- a/target-lm32/helper.c +++ b/target-lm32/helper.c @@ -49,6 +49,96 @@ hwaddr lm32_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } } +void lm32_breakpoint_insert(CPULM32State *env, int idx, target_ulong address) +{ + cpu_breakpoint_insert(env, address, BP_CPU, &env->cpu_breakpoint[idx]); +} + +void lm32_breakpoint_remove(CPULM32State *env, int idx) +{ + if (!env->cpu_breakpoint[idx]) { + return; + } + + cpu_breakpoint_remove_by_ref(env, env->cpu_breakpoint[idx]); + env->cpu_breakpoint[idx] = NULL; +} + +void lm32_watchpoint_insert(CPULM32State *env, int idx, target_ulong address, + lm32_wp_t wp_type) +{ + int flags = 0; + + switch (wp_type) { + case LM32_WP_DISABLED: + /* nothing to to */ + break; + case LM32_WP_READ: + flags = BP_CPU | BP_STOP_BEFORE_ACCESS | BP_MEM_READ; + break; + case LM32_WP_WRITE: + flags = BP_CPU | BP_STOP_BEFORE_ACCESS | BP_MEM_WRITE; + break; + case LM32_WP_READ_WRITE: + flags = BP_CPU | BP_STOP_BEFORE_ACCESS | BP_MEM_ACCESS; + break; + } + + if (flags != 0) { + cpu_watchpoint_insert(env, address, 1, flags, + &env->cpu_watchpoint[idx]); + } +} + +void lm32_watchpoint_remove(CPULM32State *env, int idx) +{ + if (!env->cpu_watchpoint[idx]) { + return; + } + + cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[idx]); + env->cpu_watchpoint[idx] = NULL; +} + +static bool check_watchpoints(CPULM32State *env) +{ + LM32CPU *cpu = lm32_env_get_cpu(env); + int i; + + for (i = 0; i < cpu->num_watchpoints; i++) { + if (env->cpu_watchpoint[i] && + env->cpu_watchpoint[i]->flags & BP_WATCHPOINT_HIT) { + return true; + } + } + return false; +} + +void lm32_debug_excp_handler(CPULM32State *env) +{ + CPUBreakpoint *bp; + + if (env->watchpoint_hit) { + if (env->watchpoint_hit->flags & BP_CPU) { + env->watchpoint_hit = NULL; + if (check_watchpoints(env)) { + raise_exception(env, EXCP_WATCHPOINT); + } else { + cpu_resume_from_signal(env, NULL); + } + } + } else { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == env->pc) { + if (bp->flags & BP_CPU) { + raise_exception(env, EXCP_BREAKPOINT); + } + break; + } + } + } +} + void lm32_cpu_do_interrupt(CPUState *cs) { LM32CPU *cpu = LM32_CPU(cs); diff --git a/target-lm32/helper.h b/target-lm32/helper.h index 3ea15a6e80..ad44fdf808 100644 --- a/target-lm32/helper.h +++ b/target-lm32/helper.h @@ -2,6 +2,9 @@ DEF_HELPER_2(raise_exception, void, env, i32) DEF_HELPER_1(hlt, void, env) +DEF_HELPER_3(wcsr_bp, void, env, i32, i32) +DEF_HELPER_3(wcsr_wp, void, env, i32, i32) +DEF_HELPER_2(wcsr_dc, void, env, i32) DEF_HELPER_2(wcsr_im, void, env, i32) DEF_HELPER_2(wcsr_ip, void, env, i32) DEF_HELPER_2(wcsr_jtx, void, env, i32) diff --git a/target-lm32/op_helper.c b/target-lm32/op_helper.c index 8f5ef554d5..71f21d1e24 100644 --- a/target-lm32/op_helper.c +++ b/target-lm32/op_helper.c @@ -19,12 +19,17 @@ #define SHIFT 3 #include "exec/softmmu_template.h" -void HELPER(raise_exception)(CPULM32State *env, uint32_t index) +void raise_exception(CPULM32State *env, int index) { env->exception_index = index; cpu_loop_exit(env); } +void HELPER(raise_exception)(CPULM32State *env, uint32_t index) +{ + raise_exception(env, index); +} + void HELPER(hlt)(CPULM32State *env) { CPUState *cs = CPU(lm32_env_get_cpu(env)); @@ -34,6 +39,57 @@ void HELPER(hlt)(CPULM32State *env) cpu_loop_exit(env); } +void HELPER(wcsr_bp)(CPULM32State *env, uint32_t bp, uint32_t idx) +{ + uint32_t addr = bp & ~1; + + assert(idx < 4); + + env->bp[idx] = bp; + lm32_breakpoint_remove(env, idx); + if (bp & 1) { + lm32_breakpoint_insert(env, idx, addr); + } +} + +void HELPER(wcsr_wp)(CPULM32State *env, uint32_t wp, uint32_t idx) +{ + lm32_wp_t wp_type; + + assert(idx < 4); + + env->wp[idx] = wp; + + wp_type = lm32_wp_type(env->dc, idx); + lm32_watchpoint_remove(env, idx); + if (wp_type != LM32_WP_DISABLED) { + lm32_watchpoint_insert(env, idx, wp, wp_type); + } +} + +void HELPER(wcsr_dc)(CPULM32State *env, uint32_t dc) +{ + uint32_t old_dc; + int i; + lm32_wp_t old_type; + lm32_wp_t new_type; + + old_dc = env->dc; + env->dc = dc; + + for (i = 0; i < 4; i++) { + old_type = lm32_wp_type(old_dc, i); + new_type = lm32_wp_type(dc, i); + + if (old_type != new_type) { + lm32_watchpoint_remove(env, i); + if (new_type != LM32_WP_DISABLED) { + lm32_watchpoint_insert(env, i, env->wp[i], new_type); + } + } + } +} + void HELPER(wcsr_im)(CPULM32State *env, uint32_t im) { lm32_pic_set_im(env->pic_state, im); diff --git a/target-lm32/translate.c b/target-lm32/translate.c index 93075e4d7c..f20460ab2c 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -876,7 +876,7 @@ static void dec_wcsr(DisasContext *dc) gen_helper_wcsr_jrx(cpu_env, cpu_R[dc->r1]); break; case CSR_DC: - tcg_gen_mov_tl(cpu_dc, cpu_R[dc->r1]); + gen_helper_wcsr_dc(cpu_env, cpu_R[dc->r1]); break; case CSR_BP0: case CSR_BP1: @@ -888,7 +888,7 @@ static void dec_wcsr(DisasContext *dc) "breakpoint #%i is not available\n", no); break; } - tcg_gen_mov_tl(cpu_bp[no], cpu_R[dc->r1]); + gen_helper_wcsr_bp(cpu_env, cpu_R[dc->r1], tcg_const_i32(no)); break; case CSR_WP0: case CSR_WP1: @@ -900,7 +900,7 @@ static void dec_wcsr(DisasContext *dc) "watchpoint #%i is not available\n", no); break; } - tcg_gen_mov_tl(cpu_wp[no], cpu_R[dc->r1]); + gen_helper_wcsr_wp(cpu_env, cpu_R[dc->r1], tcg_const_i32(no)); break; case CSR_CC: case CSR_CFG: From 9a59e6e307073f06b7a925b2b0e333301daa2f22 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 23 Sep 2013 20:49:17 +0200 Subject: [PATCH 09/12] lm32_sys: print test result on stderr Do not use qemu_log(). Signed-off-by: Michael Walle --- hw/misc/lm32_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/lm32_sys.c b/hw/misc/lm32_sys.c index 16dc254f75..b2e157c68a 100644 --- a/hw/misc/lm32_sys.c +++ b/hw/misc/lm32_sys.c @@ -80,7 +80,7 @@ static void sys_write(void *opaque, hwaddr addr, case R_PASSFAIL: s->regs[addr] = value; testname = (char *)s->testname; - qemu_log("TC %-*s %s\n", MAX_TESTNAME_LEN, + fprintf(stderr, "TC %-*s %s\n", MAX_TESTNAME_LEN, testname, (value) ? "FAILED" : "OK"); break; case R_TESTNAME: From 8c5edce5b775f98cff2e340427740137bd6847be Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 23 Sep 2013 20:53:04 +0200 Subject: [PATCH 10/12] lm32_sys: dump cpu state if test case fails This will ease debugging the test cases. Signed-off-by: Michael Walle --- hw/misc/lm32_sys.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/misc/lm32_sys.c b/hw/misc/lm32_sys.c index b2e157c68a..e394f2e63b 100644 --- a/hw/misc/lm32_sys.c +++ b/hw/misc/lm32_sys.c @@ -82,6 +82,9 @@ static void sys_write(void *opaque, hwaddr addr, testname = (char *)s->testname; fprintf(stderr, "TC %-*s %s\n", MAX_TESTNAME_LEN, testname, (value) ? "FAILED" : "OK"); + if (value) { + cpu_dump_state(qemu_get_cpu(0), stderr, fprintf, 0); + } break; case R_TESTNAME: s->regs[addr] = value; From 667ff9612b786f9bb5b70135811164b48b7d44eb Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 23 Sep 2013 20:47:33 +0200 Subject: [PATCH 11/12] target-lm32: stop VM on illegal or unknown instruction Instead of translating the instruction to a no-op, pause the VM and display a message to the user. As a side effect, this also works for instructions where the operands are only known at runtime. Signed-off-by: Michael Walle --- target-lm32/helper.h | 1 + target-lm32/op_helper.c | 17 +++++++ target-lm32/translate.c | 106 ++++++++++++++++++++++++++-------------- 3 files changed, 86 insertions(+), 38 deletions(-) diff --git a/target-lm32/helper.h b/target-lm32/helper.h index ad44fdf808..f4442e0a93 100644 --- a/target-lm32/helper.h +++ b/target-lm32/helper.h @@ -13,5 +13,6 @@ DEF_HELPER_1(rcsr_im, i32, env) DEF_HELPER_1(rcsr_ip, i32, env) DEF_HELPER_1(rcsr_jtx, i32, env) DEF_HELPER_1(rcsr_jrx, i32, env) +DEF_HELPER_1(ill, void, env) #include "exec/def-helper.h" diff --git a/target-lm32/op_helper.c b/target-lm32/op_helper.c index 71f21d1e24..7189cb5cc4 100644 --- a/target-lm32/op_helper.c +++ b/target-lm32/op_helper.c @@ -8,6 +8,10 @@ #include "exec/softmmu_exec.h" +#ifndef CONFIG_USER_ONLY +#include "sysemu/sysemu.h" +#endif + #if !defined(CONFIG_USER_ONLY) #define MMUSUFFIX _mmu #define SHIFT 0 @@ -39,6 +43,19 @@ void HELPER(hlt)(CPULM32State *env) cpu_loop_exit(env); } +void HELPER(ill)(CPULM32State *env) +{ +#ifndef CONFIG_USER_ONLY + CPUState *cs = CPU(lm32_env_get_cpu(env)); + fprintf(stderr, "VM paused due to illegal instruction. " + "Connect a debugger or switch to the monitor console " + "to find out more.\n"); + qemu_system_vmstop_request(RUN_STATE_PAUSED); + cs->halted = 1; + raise_exception(env, EXCP_HALTED); +#endif +} + void HELPER(wcsr_bp)(CPULM32State *env, uint32_t bp, uint32_t idx) { uint32_t addr = bp & ~1; diff --git a/target-lm32/translate.c b/target-lm32/translate.c index f20460ab2c..80bffc7b27 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -122,6 +122,12 @@ static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) tcg_temp_free_i32(tmp); } +static inline void t_gen_illegal_insn(DisasContext *dc) +{ + tcg_gen_movi_tl(cpu_pc, dc->pc); + gen_helper_ill(cpu_env); +} + static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { TranslationBlock *tb; @@ -425,6 +431,7 @@ static void dec_divu(DisasContext *dc) if (!(dc->features & LM32_FEATURE_DIVIDE)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n"); + t_gen_illegal_insn(dc); return; } @@ -504,6 +511,7 @@ static void dec_modu(DisasContext *dc) if (!(dc->features & LM32_FEATURE_DIVIDE)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware divider is not available\n"); + t_gen_illegal_insn(dc); return; } @@ -527,6 +535,7 @@ static void dec_mul(DisasContext *dc) if (!(dc->features & LM32_FEATURE_MULTIPLY)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware multiplier is not available\n"); + t_gen_illegal_insn(dc); return; } @@ -591,21 +600,21 @@ static void dec_orhi(DisasContext *dc) static void dec_scall(DisasContext *dc) { - if (dc->imm5 == 7) { - LOG_DIS("scall\n"); - } else if (dc->imm5 == 2) { + switch (dc->imm5) { + case 2: LOG_DIS("break\n"); - } else { - qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode @0x%x", dc->pc); - return; - } - - if (dc->imm5 == 7) { - tcg_gen_movi_tl(cpu_pc, dc->pc); - t_gen_raise_exception(dc, EXCP_SYSTEMCALL); - } else { tcg_gen_movi_tl(cpu_pc, dc->pc); t_gen_raise_exception(dc, EXCP_BREAKPOINT); + break; + case 7: + LOG_DIS("scall\n"); + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_SYSTEMCALL); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode @0x%x", dc->pc); + t_gen_illegal_insn(dc); + break; } } @@ -681,6 +690,7 @@ static void dec_sextb(DisasContext *dc) if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware sign extender is not available\n"); + t_gen_illegal_insn(dc); return; } @@ -694,6 +704,7 @@ static void dec_sexth(DisasContext *dc) if (!(dc->features & LM32_FEATURE_SIGN_EXTEND)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware sign extender is not available\n"); + t_gen_illegal_insn(dc); return; } @@ -722,6 +733,7 @@ static void dec_sl(DisasContext *dc) if (!(dc->features & LM32_FEATURE_SHIFT)) { qemu_log_mask(LOG_GUEST_ERROR, "hardware shifter is not available\n"); + t_gen_illegal_insn(dc); return; } @@ -743,24 +755,32 @@ static void dec_sr(DisasContext *dc) LOG_DIS("sr r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); } - if (!(dc->features & LM32_FEATURE_SHIFT)) { - if (dc->format == OP_FMT_RI) { - /* TODO: check r1 == 1 during runtime */ - } else { - if (dc->imm5 != 1) { - qemu_log_mask(LOG_GUEST_ERROR, - "hardware shifter is not available\n"); - return; - } - } - } - + /* The real CPU (w/o hardware shifter) only supports right shift by exactly + * one bit */ if (dc->format == OP_FMT_RI) { + if (!(dc->features & LM32_FEATURE_SHIFT) && (dc->imm5 != 1)) { + qemu_log_mask(LOG_GUEST_ERROR, + "hardware shifter is not available\n"); + t_gen_illegal_insn(dc); + return; + } tcg_gen_sari_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); } else { - TCGv t0 = tcg_temp_new(); + int l1 = gen_new_label(); + int l2 = gen_new_label(); + TCGv t0 = tcg_temp_local_new(); tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); + + if (!(dc->features & LM32_FEATURE_SHIFT)) { + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 1, l1); + t_gen_illegal_insn(dc); + tcg_gen_br(l2); + } + + gen_set_label(l1); tcg_gen_sar_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0); + gen_set_label(l2); + tcg_temp_free(t0); } } @@ -773,24 +793,30 @@ static void dec_sru(DisasContext *dc) LOG_DIS("sru r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); } - if (!(dc->features & LM32_FEATURE_SHIFT)) { - if (dc->format == OP_FMT_RI) { - /* TODO: check r1 == 1 during runtime */ - } else { - if (dc->imm5 != 1) { - qemu_log_mask(LOG_GUEST_ERROR, - "hardware shifter is not available\n"); - return; - } - } - } - if (dc->format == OP_FMT_RI) { + if (!(dc->features & LM32_FEATURE_SHIFT) && (dc->imm5 != 1)) { + qemu_log_mask(LOG_GUEST_ERROR, + "hardware shifter is not available\n"); + t_gen_illegal_insn(dc); + return; + } tcg_gen_shri_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); } else { - TCGv t0 = tcg_temp_new(); + int l1 = gen_new_label(); + int l2 = gen_new_label(); + TCGv t0 = tcg_temp_local_new(); tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); + + if (!(dc->features & LM32_FEATURE_SHIFT)) { + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 1, l1); + t_gen_illegal_insn(dc); + tcg_gen_br(l2); + } + + gen_set_label(l1); tcg_gen_shr_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0); + gen_set_label(l2); + tcg_temp_free(t0); } } @@ -819,6 +845,7 @@ static void dec_user(DisasContext *dc) LOG_DIS("user"); qemu_log_mask(LOG_GUEST_ERROR, "user instruction undefined\n"); + t_gen_illegal_insn(dc); } static void dec_wcsr(DisasContext *dc) @@ -886,6 +913,7 @@ static void dec_wcsr(DisasContext *dc) if (dc->num_breakpoints <= no) { qemu_log_mask(LOG_GUEST_ERROR, "breakpoint #%i is not available\n", no); + t_gen_illegal_insn(dc); break; } gen_helper_wcsr_bp(cpu_env, cpu_R[dc->r1], tcg_const_i32(no)); @@ -898,6 +926,7 @@ static void dec_wcsr(DisasContext *dc) if (dc->num_watchpoints <= no) { qemu_log_mask(LOG_GUEST_ERROR, "watchpoint #%i is not available\n", no); + t_gen_illegal_insn(dc); break; } gen_helper_wcsr_wp(cpu_env, cpu_R[dc->r1], tcg_const_i32(no)); @@ -956,6 +985,7 @@ static void dec_xor(DisasContext *dc) static void dec_ill(DisasContext *dc) { qemu_log_mask(LOG_GUEST_ERROR, "invalid opcode 0x%02x\n", dc->opcode); + t_gen_illegal_insn(dc); } typedef void (*DecoderInfo)(DisasContext *dc); From f41152bd9d01ab327c19a3828bb7896d67cf0752 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 28 Nov 2013 19:09:33 +0100 Subject: [PATCH 12/12] hw/lm32: print error if cpu model is not found QEMU crashed if a the given cpu_model is not found. Signed-off-by: Michael Walle Reviewed-by: Peter Maydell --- hw/lm32/lm32_boards.c | 10 ++++++++++ hw/lm32/milkymist.c | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index c032bb8b96..5e22e9b4d7 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -101,6 +101,11 @@ static void lm32_evr_init(QEMUMachineInitArgs *args) cpu_model = "lm32-full"; } cpu = cpu_lm32_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "qemu: unable to find CPU '%s'\n", cpu_model); + exit(1); + } + env = &cpu->env; reset_info->cpu = cpu; @@ -198,6 +203,11 @@ static void lm32_uclinux_init(QEMUMachineInitArgs *args) cpu_model = "lm32-full"; } cpu = cpu_lm32_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "qemu: unable to find CPU '%s'\n", cpu_model); + exit(1); + } + env = &cpu->env; reset_info->cpu = cpu; diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 15053c4c37..baf234ce04 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -108,6 +108,11 @@ milkymist_init(QEMUMachineInitArgs *args) cpu_model = "lm32-full"; } cpu = cpu_lm32_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "qemu: unable to find CPU '%s'\n", cpu_model); + exit(1); + } + env = &cpu->env; reset_info->cpu = cpu;