8/30/2011 4:09:14 PM

 

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);
}

 

不知道做了什么事?


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值