diff --git a/configure b/configure index b9552fd..9aeaae3 100755 --- a/configure +++ b/configure @@ -3612,7 +3612,7 @@ fi # check if memfd is supported memfd=no cat > $TMPC << EOF -#include +#include int main(void) { diff --git a/cpu-exec.c b/cpu-exec.c index c88d0ff..4395bb3 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -31,6 +31,7 @@ #include "hw/i386/apic.h" #endif #include "sysemu/replay.h" +#include "hw/boards.h" /* -icount align implementation. */ @@ -332,6 +333,16 @@ static void cpu_handle_debug_exception(CPUState *cpu) cc->debug_excp_handler(cpu); } +static void (*tb_exec_cb)(void *opaque, CPUState *cpu, TranslationBlock *tb); +static void * tb_exec_opaque; + +void cpu_set_tb_exec_cb(void (*cb)(void *opaque, CPUState *cpu, TranslationBlock *tb), + void *opaque) +{ + tb_exec_cb = cb; + tb_exec_opaque = opaque; +} + /* main execution loop */ int cpu_exec(CPUState *cpu) @@ -500,8 +511,28 @@ int cpu_exec(CPUState *cpu) tcg_ctx.tb_ctx.tb_invalidated_flag = 0; } if (qemu_loglevel_mask(CPU_LOG_EXEC)) { +#if defined(TARGET_ARM) + /* rather than logging just PC, also log the disassembled code */ + if (!singlestep) { + /* separate translation blocks by newlines */ + qemu_log("\n"); + } + /* some instructions are printed more than once, not sure why */ + static int prev_pc = -1; + if (tb->pc != prev_pc) { + prev_pc = tb->pc; + if (CPU_NEXT(first_cpu)) { + qemu_log("[CPU%d] ", current_cpu->cpu_index); + } + log_target_disas(cpu, tb->pc, tb->size, 0); + } +#else qemu_log("Trace %p [" TARGET_FMT_lx "] %s\n", tb->tc_ptr, tb->pc, lookup_symbol(tb->pc)); +#endif + } + if (tb_exec_cb) { + tb_exec_cb(tb_exec_opaque, cpu, tb); } /* see if we can patch the calling TB. When the TB spans two pages, we cannot safely do a direct diff --git a/disas/arm.c b/disas/arm.c index 7a7354b..95f82cc 100644 --- a/disas/arm.c +++ b/disas/arm.c @@ -2826,9 +2826,9 @@ print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given) if ((given & 0x02000000) != 0) { int rotate = (given & 0xf00) >> 7; - int immed = (given & 0xff); - immed = (((immed << (32 - rotate)) - | (immed >> rotate)) & 0xffffffff); + int64_t immed64 = (given & 0xff); + int immed = (((immed64 << (32 - rotate)) + | (immed64 >> rotate)) & 0xffffffff); func (stream, "#%d\t; 0x%x", immed, immed); } else @@ -3868,7 +3868,7 @@ int print_insn_arm (bfd_vma pc, struct disassemble_info *info) { unsigned char b[4]; - long given; + unsigned long given; int status; int is_thumb = false; int is_data = false; diff --git a/exec.c b/exec.c index 0bf0a6e..5eb69de 100644 --- a/exec.c +++ b/exec.c @@ -2564,7 +2564,7 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, } else { addr1 += memory_region_get_ram_addr(mr); /* RAM case */ - ptr = qemu_get_ram_ptr(addr1); + ptr = qemu_ram_ptr_length(addr1, &l); memcpy(ptr, buf, l); invalidate_and_set_dirty(mr, addr1, l); } @@ -2603,7 +2603,7 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, } } else { /* RAM case */ - ptr = qemu_get_ram_ptr(mr->ram_addr + addr1); + ptr = qemu_ram_ptr_length(mr->ram_addr + addr1, &l); memcpy(buf, ptr, l); } } diff --git a/gdbstub.c b/gdbstub.c index 9c29aa0..a95ec8d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -431,6 +431,7 @@ static inline int tohex(int v) return v - 10 + 'a'; } +/* writes 2*len+1 bytes in buf */ static void memtohex(char *buf, const uint8_t *mem, int len) { int i, c; @@ -787,8 +788,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) const char *p; uint32_t thread; int ch, reg_size, type, res; - char buf[MAX_PACKET_LENGTH]; uint8_t mem_buf[MAX_PACKET_LENGTH]; + char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; uint8_t *registers; target_ulong addr, len; diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 2195b60..e3e2c14 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -6,6 +6,7 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-$(CONFIG_ACPI) += virt-acpi-build.o obj-y += netduino2.o obj-y += sysbus-fdt.o +include ../hw/eos/Makefile.objs obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o diff --git a/hw/arm/digic.c b/hw/arm/digic.c index 90f8190..8e934be 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -36,7 +36,7 @@ static void digic_init(Object *obj) object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); for (i = 0; i < DIGIC4_NB_TIMERS; i++) { -#define DIGIC_TIMER_NAME_MLEN 11 +#define DIGIC_TIMER_NAME_MLEN 20 char name[DIGIC_TIMER_NAME_MLEN]; object_initialize(&s->timer[i], sizeof(s->timer[i]), TYPE_DIGIC_TIMER); diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 45239e8..825d6e6 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -97,7 +97,7 @@ #define FIMD_WINCON_BUFSTATUS ((1 << 21) | (1 << 31)) #define FIMD_WINCON_BUF0_STAT ((0 << 21) | (0 << 31)) #define FIMD_WINCON_BUF1_STAT ((1 << 21) | (0 << 31)) -#define FIMD_WINCON_BUF2_STAT ((0 << 21) | (1 << 31)) +#define FIMD_WINCON_BUF2_STAT ((0 << 21) | (1U << 31)) #define FIMD_WINCON_BUFSELECT ((1 << 20) | (1 << 30)) #define FIMD_WINCON_BUF0_SEL ((0 << 20) | (0 << 30)) #define FIMD_WINCON_BUF1_SEL ((1 << 20) | (0 << 30)) diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 788b361..6aaeced 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -245,7 +245,8 @@ static void ide_dev_instance_init(Object *obj) static int ide_hd_initfn(IDEDevice *dev) { - return ide_dev_initfn(dev, IDE_HD); + /* fixme: forcing IDE type to IDE_CFATA (was ID_HD) */ + return ide_dev_initfn(dev, IDE_CFATA); } static int ide_cd_initfn(IDEDevice *dev) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 1a9935c..2561560 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1479,6 +1479,9 @@ static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) { uint64_t end = addr + len; + DPRINTF("sd_blk_write: addr = 0x%08llx, len = %d\n", + (unsigned long long) addr, len); + if ((addr & 511) || len < 512) if (!sd->blk || blk_read(sd->blk, addr >> 9, sd->buf, 1) < 0) { fprintf(stderr, "sd_blk_write: read error on host side\n"); diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index d900b0d..1ad5332 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -405,4 +405,7 @@ extern int singlestep; extern CPUState *tcg_current_cpu; extern bool exit_request; +void cpu_set_tb_exec_cb(void (*cb)(void *opaque, CPUState *cpu, TranslationBlock *tb), + void *opaque); + #endif diff --git a/include/exec/memory.h b/include/exec/memory.h index 0f07159..40c3940 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1356,6 +1356,19 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, int is_write, hwaddr access_len); +/* memory_set_access_logging_cb: allow logging guest memory accesses + * using callbacks. + * + * Currently implemented only for ARM 32-bit (LDR/STR). + * + * @mem_log_cb: callback to be called when memory access takes place + * @opaque: argument passed to mem_log_cb + * @access_mode: bit field (PROT_READ, PROT_WRITE); fixme - better constants? define new ones? + */ +void memory_set_access_logging_cb( + void (*mem_log_cb)(void * opaque, hwaddr addr, uint64_t value, unsigned size, int is_write), + void * opaque, int access_mode); + #endif #endif diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index 0010c44..d85cc6b 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -140,8 +140,11 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, /* We are only interested in function symbols. Throw everything else away. */ if (syms[i].st_shndx == SHN_UNDEF || - syms[i].st_shndx >= SHN_LORESERVE || - ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { + (!(syms[i].st_shndx == SHN_ABS && /* allow ABS symbols (ML stubs) */ + ELF_ST_TYPE(syms[i].st_info) == STT_NOTYPE && + syms[i].st_size == 0) && + (syms[i].st_shndx >= SHN_LORESERVE || + ELF_ST_TYPE(syms[i].st_info) != STT_FUNC))) { nsyms--; if (i < nsyms) { syms[i] = syms[nsyms]; @@ -157,9 +160,13 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, syms = g_realloc(syms, nsyms * sizeof(*syms)); qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); - for (i = 0; i < nsyms - 1; i++) { + for (i = 0; i < nsyms; i++) { if (syms[i].st_size == 0) { - syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; + if (syms[i].st_shndx == SHN_ABS) { + syms[i].st_size = 4; + } else if (i + 1 < nsyms) { + syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; + } } } diff --git a/include/qemu/log.h b/include/qemu/log.h index 362cbc4..7859c5f 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -12,7 +12,7 @@ /* Private global variables, don't use */ extern FILE *qemu_logfile; -extern int qemu_loglevel; +extern uint64_t qemu_loglevel; /* * The new API: @@ -42,9 +42,50 @@ static inline bool qemu_log_enabled(void) #define CPU_LOG_MMU (1 << 12) #define CPU_LOG_TB_NOCHAIN (1 << 13) +/* various EOS options */ +/* some of them are just defined for future use */ +#define EOS_LOG_IO (1LL << 21) +#define EOS_LOG_IO_LOG (1LL << 22) +#define EOS_LOG_UART (1LL << 23) +#define EOS_LOG_MPU (1LL << 24) +#define EOS_LOG_SDCF (1LL << 25) +#define EOS_LOG_SFLASH (1LL << 26) +#define EOS_LOG_PFLASH (1LL << 27) +#define EOS_LOG_DMA (1LL << 28) +#define EOS_LOG_EDMAC (1LL << 29) + +#define EOS_LOG_VERBOSE (1LL << 32) +#define EOS_LOG_AUTOEXEC (1LL << 33) + +/* guest memory tracing (logging) */ +#define EOS_LOG_RAM_R (1LL << 40) /* memory logging backends */ +#define EOS_LOG_RAM_W (1LL << 41) /* used by other analysis tools, but not printed directly */ +#define EOS_LOG_ROM_R (1LL << 42) /* these backends have additional overhead on generated code */ +#define EOS_LOG_ROM_W (1LL << 43) /* so they are only enabled when actually used */ +#define EOS_LOG_RAM (EOS_LOG_RAM_R | EOS_LOG_RAM_W) +#define EOS_LOG_ROM (EOS_LOG_ROM_R | EOS_LOG_ROM_W) +#define EOS_LOG_MEM_R (EOS_LOG_RAM_R | EOS_LOG_ROM_R) +#define EOS_LOG_MEM_W (EOS_LOG_RAM_W | EOS_LOG_ROM_W) +#define EOS_LOG_MEM (EOS_LOG_RAM | EOS_LOG_ROM) +#define EOS_PR(mem_flag) ((mem_flag) << 4) /* memory logging printed in logs (duplicate the above flags) */ + /* this will take 4 bits: 40-43 => 44-47 */ +#define EOS_LOG_RAM_DBG (1LL << 48) /* self-test */ +#define EOS_LOG_TASKS (1LL << 49) /* task switches */ +#define EOS_LOG_DEBUGMSG (1LL << 50) /* DebugMsg calls */ + +/* analysis tools */ +#define EOS_LOG_CALLSTACK (1LL << 51) /* backend: provide call stack to other tools */ +#define EOS_LOG_CALLS (1LL << 52) /* log all calls and returns to console */ +#define EOS_LOG_IDC (1LL << 53) /* export unique calls to IDA */ +#define EOS_LOG_RAM_MEMCHK (1LL << 54) /* like valgrind memcheck */ +#define EOS_LOG_RAM_TSKMEM (1LL << 55) /* check task memory ownership assumptions */ +#define EOS_LOG_RAM_SEMCHK (1LL << 56) /* check semaphore usage (like helgrind) */ +#define EOS_LOG_ROMCPY (1LL << 57) /* find memory blocks copied from ROM to RAM */ +#define EOS_LOG_NO_TAIL_CALLS (1LL << 58) /* don't attempt to identify tail calls */ + /* Returns true if a bit is set in the current loglevel mask */ -static inline bool qemu_loglevel_mask(int mask) +static inline bool qemu_loglevel_mask(uint64_t mask) { return (qemu_loglevel & mask) != 0; } @@ -67,7 +108,7 @@ qemu_log_vprintf(const char *fmt, va_list va) /* log only if a bit is set on the current loglevel mask */ -void GCC_FMT_ATTR(2, 3) qemu_log_mask(int mask, const char *fmt, ...); +void GCC_FMT_ATTR(2, 3) qemu_log_mask(uint64_t mask, const char *fmt, ...); /* Special cases: */ @@ -95,7 +136,7 @@ static inline void log_cpu_state(CPUState *cpu, int flags) * * Logs the output of cpu_dump_state() if loglevel includes @mask. */ -static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags) +static inline void log_cpu_state_mask(uint64_t mask, CPUState *cpu, int flags) { if (qemu_loglevel & mask) { log_cpu_state(cpu, flags); @@ -152,7 +193,7 @@ static inline void qemu_log_set_file(FILE *f) /* define log items */ typedef struct QEMULogItem { - int mask; + uint64_t mask; const char *name; const char *help; } QEMULogItem; @@ -163,9 +204,9 @@ extern const QEMULogItem qemu_log_items[]; * changing the log level; it should only be accessed via * the qemu_set_log() wrapper. */ -void do_qemu_set_log(int log_flags, bool use_own_buffers); +void do_qemu_set_log(uint64_t log_flags, bool use_own_buffers); -static inline void qemu_set_log(int log_flags) +static inline void qemu_set_log(uint64_t log_flags) { #ifdef CONFIG_USER_ONLY do_qemu_set_log(log_flags, true); @@ -175,7 +216,7 @@ static inline void qemu_set_log(int log_flags) } void qemu_set_log_filename(const char *filename); -int qemu_str_to_log_mask(const char *str); +uint64_t qemu_str_to_log_mask(const char *str); /* Print a usage message listing all the valid logging categories * to the specified FILE*. diff --git a/memory.c b/memory.c index e193658..9de7251 100644 --- a/memory.c +++ b/memory.c @@ -1761,7 +1761,7 @@ static void memory_region_update_container_subregions(MemoryRegion *subregion) int128_make64(other->addr))) { continue; } -#if 0 +#if 1 printf("warning: subregion collision %llx/%llx (%s) " "vs %llx/%llx (%s)\n", (unsigned long long)offset, @@ -1798,6 +1798,9 @@ void memory_region_add_subregion(MemoryRegion *mr, hwaddr offset, MemoryRegion *subregion) { + fprintf(stderr, "%08X - %08X: %s\n", + (int)offset, (int)offset + (int)subregion->size.lo - 1, + subregion->name); subregion->may_overlap = false; subregion->priority = 0; memory_region_add_subregion_common(mr, offset, subregion); diff --git a/qemu-log.c b/qemu-log.c index 7cb01a8..7b57b2e 100644 --- a/qemu-log.c +++ b/qemu-log.c @@ -22,7 +22,7 @@ static char *logfilename; FILE *qemu_logfile; -int qemu_loglevel; +uint64_t qemu_loglevel; static int log_append = 0; void qemu_log(const char *fmt, ...) @@ -36,7 +36,7 @@ void qemu_log(const char *fmt, ...) va_end(ap); } -void qemu_log_mask(int mask, const char *fmt, ...) +void qemu_log_mask(uint64_t mask, const char *fmt, ...) { va_list ap; @@ -48,7 +48,7 @@ void qemu_log_mask(int mask, const char *fmt, ...) } /* enable or disable low levels log */ -void do_qemu_set_log(int log_flags, bool use_own_buffers) +void do_qemu_set_log(uint64_t log_flags, bool use_own_buffers) { qemu_loglevel = log_flags; if (qemu_loglevel && !qemu_logfile) { @@ -99,7 +99,7 @@ const QEMULogItem qemu_log_items[] = { "show micro ops for each compiled TB" }, { CPU_LOG_TB_OP_OPT, "op_opt", "show micro ops (x86 only: before eflags optimization) and\n" - "after liveness analysis" }, + " after liveness analysis" }, { CPU_LOG_INT, "int", "show interrupts/exceptions in short format" }, { CPU_LOG_EXEC, "exec", @@ -116,10 +116,67 @@ const QEMULogItem qemu_log_items[] = { "log unimplemented functionality" }, { LOG_GUEST_ERROR, "guest_errors", "log when the guest OS does something invalid (eg accessing a\n" - "non-existent register)" }, + " non-existent register)" }, { CPU_LOG_TB_NOCHAIN, "nochain", "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n" - "complete traces" }, + " complete traces; implies -singlestep" }, + + { EOS_LOG_IO | CPU_LOG_TB_NOCHAIN, "io", + "EOS: log low-level I/O activity (implies nochain,singlestep)" }, + { EOS_LOG_IO, "io_quick", + "EOS: log low-level I/O activity (without nochain,singlestep; PC not exact)" }, + { EOS_LOG_IO_LOG | EOS_LOG_IO | CPU_LOG_TB_NOCHAIN, "io_log", + "EOS: for every I/O read, export a mmio_log entry to use in dm-spy-extra.c\n" + " (dm-spy-experiments branch) to see the values from physical hardware." }, + { EOS_LOG_MPU, "mpu", + "EOS: log low-level MPU activity" }, + { EOS_LOG_SFLASH, "sflash", + "EOS: log low-level serial flash activity" }, + { EOS_LOG_SDCF, "sdcf", + "EOS: log low-level SD/CF activity" }, + { EOS_LOG_UART, "uart", + "EOS: log low-level UART activity" }, + + { EOS_PR(EOS_LOG_RAM) | EOS_LOG_RAM, "ram", + "EOS: log all RAM reads and writes" }, + { EOS_PR(EOS_LOG_ROM) | EOS_LOG_ROM, "rom", + "EOS: log all ROM reads and writes" }, + { EOS_PR(EOS_LOG_RAM_R) | EOS_LOG_RAM_R, "ramr", + "EOS: log all RAM reads" }, + { EOS_PR(EOS_LOG_ROM_R) | EOS_LOG_ROM_R, "romr", + "EOS: log all ROM reads" }, + { EOS_PR(EOS_LOG_RAM_W) | EOS_LOG_RAM_W, "ramw", + "EOS: log all RAM writes" }, + { EOS_PR(EOS_LOG_ROM_W) | EOS_LOG_ROM_W, "romw", + "EOS: log all ROM writes" }, + { EOS_LOG_RAM_DBG | EOS_LOG_RAM, "ram_dbg", + "EOS: self-test for the RAM logging routines" }, + + { EOS_LOG_CALLSTACK | CPU_LOG_TB_NOCHAIN, "callstack", + "EOS: reconstruct call stack (implies nochain,singlestep)" }, + { EOS_LOG_CALLS | EOS_LOG_CALLSTACK | CPU_LOG_TB_NOCHAIN | EOS_LOG_RAM_R, "calls", + "EOS: log function calls (implies callstack,nochain,singlestep; monitors RAM reads)" }, + { EOS_LOG_NO_TAIL_CALLS, "notail", + "EOS: don't identify tail calls (for troubleshooting)" }, + { EOS_LOG_IDC | EOS_LOG_CALLSTACK | CPU_LOG_TB_NOCHAIN, "idc", + "EOS: export called functions to IDA (implies callstack,nochain,singlestep)" }, + { EOS_LOG_TASKS, "tasks", + "EOS: log task switches (.current_task_addr must be defined)" }, + { EOS_LOG_DEBUGMSG, "debugmsg", + "EOS: log DebugMsg calls (QEMU_EOS_DEBUGMSG must be defined)" }, + { EOS_LOG_ROMCPY | EOS_LOG_ROM_R | EOS_LOG_RAM_W, "romcpy", + "EOS: find memory blocks copied from ROM to RAM" }, + + { EOS_LOG_RAM_MEMCHK | EOS_LOG_RAM, "memchk", + "EOS: check memory usage (malloc/free, uninitialized values)" }, + + { EOS_LOG_AUTOEXEC, "autoexec", + "EOS: start verbose logging when autoexec.bin is loaded (quiet logging for bootloader)" }, + + { EOS_LOG_VERBOSE, "v", "" }, + { EOS_LOG_VERBOSE, "verbose", + "EOS: very detailed debug messages" }, + { 0, NULL, NULL }, }; @@ -132,10 +189,10 @@ static int cmp1(const char *s1, int n, const char *s2) } /* takes a comma separated list of log masks. Return 0 if error. */ -int qemu_str_to_log_mask(const char *str) +uint64_t qemu_str_to_log_mask(const char *str) { const QEMULogItem *item; - int mask; + uint64_t mask; const char *p, *p1; p = str; diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 25fb1ce..c180f78 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -141,6 +141,7 @@ typedef struct ARMCPU { uint32_t mvfr1; uint32_t mvfr2; uint32_t ctr; + uint32_t tcmtr; uint32_t reset_sctlr; uint32_t id_pfr0; uint32_t id_pfr1; diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 30739fc..e332483 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -501,6 +501,7 @@ static void arm_cpu_initfn(Object *obj) static Property arm_cpu_reset_cbar_property = DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0); +/* fixme: how to change this property from machine code? */ static Property arm_cpu_reset_hivecs_property = DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false); @@ -513,8 +514,9 @@ static Property arm_cpu_has_el3_property = static Property arm_cpu_has_mpu_property = DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true); +/* fixme: how to change this property from machine code? */ static Property arm_cpu_pmsav7_dregion_property = - DEFINE_PROP_UINT32("pmsav7-dregion", ARMCPU, pmsav7_dregion, 16); + DEFINE_PROP_UINT32("pmsav7-dregion", ARMCPU, pmsav7_dregion, 8); static void arm_cpu_post_init(Object *obj) { @@ -711,6 +713,103 @@ static void arm926_initfn(Object *obj) cpu->reset_sctlr = 0x00090078; } +static void arm946_eos_initfn(Object *obj) +{ + /* https://chdk.setepontos.com/index.php?topic=2139.msg19836#msg19836 */ + ARMCPU *cpu = ARM_CPU(obj); + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_MPU); + cpu->midr = 0x41059461; + cpu->ctr = 0x0F112112; /* DIGIC 5: 0x0F192192 */ + cpu->tcmtr = 0x000C00C0; + cpu->reset_sctlr = 0x00000078 | (1 << 13); /* fixme: how to change the HIVECS property? */ + set_feature(&cpu->env, ARM_FEATURE_946EOS); +} + +static void arm946_eos5_initfn(Object *obj) +{ + /* mostly identical to DIGIC 2..4, with minor differences */ + arm946_eos_initfn(obj); + + ARMCPU *cpu = ARM_CPU(obj); + cpu->ctr = 0x0F192192; +} + +static void cortex_r5_initfn(Object *obj); + +static void cortex_r4_eos_initfn(Object *obj) +{ + /* Cortex R4: https://chdk.setepontos.com/index.php?topic=11316.msg124273#msg124273 */ + cortex_r5_initfn(obj); + + /* https://www.magiclantern.fm/forum/index.php?topic=17714.0 */ + ARMCPU *cpu = ARM_CPU(obj); + cpu->midr = 0x411FC143; + cpu->ctr = 0x8003C003; + cpu->tcmtr = 0x00010001; + cpu->id_mmfr3 = 0x00000011; + cpu->id_isar0 = 0x01101111; + cpu->id_isar2 = 0x21232131; + cpu->clidr = 0x09000003; + cpu->ccsidr[0] = 0xF00FE019; + cpu->ccsidr[1] = 0xF00FE019; + cpu->dbgdidr = 0x77040013; /* https://www.magiclantern.fm/forum/index.php?topic=17360.msg202322#msg202322 */ + cpu->reset_sctlr = 0x08E50878 | (1 << 13); /* fixme: how to change the HIVECS property? */ + // 1=MPU, 2=align, 4=DCacheL1, 8=SBO => 8 + // 10=SBO, 20=SBO, 40=SBO, 80=SBZ => 7 + // 100=SBZ, 200=SBZ, 400=SBZ, 800=Z(SBO) => 8 + // 1000=ICacheL1,2000=HIVECS, 4000=RR, 8000=SBZ => 2 (1 << 13) + // 10000=SBO, 20000=BR, 40000=SBO, 80000=DZ => 5 + // 100000=SBZ, 200000=FI/SBO,400000=SBO, 800000=SBO => E + // 1000000=VE, 2000000=EE, 4000000=SBZ, 8000000=NMFIQ => 8 + // 10000000=TRE, 20000000=AFE, 40000000=TE, 80000000=IE => 0 + // bic 0x20000 (disable background region) + // orr 0x1 (enable MPU) + // bic 0x1002000 (disable HIVECS, VE?!) + // orr 1004 (enable L1 DCache & ICache) + // ->reset_sctlr = 0x08E5187D; /* as printed by CHDK cpuinfo running from bootloader */ + cpu->reset_auxcr = 0x00000020; /* ACTLR */ + unset_feature(&cpu->env, ARM_FEATURE_V7MP); +} + +static void cortex_a9_initfn(Object *obj); + +static void cortex_a9_eos_initfn(Object *obj) +{ + /* Cortex A9: http://chdk.setepontos.com/index.php?topic=13014.msg131110#msg131110 */ + cortex_a9_initfn(obj); + + /* https://www.magiclantern.fm/forum/index.php?topic=19737.msg200737#msg200737 */ + ARMCPU *cpu = ARM_CPU(obj); + cpu->midr = 0x414FC091; + cpu->ctr = 0x83338003; + cpu->id_pfr0 = 0x00001231; + cpu->id_pfr1 = 0x00000011; + cpu->id_dfr0 = 0x00010444; + cpu->id_mmfr3 = 0x00102111; + cpu->id_isar4 = 0x00011142; + cpu->clidr = 0x09200003; + cpu->ccsidr[0] = 0x700FE019; + cpu->ccsidr[1] = 0x200FE019; + cpu->reset_sctlr = 0x08C50078; /* best guess */ + // 1=MMU, 2=align, 4=DCache, 8=SBO => 8 + // 10=SBO, 20=SBO, 40=SBO, 80=SBZ => 7 + // 100=SBZ, 200=SBZ, 400=SWP, 800=Z(BP) => 0 + // 1000=ICache, 2000=HIVECS, 4000=RR, 8000=SBZ => 0 + // 10000=SBO, 20000=SBZ, 40000=SBO, 80000=SBZ => 5 + // 100000=SBZ, 200000=SBZ, 400000=SBO, 800000=SBO => C + // 1000000=SBZ, 2000000=EE, 4000000=SBZ, 8000000=NMFIQ => 8 + // 10000000=TRE, 20000000=AFE, 40000000=TE, 80000000=SBZ => 0 + // bic 0x1003005 (disable MMU, DCache, ICache, HIVECS, ???) + // orr 0x40000800 (enable branch predictor and Thumb exceptions) + // orr 1005 (enable MMU, DCache, ICache) + // ->reset_sctlr = 0x48C5187D; /* as printed by CHDK cpuinfo running from bootloader */ + cpu->reset_auxcr = 0x00000045; /* ACTLR */ + //cpu->actlr2 = 0x00000201; + //cpu->cpacr = 0xC0000000; + //cpu->dbgdidr = TODO; +} + static void arm946_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -914,9 +1013,20 @@ static void arm_v7m_class_init(ObjectClass *oc, void *data) static const ARMCPRegInfo cortexr5_cp_reginfo[] = { /* Dummy the TCM region regs for the moment */ - { .name = "ATCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, + /* fixme: resetvalue hardcoded for DIGIC 6 */ + { .name = "ATCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c15_atcm), .resetvalue = 0x00000015 }, + { .name = "BTCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c15_btcm), .resetvalue = 0x8000001D }, + { .name = "INV_DCACHE", .cp = 15, .opc1 = 0, .crn = 15, .crm = 5, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST }, + { .name = "BUILDOPTS", .cp = 15, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_CONST }, - { .name = "BTCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, + { .name = "BUILDOPTS1", .cp = 15, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0x00010000 }, + { .name = "BUILDOPTS2", .cp = 15, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0x00CFC010 }, + { .name = "ACTLR2", .cp = 15, .opc1 = 0, .crn = 15, .crm = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST }, REGINFO_SENTINEL }; @@ -1329,6 +1439,8 @@ static const ARMCPUInfo arm_cpus[] = { #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) { .name = "arm926", .initfn = arm926_initfn }, { .name = "arm946", .initfn = arm946_initfn }, + { .name = "arm946-eos", .initfn = arm946_eos_initfn }, + { .name = "arm946-eos5", .initfn = arm946_eos5_initfn }, { .name = "arm1026", .initfn = arm1026_initfn }, /* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an * older core than plain "arm1136". In particular this does not @@ -1342,9 +1454,11 @@ static const ARMCPUInfo arm_cpus[] = { .class_init = arm_v7m_class_init }, { .name = "cortex-m4", .initfn = cortex_m4_initfn, .class_init = arm_v7m_class_init }, + { .name = "cortex-r4-eos",.initfn = cortex_r4_eos_initfn }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, + { .name = "cortex-a9-eos",.initfn = cortex_a9_eos_initfn }, { .name = "cortex-a15", .initfn = cortex_a15_initfn }, { .name = "ti925t", .initfn = ti925t_initfn }, { .name = "sa1100", .initfn = sa1100_initfn }, diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 815fef8..74ce4a9 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -389,6 +389,9 @@ typedef struct CPUARMState { uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */ uint64_t vpidr_el2; /* Virtualization Processor ID Register */ uint64_t vmpidr_el2; /* Virtualization Multiprocessor ID Register */ + + uint32_t c15_atcm; + uint32_t c15_btcm; } cp15; struct { @@ -912,6 +915,7 @@ enum arm_features { ARM_FEATURE_V8_SHA256, /* implements SHA256 part of v8 Crypto Extensions */ ARM_FEATURE_V8_PMULL, /* implements PMULL part of v8 Crypto Extensions */ ARM_FEATURE_THUMB_DSP, /* DSP insns supported in the Thumb encodings */ + ARM_FEATURE_946EOS, /* EOS-specific (DIGIC 2...5) */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target-arm/helper.c b/target-arm/helper.c index afc4163..632ea26 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -549,6 +549,13 @@ static const ARMCPRegInfo not_v6_cp_reginfo[] = { REGINFO_SENTINEL }; +static const ARMCPRegInfo eos_wfi_cp_reginfo[] = { + /* similar to not_v6_cp_reginfo, but .crn was changed from 7 to 15 */ + { .name = "WFI_v5", .cp = 15, .crn = 15, .crm = 8, .opc1 = 0, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_WFI }, + REGINFO_SENTINEL +}; + static const ARMCPRegInfo not_v7_cp_reginfo[] = { /* Standard v6 WFI (also used in some pre-v6 cores); not in v7 (which * is UNPREDICTABLE; we choose to NOP as most implementations do). @@ -598,6 +605,138 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { REGINFO_SENTINEL }; +static const ARMCPRegInfo eos_tcm_cp_reginfo[] = { + { .name = "ITCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c15_atcm), .resetvalue = 0x00000006 }, + { .name = "DTCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c15_btcm), .resetvalue = 0x40000006 }, + REGINFO_SENTINEL +}; + +/* ARM946E-S (EOS) cache lockdown emulation */ +/* note: the model is very incomplete, just enough to emulate + * the cache hacks from Magic Lantern */ + +struct cache_patch +{ + uint32_t addr; + uint32_t old; + uint32_t new; +}; + +static struct cache_patch cache_patches[32]; + +static int num_cache_patches = 0; + + +static uint64_t eos_cache_lockdown_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + fprintf(stderr, "Lockdown read %x\n", ri->crm); + return 0; +} + +static void eos_cache_patch(uint32_t addr, uint32_t value) +{ + uint32_t old; + cpu_physical_memory_read(addr, &old, sizeof(old)); + + if (value != old) + { + fprintf(stderr, "Cache patch: [%08X] <- %X (was %X)\n", addr, value, old); + cache_patches[num_cache_patches++] = (struct cache_patch) { .addr = addr, .old = old, .new = value }; + cpu_physical_memory_write(addr, &value, sizeof(value)); + } +} + +static void eos_revert_cache_patches(void) +{ + for ( ; num_cache_patches > 0; num_cache_patches--) + { + struct cache_patch * p = &cache_patches[num_cache_patches - 1]; + fprintf(stderr, "Reverting cache patch: [%08X] <- %X (was patched to %X)\n", p->addr, p->old, p->new); + cpu_physical_memory_write(p->addr, &p->old, sizeof(p->old)); + } +} + +static void eos_cache_lockdown_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t val) +{ + static uint32_t index = 0; + static uint32_t itag = 0; + static uint32_t dtag = 0; + + /* FIXME: not exactly accurate, just a rough approximation */ + /* no difference is made between data and instruction cache - + * first cache flush or lockdown disable event will revert all cache patches */ + switch (ri->crn) + { + case 7: + /* FlushICache */ + eos_revert_cache_patches(); + return; + + case 9: + /* DLockDown / ILockDown */ + if (val == 0) { + eos_revert_cache_patches(); + } + CPREG_FIELD32(env, ri) = val; + return; + + case 15: + /* see the next switch */ + break; + + default: + /* unreachable */ + assert(0); + } + + switch (ri->crm) + { + case 0: /* cache debug index register */ + index = val; + break; + + case 1: /* icache tag */ + itag = val; + break; + + case 2: /* dcache tag */ + dtag = val; + break; + + case 3: /* icache value */ + eos_cache_patch((itag & 0xFFFFFFE0) | (index & 0x1C), val); + break; + + case 4: /* dcache value */ + eos_cache_patch((dtag & 0xFFFFFFE0) | (index & 0x1C), val); + break; + } +} + +static const ARMCPRegInfo eos_lockdown_cp_reginfo[] = { + { .name = "CacheDbgIdx", .cp = 15, .crn = 15, .crm = 0, .opc1 = 3, .opc2 = 0, + .access = PL1_RW, .readfn = eos_cache_lockdown_read, .writefn = eos_cache_lockdown_write }, + { .name = "IcacheTag", .cp = 15, .crn = 15, .crm = 1, .opc1 = 3, .opc2 = 0, + .access = PL1_RW, .readfn = eos_cache_lockdown_read, .writefn = eos_cache_lockdown_write }, + { .name = "DcacheTag", .cp = 15, .crn = 15, .crm = 2, .opc1 = 3, .opc2 = 0, + .access = PL1_RW, .readfn = eos_cache_lockdown_read, .writefn = eos_cache_lockdown_write }, + { .name = "IcacheVal", .cp = 15, .crn = 15, .crm = 3, .opc1 = 3, .opc2 = 0, + .access = PL1_RW, .readfn = eos_cache_lockdown_read, .writefn = eos_cache_lockdown_write }, + { .name = "DcacheVal", .cp = 15, .crn = 15, .crm = 4, .opc1 = 3, .opc2 = 0, + .access = PL1_RW, .readfn = eos_cache_lockdown_read, .writefn = eos_cache_lockdown_write }, + { .name = "DLockDown", .cp = 15, .crn = 9, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_data), + .writefn = eos_cache_lockdown_write, .resetvalue = 0, .type = ARM_CP_OVERRIDE }, + { .name = "ILockDown", .cp = 15, .crn = 9, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_insn), + .writefn = eos_cache_lockdown_write, .resetvalue = 0, .type = ARM_CP_OVERRIDE }, + { .name = "FlushICache", .cp = 15, .crn = 7, .crm = 5, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .readfn = eos_cache_lockdown_read, .writefn = eos_cache_lockdown_write }, + REGINFO_SENTINEL +}; + static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -4410,7 +4549,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) /* TCMTR and TLBTR exist in v8 but have no 64-bit versions */ { .name = "TCMTR", .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 = cpu->tcmtr }, REGINFO_SENTINEL }; /* TLBTR is specific to VMSA */ @@ -4542,6 +4681,13 @@ void register_cp_regs_for_features(ARMCPU *cpu) } define_one_arm_cp_reg(cpu, &sctlr); } + + /* Features specific to DIGIC 2..5 (EOS) */ + if (arm_feature(env, ARM_FEATURE_946EOS)) { + define_arm_cp_regs(cpu, eos_wfi_cp_reginfo); + define_arm_cp_regs(cpu, eos_lockdown_cp_reginfo); + define_arm_cp_regs(cpu, eos_tcm_cp_reginfo); + } } ARMCPU *cpu_arm_init(const char *cpu_model) @@ -5422,7 +5568,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) uint32_t lr; uint32_t addr; - arm_log_exception(cs->exception_index); + arm_log_exception(cs->exception_index, env); lr = 0xfffffff1; if (env->v7m.current_sp) @@ -5719,7 +5865,7 @@ void arm_cpu_do_interrupt(CPUState *cs) assert(!IS_M(env)); - arm_log_exception(cs->exception_index); + arm_log_exception(cs->exception_index, env); if (arm_is_psci_call(cpu, cs->exception_index)) { arm_handle_psci_call(cpu); @@ -5891,11 +6037,21 @@ void arm_cpu_do_interrupt(CPUState *cs) /* Switch to the new mode, and to the correct instruction set. */ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; env->daif |= mask; - /* 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 */ - if (arm_feature(env, ARM_FEATURE_V4T)) { + + if (arm_feature(env, ARM_FEATURE_V6) || + arm_feature(env, ARM_FEATURE_V7) || + arm_feature(env, ARM_FEATURE_V8)) { + /* newer processors execute the interrupts in Thumb mode + * if the SCTLR TE bit is enabled */ env->thumb = (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_TE) != 0; + } else if (arm_feature(env, ARM_FEATURE_V4T)) { + /* there was no c1_sys on V4T/V5, + * and we should just guard the thumb mode on V4 + * note: arm946eos uses the SCTLR TE bit for something else, + * so we can't assume this bit being always 0 on older platforms */ + env->thumb = 0; } + env->regs[14] = env->regs[15] + offset; env->regs[15] = addr; cs->interrupt_request |= CPU_INTERRUPT_EXITTB; diff --git a/target-arm/helper.h b/target-arm/helper.h index c2a85c7..98aa94b 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -64,10 +64,15 @@ DEF_HELPER_2(v7m_mrs, i32, env, i32) DEF_HELPER_3(access_check_cp_reg, void, env, ptr, i32) DEF_HELPER_3(set_cp_reg, void, env, ptr, i32) +DEF_HELPER_3(print_set_cp_reg, void, env, ptr, i32) +DEF_HELPER_3(print_get_cp_reg, void, env, ptr, i32) DEF_HELPER_2(get_cp_reg, i32, env, ptr) DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64) DEF_HELPER_2(get_cp_reg64, i64, env, ptr) +DEF_HELPER_3(log_ldr, void, i32, i32, i32) +DEF_HELPER_3(log_str, void, i32, i32, i32) + DEF_HELPER_3(msr_i_pstate, void, env, i32, i32) DEF_HELPER_1(clear_pstate_ss, void, env) DEF_HELPER_1(exception_return, void, env) diff --git a/target-arm/internals.h b/target-arm/internals.h index 347998c..6265345 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -72,7 +72,7 @@ static const char * const excnames[] = { [EXCP_SEMIHOST] = "Semihosting call", }; -static inline void arm_log_exception(int idx) +static inline void arm_log_exception(int idx, CPUARMState *env) { if (qemu_loglevel_mask(CPU_LOG_INT)) { const char *exc = NULL; @@ -83,7 +83,7 @@ static inline void arm_log_exception(int idx) if (!exc) { exc = "unknown"; } - qemu_log_mask(CPU_LOG_INT, "Taking exception %d [%s]\n", idx, exc); + qemu_log_mask(CPU_LOG_INT, "%08X: Taking exception %d [%s]\n", env->regs[15], idx, exc); } } diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 6cd54c8..055aaf7 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -474,6 +474,199 @@ void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value) ri->writefn(env, ri, value); } +/* adapted from CHDK cpuinfo */ +static char linebuf[128]; + +static const char *regperm_str(unsigned val) { + switch(val) { + case 0: return "P:-- U:--"; + case 1: return "P:RW U:--"; + case 2: return "P:RW U:R-"; + case 3: return "P:RW U:RW"; + case 5: return "P:R- U:--"; + case 6: return "P:R- U:R-"; + default: + return "P:?? U:??"; + } +} + +static const char *mpu_rattr(unsigned val) { + const char *s=""; + const char *s2=""; + const char *t; + t = (val&4)?"Shared":"Non-shared"; + if (val&0x20) { + switch (val&3) { + case 0: s = "Inner Non-cacheable"; break; + case 1: s = "Inner Write-back, write-allocate"; break; + case 2: s = "Inner Write-through, no write-allocate"; break; + case 3: s = "Inner Write-back, no write-allocate"; break; + } + switch ((val&0x18)>>3) { + case 0: s2 = "Outer Non-cacheable"; break; + case 1: s2 = "Outer Write-back, write-allocate"; break; + case 2: s2 = "Outer Write-through, no write-allocate"; break; + case 3: s2 = "Outer Write-back, no write-allocate"; break; + } + sprintf(linebuf,"%s; %s; %s",s, s2, t); + } + else { + switch (val&0x1B) { + case 0: s = "Strongly ordered, shareable"; t=""; break; + case 1: s = "Shareable device"; t="Shareable"; break; + case 2: s = "Outer and Inner write-through, no write-allocate"; break; + case 3: s = "Outer and Inner write-back, no write-allocate"; break; + case 8: s = "Outer and Inner Non-cacheable"; break; + case 11: s = "Outer and Inner write-back, write-allocate"; break; + case 16: s = "Non-shareable Device"; t=""; break; + default: s = "(reserved)"; t=""; + } + sprintf(linebuf,"%s; %s",s, t); + } + return linebuf; +} + +static void print_cpu_mpidr(CPUARMState *env) +{ + if (arm_feature(env, ARM_FEATURE_V7MP)) { + ARMCPU *cpu = ARM_CPU(arm_env_get_cpu(env)); + uint64_t mpidr = cpu->mp_affinity; + fprintf(stderr, "[CPU%d] ", (int) mpidr & 0xFF); + } +} + +void HELPER(print_set_cp_reg)(CPUARMState *env, void *rip, uint32_t value) +{ + const ARMCPRegInfo *ri = rip; + char desc[256] = ""; + + if (strcmp(ri->name, "DSB") == 0) + { + return; + } + + /* CACHEMAINT registers come in very large groups; + * just display how many calls there were. + * fixme: if there are MRC calls, the display order may be wrong + */ + static int cachemaint_count = 0; + static uint32_t cachemaint_pc = 0; + if (strcmp(ri->name, "CACHEMAINT") == 0) + { + cachemaint_pc = env->regs[15]; + cachemaint_count++; + return; + } + else + { + if (cachemaint_count) + { + print_cpu_mpidr(env); + fprintf(stderr, "%08X: MCR p%d, ... : CACHEMAINT x%d (omitted)\n", cachemaint_pc, ri->cp, cachemaint_count); + cachemaint_count = 0; + } + } + + if (strncmp(ri->name, "946_PRBS", 8) == 0 && + (value & 1)) + { + uint64_t base = value & 0xFFFFF000; + uint64_t size = 1ull << (((value >> 1) & 0x1F) + 1); + uint64_t end = base + size - 1; + snprintf(desc, sizeof(desc), "(%08"PRIX64" - %08"PRIX64", 0x%"PRIX64")", base, end, size); + + /* workaround to emulate io_trace */ + /* fixme - proper solution? */ + ARMCPU *cpu = arm_env_get_cpu(env); + tlb_flush(CPU(cpu), 1); + } + + if (strcmp(ri->name+1, "TCM") == 0) + { + uint64_t base = value & 0xFFFFF000; + uint64_t size = 1ull << (((value >> 1) & 0x1F) + 9); + uint64_t end = base + size - 1; + snprintf(desc, sizeof(desc), "(%08"PRIX64" - %08"PRIX64", 0x%"PRIX64")", base, end, size); + } + + if (strcmp(ri->name, "DRSR") == 0 && + (value & 1)) + { + uint64_t size = 1ull << (((value >> 1) & 0x1F) + 1); + snprintf(desc, sizeof(desc), "(0x%"PRIX64")", size); + + uint32_t rgnr = env->cp15.c6_rgnr; + uint32_t base = env->pmsav7.drbar[rgnr]; + assert(base % size == 0); + + /* subregion disable bits */ + uint32_t subregion_disable = (value & 0xFF00) >> 8; + if (subregion_disable) + { + /* Region sizes of less than 256 bytes do not support subregions + * (Cortex R4 TRM, 7.1.1 Memory regions) */ + assert(size >= 256); + + uint32_t subregion_size = size / 8; + for (int i = 0; i < 8; i++) + { + fprintf(stderr, + "Subregion %08X - %08X: %s\n", + base + subregion_size * i, + base + subregion_size * (i + 1) - 1, + subregion_disable & (1 << i) ? "disabled" : "enabled" + ); + } + } + } + + if (strcmp(ri->name, "DRACR") == 0) + { + uint32_t rattr = value & 0x3F; + uint32_t accpm = (value >> 8) & 3; + uint32_t xn = (value >> 12) & 1; + snprintf(desc, sizeof(desc), + "(%s; %s%s)", + regperm_str(accpm), + mpu_rattr(rattr), + xn ? "; Execute never" : "" + ); + } + + print_cpu_mpidr(env); + fprintf(stderr, "%08X: MCR p%d,%d,Rd,cr%d,cr%d,%d: %10s <- 0x%-8X %s\n", + env->regs[15], ri->cp, + ri->opc1, ri->crn, ri->crm, ri->opc2, + ri->name, value, desc + ); +} + +void HELPER(print_get_cp_reg)(CPUARMState *env, void *rip, uint32_t value) +{ + const ARMCPRegInfo *ri = rip; + + print_cpu_mpidr(env); + fprintf(stderr, "%08X: MRC p%d,%d,Rd,cr%d,cr%d,%d: %10s -> 0x%X\n", + env->regs[15], ri->cp, + ri->opc1, ri->crn, ri->crm, ri->opc2, + ri->name, value + ); +} + +/* fixme: how to pass a pointer here, to call the registered CBR directly? */ +extern void log_ldr_cb(uint32_t addr, uint32_t value, uint32_t opc); +extern void log_str_cb(uint32_t addr, uint32_t value, uint32_t opc); + +void HELPER(log_ldr)(uint32_t addr, uint32_t value, uint32_t opc) +{ + log_ldr_cb(addr, value, opc); +} + +void HELPER(log_str)(uint32_t addr, uint32_t value, uint32_t opc) +{ + log_str_cb(addr, value, opc); +} + uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip) { const ARMCPRegInfo *ri = rip; diff --git a/target-arm/translate.c b/target-arm/translate.c index 5d22879..a413e2c 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -37,6 +37,8 @@ #include "trace-tcg.h" +#define LOG_ALL_CP_READS +#define LOG_ALL_CP_WRITES #define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) #define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5) @@ -923,25 +925,101 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) */ #if TARGET_LONG_BITS == 32 +/* fixme: should be moved to memory.c and accessed somehow from there? */ +/* fixme: use MemoryRegionOps? The read callback doesn't accept address and value */ +struct mem_log +{ + void (*read_cb)(void * opaque, hwaddr addr, uint64_t value, unsigned size, int is_write); + void * read_arg; + void (*write_cb)(void * opaque, hwaddr addr, uint64_t value, unsigned size, int is_write); + void * write_arg; +} mem_log; + +void memory_set_access_logging_cb( + void (*mem_log_cb)(void * opaque, hwaddr addr, uint64_t value, unsigned size, int is_write), + void * opaque, int access_mode) +{ + if (access_mode & PROT_READ) { + mem_log.read_cb = mem_log_cb; + mem_log.read_arg = opaque; + } + if (access_mode & PROT_WRITE) { + mem_log.write_cb = mem_log_cb; + mem_log.write_arg = opaque; + } +} + +/* called from helper_log_ldr */ +void log_ldr_cb(uint32_t addr, uint32_t value, uint32_t opc) +{ + if (mem_log.read_cb) { + unsigned size = 1 << (opc & MO_SIZE); + mem_log.read_cb(mem_log.read_arg, addr, value, size, 0); + } +} + +/* called from helper_log_str */ +void log_str_cb(uint32_t addr, uint32_t value, uint32_t opc) +{ + if (mem_log.write_cb) { + unsigned size = 1 << (opc & MO_SIZE); + mem_log.write_cb(mem_log.write_arg, addr, value, size, 1); + } +} + +static inline void gen_log_ldr(TCGv_i32 addr, TCGv_i32 val, int opc) +{ + /* called after the actual LDR, to get the value too */ + /* only compile the logging code if a CBR was registered */ + if (mem_log.read_cb) { + TCGv_i32 opc_reg = tcg_temp_new_i32(); + tcg_gen_movi_i32(opc_reg, opc); + gen_helper_log_ldr(addr, val, opc_reg); + tcg_temp_free_i32(opc_reg); + } +} + +static inline void gen_log_str(TCGv_i32 addr, TCGv_i32 val, int opc) +{ + /* called before the actual STR */ + /* only compile the logging code if a CBR was registered */ + if (mem_log.write_cb) { + TCGv_i32 opc_reg = tcg_temp_new_i32(); + tcg_gen_movi_i32(opc_reg, opc); + gen_helper_log_str(addr, val, opc_reg); + tcg_temp_free_i32(opc_reg); + } +} + #define DO_GEN_LD(SUFF, OPC) \ static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ { \ tcg_gen_qemu_ld_i32(val, addr, index, OPC); \ + gen_log_ldr(addr, val, OPC); \ } #define DO_GEN_ST(SUFF, OPC) \ static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ { \ + gen_log_str(addr, val, OPC); \ tcg_gen_qemu_st_i32(val, addr, index, OPC); \ } static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) { + if (mem_log.read_cb) { + /* not implemented */ + assert(0); + } tcg_gen_qemu_ld_i64(val, addr, index, MO_TEQ); } static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) { + if (mem_log.write_cb) { + /* not implemented */ + assert(0); + } tcg_gen_qemu_st_i64(val, addr, index, MO_TEQ); } @@ -7222,6 +7300,27 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) /* Handle special cases first */ switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) { case ARM_CP_NOP: + #if defined(LOG_ALL_CP_READS) || defined(LOG_ALL_CP_WRITES) + if (!is64) { + /* log all reads to coprocessor registers (32-bit) */ + TCGv_ptr tmpptr; + TCGv_i32 tmp; + tmpptr = tcg_const_ptr(ri); + if (isread) { + #ifdef LOG_ALL_CP_READS + tmp = load_cpu_offset(ri->fieldoffset); + gen_helper_print_get_cp_reg(cpu_env, tmpptr, tmp); + #endif + } else { + #ifdef LOG_ALL_CP_WRITES + tmp = load_reg(s, rt); + gen_helper_print_set_cp_reg(cpu_env, tmpptr, tmp); + #endif + } + tcg_temp_free_ptr(tmpptr); + tcg_temp_free_i32(tmp); + } + #endif return 0; case ARM_CP_WFI: if (isread) { @@ -7276,6 +7375,15 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) } else { tmp = load_cpu_offset(ri->fieldoffset); } + + #ifdef LOG_ALL_CP_READS + /* log all reads to coprocessor registers (32-bit) */ + TCGv_ptr tmpptr; + tmpptr = tcg_const_ptr(ri); + gen_helper_print_get_cp_reg(cpu_env, tmpptr, tmp); + tcg_temp_free_ptr(tmpptr); + #endif + if (rt == 15) { /* Destination register of r15 for 32 bit loads sets * the condition codes from the high 4 bits of the value @@ -7287,6 +7395,19 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) } } } else { + #ifdef LOG_ALL_CP_WRITES + if (!is64) { + /* log all writes to coprocessor registers (32-bit) */ + TCGv_i32 tmp; + TCGv_ptr tmpptr; + tmp = load_reg(s, rt); + tmpptr = tcg_const_ptr(ri); + gen_helper_print_set_cp_reg(cpu_env, tmpptr, tmp); + tcg_temp_free_ptr(tmpptr); + tcg_temp_free_i32(tmp); + } + #endif + /* Write */ if (ri->type & ARM_CP_CONST) { /* If not forbidden by access permissions, treat as WI */ diff --git a/target-arm/translate.h b/target-arm/translate.h index 53ef971..260331a 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -151,4 +151,8 @@ void arm_free_cc(DisasCompare *cmp); void arm_jump_cc(DisasCompare *cmp, TCGLabel *label); void arm_gen_test_cc(int cc, TCGLabel *label); +/* fixme: duplicate declaration in op_helper.c */ +void log_ldr_cb(uint32_t addr, uint32_t value, uint32_t opc); +void log_str_cb(uint32_t addr, uint32_t value, uint32_t opc); + #endif /* TARGET_ARM_TRANSLATE_H */ diff --git a/util/memfd.c b/util/memfd.c index 7c40691..3636f0d 100644 --- a/util/memfd.c +++ b/util/memfd.c @@ -34,9 +34,7 @@ #include "qemu/memfd.h" -#ifdef CONFIG_MEMFD -#include -#elif defined CONFIG_LINUX +#if defined CONFIG_LINUX && !defined CONFIG_MEMFD #include #include diff --git a/vl.c b/vl.c index 4211ff1..93d4df4 100644 --- a/vl.c +++ b/vl.c @@ -4102,7 +4102,7 @@ int main(int argc, char **argv, char **envp) } if (log_mask) { - int mask; + uint64_t mask; mask = qemu_str_to_log_mask(log_mask); if (!mask) { qemu_print_log_usage(stdout);