QEMU翻译块(TB)分析

原创 2012年03月28日 13:32:05

1 翻译缓存

qemu中翻译缓存是一块连续的内存单元,使用全局变量code_gen_buffer保存其首地址,缓存大小由全局变量code_gen_buffer_size指示;全局变量code_gen_ptr指示当前未使用的缓存地址。
static uint8_t *code_gen_buffer;
static unsigned long code_gen_buffer_size;
static uint8_t *code_gen_ptr;

这片内存可以采用静态分配方式,也可以采用动态分配方式,前者将code_gen_buffer指向静态分配的空间,后者将code_gen_buffer指向动态分配的空间。编译时由宏USE_STATIC_CODE_GEN_BUFFER控制选用那种方式。
#ifdef USE_STATIC_CODE_GEN_BUFFER
code_gen_buffer = static_code_gen_buffer;
code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
map_exec(code_gen_buffer, code_gen_buffer_size);
#else
...
code_gen_buffer = qemu_malloc(code_gen_buffer_size);
map_exec(code_gen_buffer, code_gen_buffer_size);
#endif

2 翻译块存储

翻译块用一块连续的存储空间存放,采用动态申请的方式,用全局变量tbs保存返回值,一次申请全部的空间,空间大小由全局变量code_gen_max_blocks决定。
static TranslationBlock *tbs;
static int code_gen_max_blocks;

code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE;
tbs = qemu_malloc(code_gen_max_blocks * sizeof(TranslationBlock));

3 翻译块的结构

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];
tb_page_addr_t 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[2]; /* 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;
uint32_t icount;
};

其中tc_ptr指向翻译块对应存放在翻译缓存中的翻译码;
jmp_first与jmp_next构成了循环链表,该链表既包含跳向当前TB的信息,也包含当前TB的跳转信息。这个循环链表是怎么构建的呢?设计者巧妙利用了jmp_next[n]与jmp_first的低两位,由于指针是4地址对齐,正常情况低两位为0,于是就用这两位指示那个是后继指针。
请看下面函数:
static inline void tb_add_jump(TranslationBlock *tb, int n,
TranslationBlock *tb_next)
{
/* NOTE: this test is only needed for thread safety */
if (!tb->jmp_next[n]) {
/* patch the native jump address */
tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr);

/* add in TB jmp circular list */
tb->jmp_next[n] = tb_next->jmp_first;
tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n));
}
}
下面举例说明,假设tb1,tb2,tb3是未建立任何跳转关系的TB,注意此时
tb1->jmp_first为(TranslationBlock*)((long)(tb1) | 2);
tb2->jmp_first为(TranslationBlock*)((long)(tb2) | 2);
tb3->jmp_first为(TranslationBlock*)((long)(tb3) | 2);
在n=0情况下,如果tb2,tb3均跳转到tb1,执行语句
(1) tb_add_jump(tb2, 0, tb1);
(2) tb_add_jump(tb3, 0, tb1);
(3) tb_add_jump(tb2, 1, tb1);
语句(1)执行后,
tb2->jmp_next[0] = tb1->jmp_first;//即 (TranslationBlock*)((long)(tb1) | 2);
tb1->jmp_first = (TranslationBlock*)((long)(tb2) | 0);
语句(2)执行后,
tb3->jmp_next[0] = tb1->jmp_first;//即(TranslationBlock*)((long)(tb2) | 0);
tb1->jmp_first = (TranslationBlock*)((long)(tb3) | 0);
语句(3)执行后,
tb2->jmp_next[1] = tb1->jmp_first;//即(TranslationBlock*)((long)(tb3) | 0);
tb1->jmp_first = (TranslationBlock*)((long)(tb2) | 1);
这时构建的循环链表节点是:
tb1, tb1->jmp_first, tb2->jmp_next[1], tb3->jmp_next[0], tb2->jmp_next[0]
即tb1-->tb2(1)-->tb3(0)-->tb2(0)-->tb1(2)

