8/30/2011 4:09:14 PM
没有找到对应的操作翻译
枚举了操作数
INDEX_op_end
decode_opc
arm_translate_init 这个是在初始化机器的时候就调用了
cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
本质上也就是返回一个int
参数一个枚举变量 一个字符串
TCGv_i32 tcg_global_reg_new_i32(int reg, const char *name)
{
int idx;
idx = tcg_global_reg_new_internal(TCG_TYPE_I32, reg, name);
return MAKE_TCGV_I32(idx);
}
创建一个32位的tcg Reg ,id 和name
struct TCGContext {
uint8_t *pool_cur, *pool_end;
TCGPool *pool_first, *pool_current;
TCGLabel *labels;
int nb_labels;
TCGTemp *temps; /* globals first, temps after */
int nb_globals;
int nb_temps;
/* index of free temps, -1 if none */
int first_free_temp[TCG_TYPE_COUNT * 2];
/* goto_tb support */
uint8_t *code_buf;
unsigned long *tb_next;
uint16_t *tb_next_offset;
uint16_t *tb_jmp_offset; /* != NULL if USE_DIRECT_JUMP */
/* liveness analysis */
uint16_t *op_dead_iargs; /* for each operation, each bit tells if the
corresponding input argument is dead */
/* tells in which temporary a given register is. It does not take
into account fixed registers */
int reg_to_temp[TCG_TARGET_NB_REGS];
TCGRegSet reserved_regs;
tcg_target_long current_frame_offset;
tcg_target_long frame_start;
tcg_target_long frame_end;
int frame_reg;
uint8_t *code_ptr;
TCGTemp static_temps[TCG_MAX_TEMPS];
TCGHelperInfo *helpers;
int nb_helpers;
int allocated_helpers;
int helpers_sorted;
#ifdef CONFIG_PROFILER
/* profiling info */
int64_t tb_count1;
int64_t tb_count;
int64_t op_count; /* total insn count */
int op_count_max; /* max insn per TB */
int64_t temp_count;
int temp_count_max;
int64_t del_op_count;
int64_t code_in_len;
int64_t code_out_len;
int64_t interm_time;
int64_t code_time;
int64_t la_time;
int64_t restore_count;
int64_t restore_time;
#endif
};
就一个全局的TCG上下文
同一个类型的不能重复分配
typedef struct TCGTemp {
TCGType base_type;
TCGType type;
int val_type;
int reg;
tcg_target_long val;
int mem_reg;
tcg_target_long mem_offset;
unsigned int fixed_reg:1;
unsigned int mem_coherent:1;
unsigned int mem_allocated:1;
unsigned int temp_local:1; /* If true, the temp is saved accross
basic blocks. Otherwise, it is not
preserved accross basic blocks. */
unsigned int temp_allocated:1; /* never used for code gen */
/* index of next free temp of same base type, -1 if end */
int next_free_temp;
const char *name;
} TCGTemp;
每一个都有这样一个结构体
/* The code generator doesn't like lots of temporaries, so maintain our own
cache for reuse within a function. */
#define MAX_TEMPS 8
static int num_temps;
static TCGv temps[MAX_TEMPS];
最大能放8个Reg?
/* Allocate a temporary variable. */
static TCGv_i32 new_tmp(void)
{
TCGv tmp;
if (num_temps == MAX_TEMPS)
abort();
if (GET_TCGV_I32(temps[num_temps]))
return temps[num_temps++];
tmp = tcg_temp_new_i32();
temps[num_temps++] = tmp;
return tmp;
}
就说获取数组的指定元素
/* Release a temporary variable. */
static void dead_tmp(TCGv tmp)
{
int i;
num_temps--; //这是个数
i = num_temps;
if (TCGV_EQUAL(temps[i], tmp))
return;
//如果最后一个就是,最方便了 直接返回
/* Shuffle this temp to the last slot. */
while (!TCGV_EQUAL(temps[i], tmp))
i--;
while (i < num_temps) {
temps[i] = temps[i + 1];
i++;
}
temps[i] = tmp;
}
这个dead_tmp 是怎么回事?
移动到最后。。。
static inline TCGv load_cpu_offset(int offset)
{
TCGv tmp = new_tmp();
tcg_gen_ld_i32(tmp, cpu_env, offset);
return tmp;
}
你要产生load 指令么?
static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset)
{
tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset);
}
uint16_t *gen_opc_ptr;
TCGArg *gen_opparam_ptr;
操作码指针和操作数指针
static inline void tcg_gen_ldst_op_i32(int opc, TCGv_i32 val, TCGv_ptr base,
TCGArg offset)
{
*gen_opc_ptr++ = opc;
*gen_opparam_ptr++ = GET_TCGV_I32(val);
*gen_opparam_ptr++ = GET_TCGV_PTR(base);
*gen_opparam_ptr++ = offset;
}
返回值 ?? 参数 偏移
INDEX_op_ld_i32
插入指令流和数据流中
#define load_cpu_field(name) load_cpu_offset(offsetof(CPUState, name))
加载CPU状态的某个域
有load 就有store
static inline void store_cpu_offset(TCGv var, int offset)
{
tcg_gen_st_i32(var, cpu_env, offset);
dead_tmp(var);
}
#define store_cpu_field(var, name) \
store_cpu_offset(var, offsetof(CPUState, name))
/* internal defines */
typedef struct DisasContext {
target_ulong pc;
int is_jmp;
/* Nonzero if this instruction has been conditionally skipped. */
int condjmp;
/* The label that will be jumped to when the instruction is skipped. */
int condlabel;
/* Thumb-2 condtional execution bits. */
int condexec_mask;
int condexec_cond;
int condexec_mask_prev; /* mask at start of instruction/block */
struct TranslationBlock *tb;
int singlestep_enabled;
int thumb;
#if !defined(CONFIG_USER_ONLY)
int user;
#endif
#ifdef CONFIG_MEMCHECK
int search_pc;
#endif // CONFIG_MEMCHECK
} DisasContext;
/* Set a variable to the value of a CPU register. */
static void load_reg_var(DisasContext *s, TCGv var, int reg)
{
if (reg == 15) {
uint32_t addr;
/* normaly, since we updated PC, we need only to add one insn */
if (s->thumb)
addr = (long)s->pc + 2;
else
addr = (long)s->pc + 4;
tcg_gen_movi_i32(var, addr);
} else {
tcg_gen_ld_i32(var, cpu_env, offsetof(CPUState, regs[reg]));
}
}
如果想加载寄存器就再CPU环境中加载
为某个CPU寄存器赋值
读取当前PC位置的code
谁到谁的转换
对于全系统模拟,直接 Hack SoftMMU 模块显得更为简单有效,通过往 softmmu-template.c 文件
中 的 glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, mem_index) /glue(glue(__st, SUFFIX), MMUSUFFIX)(addr, mem_index)
函数直接添加 C 代码可以方便的记录所需要的 Trace,其中 SUFFIX 表示 ld/st 的长度, addr 为 Guest 访存地址,mem_index 为相应的偏移量。
基于此,TCG就把微指令按以上几大类定义(见tcg/i386/tcg-target.c),例如:其中一个最简单的函数 tcg_out_movi 如下:
// tcg/tcg.c
static inline void tcg_out8(TCGContext *s, uint8_t v)
{
*s->code_ptr++ = v;
}
static inline void tcg_out32(TCGContext *s, uint32_t v)
{
*(uint32_t *)s->code_ptr = v;
s->code_ptr += 4;
}
// tcg/i386/tcg-target.c
static inline void tcg_out_movi(TCGContext *s, TCGType type,
int ret, int32_t arg)
{
if (arg == 0) {
/* xor r0,r0 */
tcg_out_modrm(s, 0x01 | (ARITH_XOR << 3), ret, ret);
} else {
tcg_out8(s, 0xb8 + ret); // 输出操作码,ret是寄存器索引
tcg_out32(s, arg); // 输出操作数
}
}
gen_opc_buf:操作码缓冲区
gen_opparam_buf:参数缓冲区
gen_code_buf:存放翻译后指令的缓冲区
gen_opc_ptr、gen_opparam_ptr、gen_code_ptr三个指针变量分别指向上述缓冲区。
这3个缓冲区
tb_find_pc
根据ret地址来找到对应的tb
这些tb的指针构成一个数组
jmp_next jmp_first 组成一个单链表
ptb = &tb_next->jmp_first;
for(;;) {
tb1 = *ptb;
n1 = (long)tb1 & 3;
tb1 = (TranslationBlock *)((long)tb1 & ~3);
if (n1 == n && tb1 == tb)
break;
ptb = &tb1->jmp_next[n1];
}
first了再next
tb_next_offset
//
struct TranslationBlock {
target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */
target_ulong cs_base; /* CS base for this block */
uint64_t flags; /* flags defining in which context the code was generated */
uint16_t size; /* size of target code for this block (1 <=
size <= TARGET_PAGE_SIZE) */
uint16_t cflags; /* compile flags */
#define CF_COUNT_MASK 0x7fff
#define CF_LAST_IO 0x8000 /* Last insn may be an IO access. */
uint8_t *tc_ptr; /* pointer to the translated code */
/* next matching tb for physical address. */
struct TranslationBlock *phys_hash_next;
/* first and second physical page containing code. The lower bit
of the pointer tells the index in page_next[] */
struct TranslationBlock *page_next[2];
target_ulong page_addr[2];
/* the following data are used to directly call another TB from
the code of this one. */
uint16_t tb_next_offset[2]; /* offset of original jump target */
#ifdef USE_DIRECT_JUMP
uint16_t tb_jmp_offset[4]; /* offset of jump instruction */
#else
unsigned long tb_next[2]; /* address of jump generated code */
#endif
/* list of TBs jumping to this one. This is a circular list using
the two least significant bits of the pointers to tell what is
the next pointer: 0 = jmp_next[0], 1 = jmp_next[1], 2 =
jmp_first */
struct TranslationBlock *jmp_next[2];
struct TranslationBlock *jmp_first;
#ifdef CONFIG_TRACE
struct BBRec *bb_rec;
uint64_t prev_time;
#endif
#ifdef CONFIG_MEMCHECK
/* Maps PCs in this translation block to corresponding PCs in guest address
* space. The array is arranged in such way, that every even entry contains
* PC in the translation block, followed by an odd entry that contains
* guest PC corresponding to that PC in the translation block. This
* arrangement is set by tcg_gen_code_common that initializes this array
* when performing guest code translation. */
target_ulong* tpc2gpc;
/* Number of pairs (pc_tb, pc_guest) in tpc2gpc array. */
unsigned int tpc2gpc_pairs;
#endif // CONFIG_MEMCHECK
uint32_t icount;
};
/* the following data are used to directly call another TB from
the code of this one. */
指向调用的TB
设置的跳转目标
static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
{
#if QEMU_GNUC_PREREQ(4, 1)
void __clear_cache(char *beg, char *end);
#else
register unsigned long _beg __asm ("a1");
register unsigned long _end __asm ("a2");
register unsigned long _flg __asm ("a3");
#endif
/* we could use a ldr pc, [pc, #-4] kind of branch and avoid the flush */
*(uint32_t *)jmp_addr =
(*(uint32_t *)jmp_addr & ~0xffffff)
| (((addr - (jmp_addr + 8)) >> 2) & 0xffffff);
从某个地址跳转到某个地址
长跳转还是短跳转 ??
#if QEMU_GNUC_PREREQ(4, 1)
__clear_cache((char *) jmp_addr, (char *) jmp_addr + 4);
#else
/* flush icache */
_beg = jmp_addr;
_end = jmp_addr + 4;
_flg = 0;
__asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
#endif
}
从下面的一个ARM 汇编小程序要弄懂的以下三个问题:1).在ARM状态转到THUNB状态和BX的应用2).汇编的架构3)
SWI指令的使用
AREA ADDREG,CODE,READONLY ENTRYMAIN ADR r0,ThunbProg + 1 ;(为什么要加1呢?因为BX指令跳转到指定的地址执行程序 时,若(BX{cond} Rm)Rm的位[0]为1,则跳转时自动将CPSR中的标志T置位即把目标 代码解释为Thunb代码) BX r0 CODE16ThunbProg mov r2,#2 mov r3,#3 add r2,r2,r3
ADR r0,ARMProg;ADM伪指令ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。
例如:(查表)
ADR R0,DISP_TAB ; 加载转换表地址
LDRB R1,[R0,R2] ; 使用R2作为参数,进行查表
…
DISP_TAB
DCB 0xC0,0xF9,0xA4,0xB0,0x99, 0x92,0x82,0xF8 BX ro;转回ARM状态 CODE32ARMProg mov r4,#4 mov r5,#5 add r4,r4,r5 stop
LDR r1,=0x20026 SWI 0x123456 ENDSWI--软中断指令:SWI指令用于产生软中断,从用户模式变换到管理模式,CPSR保存到管理模式的SPSR中. SWI{cond} immed_24 ;immed_24为软中断号(服务类型)使用SWI指令时,通常使用以下两种方法进行传递参数,SWI 异常中断处理程序就可以提供相关的服务,这两种方法均是用户软件协定.SWI异常中断处理程序要通过读取引起软中断的SWI指令,以取得24位立即数.(1) 指令中的24位立即数指定了用户请求的服务类型,参数通过通用寄存器传递.(APCS) mov r0,#34 ;设置子功能号位34 SWI 12 ;调用12号软中断(2) 指令中的24位立即数被忽略,用户请求的服务类型有寄存器RO的值决定,参数通过其他的通用寄存器传递. mov r0,#12 ;调用12号软中断 mov r1,#34 ;设置子功能号位34 SWI 0在SWI异常中断处理程序中,取出SWI立即数的步骤为:首先确定引起软中断的SWI指令是ARM指令还是Thunb指令,这可通过对SPSR访问得到;然后取得该SWI指令的地址,这可通过访问LR寄存器得到;接着读出指令,分解出立即数.如如下程序:T_bit EQU 0X20SWI_Handler:0x00000008(SWI中断的入口地址应该有一个BL指令对应这一块) STMFD SP!,{R0-R3,R12,LR};现场保护
MOV r1,sp;// 若SWI调用带参,将R1指向第一个参数 MRS R0,SPSR ;读取SPSR STMFD SP!,{R0};保存SPSR TST R0,#T_bit LDRNEH R0,[LR,#-2] ;若是Thunb指令,读取指令码(16位) BICNE R0,#0XFF00;取得Thunb指令的8位立即数 LDREQ R0,[LR,#-4];若是ARM指令,读取指令码(32位) BICEQ R0,#0XFF000000 ;取得ARM指令的24位立即数
;// r0 now contains SWI number
;// r1 now contains pointer to stacked registers BL SWI_Exception_Function ;// 调用C编写的SWI处理函数(这个应该是下面的红色的部分)
LDMFD sp!, {r0}
MSR spsr_cf,r0 ;// spsr出栈
LDMFD sp!, {r0-r3, r12, pc}^ ;// 恢复现场 软件中断SWI的实现在需要软件中断处调用__SWI 0xNum ;Num为SWI中断处理模块的编号,见表SwiFunction;软件中断
SWI_Exception_Function
CMP R0, #12 ;R0中的SWI编号是否大于最大值
/* 下面这句语句把 (LDRLO这条指令的地址+8+ R0*4) 的地址装载到PC寄存器,举例如果上面的 Num="1",也就是R0 = 1, 假设LDRLO这条指令的地址是0x00008000,那么根据ARM体系的3级流水线 PC寄存器里指向是下两条指令 于是PC = 0x00008008 也就是伪指令DCD TASK_SW 声明的标号TASK_SW 的地址,注意DCD TASK_SW 这条指令本身不是ARM能执行的指令,也不会占有地址,这条指令靠汇编器汇编成可执行代码,它的意义就是声明 TASK_SW的地址, , [PC, R0, LSL #2] 这个寻址方式就是 PC + R0的值左移2位的值( 0x01<<2 => 0x04 ),这样PC的值就是0x0000800C, 即ENTER_CRITICAL的地址于是ARM执行该标号下的任务 */
LDRLO PC, [PC, R0, LSL #2]
MOVS PC, LR;返回SwiFunction
DCD TASK_SW ;0
DCD ENTER_CRITICAL ;1
DCD EXIT_CRITICAL ;2
DCD ISRBegin ;3
DCD ChangeToSYSMode ;4
DCD ChangeToUSRMode ;5
DCD __OSStartHighRdy ;6
DCD TaskIsARM ;7
DCD TaskIsTHUMB ;8
DCD OSISRNeedSwap ;9
DCD GetOSFunctionAddr ;10
DCD GetUsrFunctionAddr ;11TASK_SW
MRS R3, SPSR ;保存任务的CPSR
MOV R2, LR ;保存任务的PC
MSR CPSR_c, #(NoInt | SYS32Mode) ;切换到系统模式
STMFD SP!, {R2} ;保存PC到堆栈
STMFD SP!, {R0-R12, LR} ;保存R0-R12,LR到堆栈
;因为R0~R3没有保存有用数据,所以可以这样做
B OSIntCtxSw_0 ;真正进行任务切换ENTER_CRITICAL
;OsEnterSum++
LDR R1, =OsEnterSum
LDRB R2, [R1]
ADD R2, R2, #1
STRB R2, [R1]
;关中断
MRS R0, SPSR
ORR R0, R0, #NoInt
MSR SPSR_c, R0
MOVS PC, LR
================================================================================
备忘如下:
1、触及SWI软中断,就不能不说ATPCS过程调用,将后续日志记录;
2、SWI异常一旦触发,内核硬件完成:
进入Supervisor模式;
拷贝CPSR至SPSR_svc
拷贝异常返回地址至LR_svc 将0x00000008装入PC 因此,当触发SWI软中断前内核处于Supervisor模式,SPSR_svc、LR_svc中的值将被破坏;
3、SWI指令编码中自带24bit数据作为软中断号(swi_num),因此可通过取SWI指令编码获取软中断号;LDR r0,[lr,#-4]就是这样;
4、SWI_Exception_Function函数一般采用C编码(也可汇编),采用C编码可直接套用switch根据swi_nun软中断号切换,SWI_Exception_Function函数的编制是灵活的,比如可以为带参或不带参函数;
5、一个SWI调用允许带1~4个字型参数和1~4个字型返回值,触发SWI调用时四个参数依次保存在R0~R3中,返回值也存于R0~R3内,这和ATPCS函数调用一致;
6、在C中声明一个典型的无参无返回值的SWI调用为:”__swi(0x00) void IRQEnable();
“这样随时都可以使用”IRQEnable();“触发一个软中断(中断号0),其允许IRQ中断的功能必须在 SWI_Exception_Function软中断处理函数中实现;
8、最后须要注意的:SP堆栈指针时为间接寻址,”MOV r1,sp“指令将R1也变成一个指针,其存放的内容为R0在RAM中的地址而非R0值;
9、还有一点:SWI调用带参和SWI_Exception_Function函数的带参,是两回事,SWI调用带参是指的是调用多少号软中断,SWI_Exception_Function函数是函数(软中断运行的参数)
tb_set_jmp_target1 目标地址处理后 就直接跳过去
直接跳转 0 1 2 3
static inline void tb_set_jmp_target(TranslationBlock *tb,
int n, unsigned long addr)
{
unsigned long offset;
offset = tb->tb_jmp_offset[n];
tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
offset = tb->tb_jmp_offset[n + 2];
if (offset != 0xffff)
tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
}
不知道做了什么事?