unicorn/qemu/target/m68k/unicorn.c

109 lines
2.9 KiB
C
Raw Normal View History

2015-08-21 07:04:50 +00:00
/* Unicorn Emulator Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015 */
#include "qemu/osdep.h"
#include "cpu.h"
2015-08-21 07:04:50 +00:00
#include "hw/boards.h"
#include "hw/m68k/m68k.h"
#include "sysemu/cpus.h"
#include "unicorn.h"
#include "unicorn_common.h"
#include "uc_priv.h"
2015-08-21 07:04:50 +00:00
const int M68K_REGS_STORAGE_SIZE = offsetof(CPUM68KState, tlb_table);
2015-08-21 07:04:50 +00:00
static void m68k_set_pc(struct uc_struct *uc, uint64_t address)
{
CPUM68KState *state = uc->cpu->env_ptr;
state->pc = address;
2015-08-21 07:04:50 +00:00
}
2016-10-03 19:47:03 +00:00
void m68k_release(void* ctx);
void m68k_release(void* ctx)
{
TCGContext *tcg_ctx = ctx;;
2016-10-03 19:47:03 +00:00
release_common(ctx);
translate-all: use a binary search tree to track TBs in TBContext This is a prerequisite for supporting multiple TCG contexts, since we will have threads generating code in separate regions of code_gen_buffer. For this we need a new field (.size) in struct tb_tc to keep track of the size of the translated code. This field uses a size_t to avoid adding a hole to the struct, although really an unsigned int would have been enough. The comparison function we use is optimized for the common case: insertions. Profiling shows that upon booting debian-arm, 98% of comparisons are between existing tb's (i.e. a->size and b->size are both !0), which happens during insertions (and removals, but those are rare). The remaining cases are lookups. From reading the glib sources we see that the first key is always the lookup key. However, the code does not assume this to always be the case because this behaviour is not guaranteed in the glib docs. However, we embed this knowledge in the code as a branch hint for the compiler. Note that tb_free does not free space in the code_gen_buffer anymore, since we cannot easily know whether the tb is the last one inserted in code_gen_buffer. The next patch in this series renames tb_free to tb_remove to reflect this. Performance-wise, lookups in tb_find_pc are the same as before: O(log n). However, insertions are O(log n) instead of O(1), which results in a small slowdown when booting debian-arm: Performance counter stats for 'build/arm-softmmu/qemu-system-arm \ -machine type=virt -nographic -smp 1 -m 4096 \ -netdev user,id=unet,hostfwd=tcp::2222-:22 \ -device virtio-net-device,netdev=unet \ -drive file=img/arm/jessie-arm32.qcow2,id=myblock,index=0,if=none \ -device virtio-blk-device,drive=myblock \ -kernel img/arm/aarch32-current-linux-kernel-only.img \ -append console=ttyAMA0 root=/dev/vda1 \ -name arm,debug-threads=on -smp 1' (10 runs): - Before: 8048.598422 task-clock (msec) # 0.931 CPUs utilized ( +- 0.28% ) 16,974 context-switches # 0.002 M/sec ( +- 0.12% ) 0 cpu-migrations # 0.000 K/sec 10,125 page-faults # 0.001 M/sec ( +- 1.23% ) 35,144,901,879 cycles # 4.367 GHz ( +- 0.14% ) <not supported> stalled-cycles-frontend <not supported> stalled-cycles-backend 65,758,252,643 instructions # 1.87 insns per cycle ( +- 0.33% ) 10,871,298,668 branches # 1350.707 M/sec ( +- 0.41% ) 192,322,212 branch-misses # 1.77% of all branches ( +- 0.32% ) 8.640869419 seconds time elapsed ( +- 0.57% ) - After: 8146.242027 task-clock (msec) # 0.923 CPUs utilized ( +- 1.23% ) 17,016 context-switches # 0.002 M/sec ( +- 0.40% ) 0 cpu-migrations # 0.000 K/sec 18,769 page-faults # 0.002 M/sec ( +- 0.45% ) 35,660,956,120 cycles # 4.378 GHz ( +- 1.22% ) <not supported> stalled-cycles-frontend <not supported> stalled-cycles-backend 65,095,366,607 instructions # 1.83 insns per cycle ( +- 1.73% ) 10,803,480,261 branches # 1326.192 M/sec ( +- 1.95% ) 195,601,289 branch-misses # 1.81% of all branches ( +- 0.39% ) 8.828660235 seconds time elapsed ( +- 0.38% ) Backports commit 2ac01d6dafabd4a726254eea98824c798d416ee4 from qemu
2018-03-13 19:34:46 +00:00
g_tree_destroy(s->tb_ctx.tb_tree);
2016-10-03 19:47:03 +00:00
}
2015-08-26 11:11:49 +00:00
void m68k_reg_reset(struct uc_struct *uc)
2015-08-21 07:04:50 +00:00
{
2016-09-23 14:38:21 +00:00
CPUArchState *env = uc->cpu->env_ptr;
2015-08-21 07:04:50 +00:00
memset(env->aregs, 0, sizeof(env->aregs));
memset(env->dregs, 0, sizeof(env->dregs));
env->pc = 0;
}
2016-04-04 15:25:30 +00:00
int m68k_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int count)
2015-08-21 07:04:50 +00:00
{
2016-09-23 14:38:21 +00:00
CPUState *mycpu = uc->cpu;
CPUM68KState *state = &M68K_CPU(uc, mycpu)->env;
2016-04-04 15:25:30 +00:00
int i;
2015-08-21 07:04:50 +00:00
2016-04-04 15:25:30 +00:00
for (i = 0; i < count; i++) {
unsigned int regid = regs[i];
void *value = vals[i];
if (regid >= UC_M68K_REG_A0 && regid <= UC_M68K_REG_A7)
*(int32_t *)value = state->aregs[regid - UC_M68K_REG_A0];
2016-04-04 15:25:30 +00:00
else if (regid >= UC_M68K_REG_D0 && regid <= UC_M68K_REG_D7)
*(int32_t *)value = state->dregs[regid - UC_M68K_REG_D0];
2016-04-04 15:25:30 +00:00
else {
switch(regid) {
default: break;
case UC_M68K_REG_PC:
*(int32_t *)value = state->pc;
2016-04-04 15:25:30 +00:00
break;
}
2015-08-21 07:04:50 +00:00
}
}
return 0;
}
2016-04-04 15:25:30 +00:00
int m68k_reg_write(struct uc_struct *uc, unsigned int *regs, void *const *vals, int count)
2015-08-21 07:04:50 +00:00
{
2016-09-23 14:38:21 +00:00
CPUState *mycpu = uc->cpu;
CPUM68KState *state = &M68K_CPU(uc, mycpu)->env;
2016-04-04 15:25:30 +00:00
int i;
2015-08-21 07:04:50 +00:00
2016-04-04 15:25:30 +00:00
for (i = 0; i < count; i++) {
unsigned int regid = regs[i];
const void *value = vals[i];
if (regid >= UC_M68K_REG_A0 && regid <= UC_M68K_REG_A7)
state->aregs[regid - UC_M68K_REG_A0] = *(uint32_t *)value;
2016-04-04 15:25:30 +00:00
else if (regid >= UC_M68K_REG_D0 && regid <= UC_M68K_REG_D7)
state->dregs[regid - UC_M68K_REG_D0] = *(uint32_t *)value;
2016-04-04 15:25:30 +00:00
else {
switch(regid) {
default: break;
case UC_M68K_REG_PC:
state->pc = *(uint32_t *)value;
2016-04-04 15:25:30 +00:00
// force to quit execution and flush TB
uc->quit_request = true;
uc_emu_stop(uc);
break;
}
2015-08-21 07:04:50 +00:00
}
}
return 0;
}
DEFAULT_VISIBILITY
2015-08-21 07:04:50 +00:00
void m68k_uc_init(struct uc_struct* uc)
{
register_accel_types(uc);
m68k_cpu_register_types(uc);
dummy_m68k_machine_init_register_types(uc);
2016-10-03 19:47:03 +00:00
uc->release = m68k_release;
2015-08-21 07:04:50 +00:00
uc->reg_read = m68k_reg_read;
uc->reg_write = m68k_reg_write;
uc->reg_reset = m68k_reg_reset;
uc->set_pc = m68k_set_pc;
uc_common_init(uc);
}