从上述例子不难看出,循环链表包含了两方面的信息,一是跳向TB块的TB信息,如跳向tb1的TB块有tb3和tb2;二是TB块沿着一条链路跳向了那个节点,如tb3着链路0跳向了tb1,tb2沿着链路0、1均能跳到tb1.

4 TB的分配与释放

分配与释放函数分别是tb_alloc, tb_free函数,它们并不真正进行内存的申请与释放,只是对code_gen_ptr与nb_tbs进行增减操作。

5 TB相互间链接信息的删除

当一个TB无效时,需要清除与它有关的链接信息,一是要清除TB的跳转信息,二是要清除跳向该TB的信息。
清除TB的跳转信息用下面语句,先后清除两条链路上的信息:
tb_jmp_remove(tb, 0);
tb_jmp_remove(tb, 1);
函数tb_jmp_remove声明如下:
static inline void tb_jmp_remove(TranslationBlock *tb, int n);
实现过程是从tb的jmp_next[n]开始遍历n链路循环链表,当找到待清除节点且n值匹配时将其从链表中移出。如上例中执行语句tb_jmp_remove(tb2, 0);后链表变为:
tb1-->tb2(1)-->tb3(0)-->tb1(2)
执行语句tb_jmp_remove(tb2, 1);后链表变为:
tb1-->tb3(0)-->tb1(2)

清除跳向该TB的信息则比较容易,沿着jmp_first链表逐个移除,知道遇到低两位bit值为2的指针,这是循环链表遍历完的标志。

相关文章推荐

qemu 学习(一)————qemu整体流程解读

本文由博主原创,转载请注明出处(保留此处和链接): IT人生 (http://blog.csdn.net/ningxialieri/article/details/8876599)       学习q...

qemu 二进制翻译 执行流程分析

一.qemu简介          qemu是使用动态二进制翻译的cpu模拟器,它支持两种运行模式:全系统模拟和用户态模拟。在全系统模拟下,qemu可以模拟处理器和各种外设,可以运行操作系统。用...
  • edonlii
  • edonlii
  • 2014年04月26日 15:25
  • 2173

Qemu中间代码微指令类型总结

Qemu采用TCG(Tiny code generator)翻译引擎,TCG的作用也和一个真正的编译器后端一样,主要负责分析、优化Target代码以及生成Host代码。所谓“微指令”,是qemu用于指...

qemu源码分析之五-- TCG动态翻译技术

1. TCG简单介绍 TCG(Tiny Code Generator)最早被用于C编译器的后端。在TCG相关的代码中,target指的是我们通常说的host,这一点需要注意,并不是我们理解的被仿真的...

qemu源码分析之四--dyngen动态翻译技术

由于刚刚接触qemu,所以前面几篇文章仅仅是肤浅的介绍qemu的一些背景知识,今天突然感觉前面说的太没有条理了,而且大部分是读别人的文章,一知半解,没有自己的总结体会,今天感觉稍微有点心得,敬请指教。...

【翻译自mos文章】使用asm来部署 超大数据库(10TB到PB 范围)--针对oracle 10G

使用asm来部署 超大数据库(10TB到PB 范围)--针对oracle 10G

一些算法的MapReduce实现——1 TB数据排序源码分析

1、概述 1TB排序通常用于衡量分布式数据处理框架的数据处理能力。Terasort是Hadoop中的的一个排序作业,在2008年,Hadoop在1TB排序基准评估中赢得第一名,耗时209秒。那么...

QEMU源代码,二进制翻译工具

  • 2008年10月24日 22:12
  • 1.81MB
  • 下载

qemu技术分析

  • 2014年01月08日 22:43
  • 42KB
  • 下载

qemu a fast and portable dynamic translator——大致翻译

QEMU是一套由Fabrice Bellard所编写的以GPL许可证分发源码的machine emulator。可以在不同的主机(X86,PowerPC,ARM,Sparc)上对不同的CPU(比如x8...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:QEMU翻译块(TB)分析
举报原因:
原因补充:

(最多只允许输入30个字)