<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>vm-kernel &#187; qemu</title>
	<atom:link href="http://vm-kernel.org/blog/category/qemu/feed/" rel="self" type="application/rss+xml" />
	<link>http://vm-kernel.org/blog</link>
	<description>All about emulation and virtualization</description>
	<lastBuildDate>Sat, 17 Apr 2010 03:51:46 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>qemu internal part 3: memory watchpoint</title>
		<link>http://vm-kernel.org/blog/2009/07/15/qemu-internal-part-3-memory-watchpoint/</link>
		<comments>http://vm-kernel.org/blog/2009/07/15/qemu-internal-part-3-memory-watchpoint/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 09:31:00 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[emulation]]></category>
		<category><![CDATA[qemu]]></category>
		<category><![CDATA[watchpoint]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/07/15/qemu-internal-part-3-memory-watchpoint/</guid>
		<description><![CDATA[In qemu there is an amazing feature – memory watchpoint. It can watch all the memory access including memory read, write or both of them. When guest os/application touches the memory region watched by qemu, a registered function will be called and you can do everything as you want in this function. The gdb stub [...]]]></description>
			<content:encoded><![CDATA[<p align="justify">In qemu there is an amazing feature – memory watchpoint. It can watch all the memory access including memory read, write or both of them. When guest os/application touches the memory region watched by qemu, a registered function will be called and you can do everything as you want in this function. The gdb stub in qemu uses it to implement the memory watch command.</p>
<p align="justify">The implemention of memory watchpoint is tricky in qemu. <a href="http://vm-kernel.org/blog/2009/07/10/qemu-internal-part-2-softmmu/">In last article of qemu internal</a>, we know that when emulating memory access, qemu needs to distinguish the normal RAM read/write from memory mapped I/O read/write. If it is a memory mapped I/O address access, qemu will dispatch this access to the registered I/O emulation functions. Qemu use this mechanism to implement the memory watchpoint. When accessing the memory address watched by qemu, qemu will dispatch this access to the registered memory watch functions, even if this address is normal guest RAM address or memory mapped I/O address! Qemu will do all the magic things in these memory watch functions.</p>
<p align="justify">In the following, I will use an example to explain the whole process of memory watch implement of qemu.</p>
<blockquote><p>80103c60 &lt;memcpy&gt;:     <br />80103c60:&#160;&#160;&#160;&#160;&#160;&#160; 00801021&#160;&#160;&#160;&#160;&#160;&#160;&#160; move&#160;&#160;&#160; v0,a0      <br />80103c64 &lt;__copy_user&gt;:      <br />80103c64:&#160;&#160;&#160;&#160;&#160;&#160; 2cca0004&#160;&#160;&#160;&#160;&#160;&#160;&#160; sltiu&#160;&#160; t2,a2,4      <br />80103c68:&#160;&#160;&#160;&#160;&#160;&#160; 30890003&#160;&#160;&#160;&#160;&#160;&#160;&#160; andi&#160;&#160;&#160; t1,a0,0x3      <br />80103c6c:&#160;&#160;&#160;&#160;&#160;&#160; 15400068&#160;&#160;&#160;&#160;&#160;&#160;&#160; bnez&#160;&#160;&#160; t2,80103e10 &lt;__copy_user+0x1ac&gt;      <br />80103c70:&#160;&#160;&#160;&#160;&#160;&#160; 30a80003&#160;&#160;&#160;&#160;&#160;&#160;&#160; andi&#160;&#160;&#160; t0,a1,0x3      <br />80103c74:&#160;&#160;&#160;&#160;&#160;&#160; 1520003d&#160;&#160;&#160;&#160;&#160;&#160;&#160; bnez&#160;&#160;&#160; t1,80103d6c &lt;__copy_user+0x108&gt;      <br />80103c78:&#160;&#160;&#160;&#160;&#160;&#160; 00000000&#160;&#160;&#160;&#160;&#160;&#160;&#160; nop      <br />80103c7c:&#160;&#160;&#160;&#160;&#160;&#160; 15000046&#160;&#160;&#160;&#160;&#160;&#160;&#160; bnez&#160;&#160;&#160; t0,80103d98 &lt;__copy_user+0x134&gt;      <br />80103c80:&#160;&#160;&#160;&#160;&#160;&#160; 00064142&#160;&#160;&#160;&#160;&#160;&#160;&#160; srl&#160;&#160;&#160;&#160; t0,a2,0x5      <br />80103c84:&#160;&#160;&#160;&#160;&#160;&#160; 11000017&#160;&#160;&#160;&#160;&#160;&#160;&#160; beqz&#160;&#160;&#160; t0,80103ce4 &lt;__copy_user+0x80&gt;      <br />80103c88:&#160;&#160;&#160;&#160;&#160;&#160; 30d8001f&#160;&#160;&#160;&#160;&#160;&#160;&#160; andi&#160;&#160;&#160; t8,a2,0x1f      <br />80103c8c:&#160;&#160;&#160;&#160;&#160;&#160; 00000000&#160;&#160;&#160;&#160;&#160;&#160;&#160; nop      <br />80103c90:&#160;&#160;&#160;&#160;&#160;&#160; 8ca80000&#160;&#160;&#160;&#160;&#160;&#160;&#160; lw&#160;&#160;&#160;&#160;&#160; t0,0(a1)</p>
</blockquote>
<p align="justify">These asm lines are objdumped from linux 2.6.30 kernel for mips malta. Assume that I want to&#160; watch the memory access of virtual address 0x804cd000(<em>swapper_pg_dir</em> in linux kernel).</p>
<p align="justify">First I insert the watchpoint into cpu. </p>
<blockquote><p>cpu_watchpoint_insert(env, 0x804cd000, 4, BP_GDB | BP_MEM_ACCESS,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; NULL);</p>
</blockquote>
<p>And then I need to register the vm state changing call back functions.</p>
<blockquote><p>qemu_add_vm_change_state_handler(spy_vm_state_change, NULL);</p>
</blockquote>
<p align="justify">If register a1=0x804cd000, guest linux kernel will touch the watched memory region when pc is 0x80103c90, then qemu dispatches this access to the registered memory watch function, even if this access is a noram guest RAM access.The memory watch functions in qemu are in array <em>watch_mem_read/watch_mem_write</em>.</p>
<blockquote><p>exec.c</p>
<p>2649 static CPUReadMemoryFunc *watch_mem_read[3] = {     <br />2650&#160;&#160;&#160;&#160; watch_mem_readb,      <br />2651&#160;&#160;&#160;&#160; watch_mem_readw,      <br />2652&#160;&#160;&#160;&#160; watch_mem_readl,      <br />2653 };      <br />2654       <br />2655 static CPUWriteMemoryFunc *watch_mem_write[3] = {      <br />2656&#160;&#160;&#160;&#160; watch_mem_writeb,      <br />2657&#160;&#160;&#160;&#160; watch_mem_writew,      <br />2658&#160;&#160;&#160;&#160; watch_mem_writel,      <br />2659 };</p>
</blockquote>
<p>In function <em>watch_mem_readl</em>, it will call function <em>check_watchpoint</em> first.</p>
<blockquote><p>exec.c</p>
<p>2622 static uint32_t watch_mem_readl(void *opaque, target_phys_addr_t addr)     <br />2623 {      <br />2624&#160;&#160;&#160;&#160; check_watchpoint(addr &amp; ~TARGET_PAGE_MASK, ~0x3, BP_MEM_READ);      <br />2625&#160;&#160;&#160;&#160; return ldl_phys(addr);      <br />2626 }</p>
</blockquote>
<blockquote><p>2563 static void check_watchpoint(int offset, int len_mask, int flags)     <br />2564 {      <br />2565&#160;&#160;&#160;&#160; CPUState *env = cpu_single_env;      <br />2566&#160;&#160;&#160;&#160; target_ulong pc, cs_base;      <br />2567&#160;&#160;&#160;&#160; TranslationBlock *tb;      <br />2568&#160;&#160;&#160;&#160; target_ulong vaddr;      <br />2569&#160;&#160;&#160;&#160; CPUWatchpoint *wp;      <br />2570&#160;&#160;&#160;&#160; int cpu_flags;      <br />2571       <br />2572&#160;&#160;&#160;&#160; if (env-&gt;watchpoint_hit) {      <br />2573&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; /* We re-entered the check after replacing the TB. Now raise      <br />2574&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; * the debug interrupt so that is will trigger after the      <br />2575&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; * current instruction. */      <br />2576&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; cpu_interrupt(env, CPU_INTERRUPT_DEBUG);      <br />2577&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return;      <br />2578&#160;&#160;&#160;&#160; }      <br />2579&#160;&#160;&#160;&#160; vaddr = (env-&gt;mem_io_vaddr &amp; TARGET_PAGE_MASK) + offset;      <br />2580&#160;&#160;&#160;&#160; TAILQ_FOREACH(wp, &amp;env-&gt;watchpoints, entry) {      <br />2581&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ((vaddr == (wp-&gt;vaddr &amp; len_mask) ||      <br />2582&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (vaddr &amp; wp-&gt;len_mask) == wp-&gt;vaddr) &amp;&amp; (wp-&gt;flags &amp; flags)) {      <br />2583&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; wp-&gt;flags |= BP_WATCHPOINT_HIT;      <br />2584&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!env-&gt;watchpoint_hit) {      <br />2585&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; env-&gt;watchpoint_hit = wp;      <br />2586&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; tb = tb_find_pc(env-&gt;mem_io_pc);      <br />2587&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!tb) {      <br />2588&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; cpu_abort(env, &quot;check_watchpoint: could not find TB for &quot;      <br />2589&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;pc=%p&quot;, (void *)env-&gt;mem_io_pc);      <br />2590&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />2591&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; cpu_restore_state(tb, env, env-&gt;mem_io_pc, NULL);      <br />2592&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; tb_phys_invalidate(tb, -1);      <br />2593&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (wp-&gt;flags &amp; BP_STOP_BEFORE_ACCESS) {      <br />2594&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; env-&gt;exception_index = EXCP_DEBUG;      <br />2595&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } else {      <br />2596&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; cpu_get_tb_cpu_state(env, &amp;pc, &amp;cs_base, &amp;cpu_flags);      <br />2597&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; tb_gen_code(env, pc, cs_base, cpu_flags, 1);      <br />2598&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />2599&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; cpu_resume_from_signal(env, NULL);      <br />2600&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />2601&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } else {      <br />2602&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; wp-&gt;flags &amp;= ~BP_WATCHPOINT_HIT;      <br />2603&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />2604&#160;&#160;&#160;&#160; }      <br />2605 }</p>
</blockquote>
<p align="justify">When <em>check_watchpoint</em> is executed in the first time, <em>env-&gt;watchpoint_hit</em> is null. Then it will check whether the address is a watched address. If so, set the flag BP_WATCHPOINT_HIT in <em>wp-&gt;flags</em>(line 2583) and set <em>env-&gt;watchpoint_hit</em> to <em>wp</em>. Then it will find and invalidate the current translation block(line 2586-2592). If the flag BP_STOP_BEFORE_ACCESS in <em>wp</em> is not set, then qemu will translate the code from current pc(line 2596-2597) and resume the guest instruction emulation(line 2599). Function <em>cpu_resume_from_signal</em> will jump to line 256 in cpu-exec.c and rerun the emulation process from the lw instruction(pc=0x80103c90). </p>
<blockquote><p align="justify">cpu-exec.c</p>
<p>255&#160;&#160;&#160;&#160; for(;;) {     <br />256&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (setjmp(env-&gt;jmp_env) == 0) {      <br />257&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; env-&gt;current_tb = NULL;      <br />258&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; /* if an exception is pending, we execute it here */      <br />259&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (env-&gt;exception_index &gt;= 0) {      <br />260&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (env-&gt;exception_index &gt;= EXCP_INTERRUPT) {      <br />261&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; /* exit request from the cpu execution loop */      <br />262&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ret = env-&gt;exception_index;      <br />263&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (ret == EXCP_DEBUG)      <br />264&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; cpu_handle_debug_exception(env);      <br />265&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;      <br />266&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } else {</p>
</blockquote>
<p align="justify">Why do qemu need to invalidate current translation block and regenerate the code? Because this memory access(pc=0x80103c90) is in the middle of a translation block. If we want to rerun this instruction, we need to regenerate the code from this instruction(pc=0x80103c90). Moreover before invalidating the translation block, qemu needs to sync the cpu state to guest cpu(<em>cpu_restore_state</em>). That’s because the cpu state in the middle of translation block is different from the actual cpu state. Understanding this process needs some knowledge of binary translation. If you find it is hard to understand, just ignore it.</p>
<p align="justify">Now qemu rerun the guest os from pc=0x80103c90. Because the memory address is a watched memory address, qemu will call <em>watch_mem_readl-&gt;check_watchpoint</em> again. But this time, <em>env-&gt;watchpoint_hit</em> is not null(qemu set it in last call), then it will call <em>cpu_interrupt</em> and return from function <em>check_watchpoint</em>. Then in <em>watch_mem_readl</em> it will call <em>ldl_phys</em> to fetch the value from guest RAM. Function <em>cpu_interrupt</em> in <em>check_watchpoint</em>&#160; sets the <em>CPU_INTERRUPT_DEBUG</em> to flag to <em>env-&gt;interrupt_request</em>.&#160; </p>
<p align="justify">Then qemu runs normally just like nothing has happened. Because the <em>CPU_INTERRUPT_DEBUG</em> has been set in <em>env-&gt;interrupt_request</em>, the main loop of cpu emulation will return.</p>
<blockquote><p>cpu-exec.c</p>
<p>355&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (interrupt_request &amp; CPU_INTERRUPT_DEBUG) {     <br />356&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; env-&gt;interrupt_request &amp;= ~CPU_INTERRUPT_DEBUG;      <br />357&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; env-&gt;exception_index = EXCP_DEBUG;      <br />358&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; cpu_loop_exit();      <br />359&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
<p> 54 void cpu_loop_exit(void)     <br /> 55 {      <br /> 56&#160;&#160;&#160;&#160; /* NOTE: the register at this point must be saved by hand because      <br /> 57&#160;&#160;&#160;&#160;&#160;&#160;&#160; longjmp restore them */      <br /> 58&#160;&#160;&#160;&#160; regs_to_env();      <br /> 59&#160;&#160;&#160;&#160; longjmp(env-&gt;jmp_env, 1);      <br /> 60 }</p>
</blockquote>
<p align="justify">Function <em>cpu_loop_exit</em> will do longjmp to line 256 in cpu-exec.c. Because <em>env-&gt;exception_index</em> is <em>EXCP_DEBUG</em>, it will break from the loop of function <em>cpu_exec</em>. Function <em>cpu_exec</em> returns to <em>main_loop</em> in vl.c.</p>
<blockquote><p>vl.c</p>
<p>3800&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ret = cpu_exec(env);</p>
<p>3850&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (unlikely(ret == EXCP_DEBUG)) {     <br />3851&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; gdb_set_stop_cpu(cur_cpu);      <br />3852&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; vm_stop(EXCP_DEBUG);      <br />3853&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
</blockquote>
<p align="justify">It will call <em>gdb_set_stop_cpu</em> and then <em>vm_stop</em> to stop the qemu. It the virtual state is changed, qemu will the call the callback functions registered by <em>qemu_add_vm_change_state_handler</em>. So the function <em>spy_vm_state_change</em> will be called.</p>
<p align="justify">In sum, when accessing the watched memory address, the memory watch functions will be called. It will call function<em> check_watchpoint</em>. Function <em>check_watchpoint</em> will set <em>env-&gt;watchpoint_hit</em> to current watchpoint and rerun the guest os/applicaton from current pc. Then memory watched functions will be called again. It will call <em>function check_watchpoint</em>. This time, function <em>check_watchpoint</em> just set the flag in <em>env-&gt;interrupt_request</em> and tells cpu to interrupt the emulation process. And then qemu will return to the <em>main_loop</em> and stop the vm. At last it will call the registered vm change state callback functions. </p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/07/15/qemu-internal-part-3-memory-watchpoint/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>qemu internal part 2: softmmu</title>
		<link>http://vm-kernel.org/blog/2009/07/10/qemu-internal-part-2-softmmu/</link>
		<comments>http://vm-kernel.org/blog/2009/07/10/qemu-internal-part-2-softmmu/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 03:14:00 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[emulation]]></category>
		<category><![CDATA[qemu]]></category>
		<category><![CDATA[softmmu]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/07/10/qemu-internal-part-2-softmmu/</guid>
		<description><![CDATA[Qemu uses softmmu to accelerate the process of finding the mapping between guest physical address and host virtual address and the mapping between guest I/O region and qemu I/O emulation functions. In this article, I assume the guest page table size is 4K.
1. the two level guest physical page descriptor table
Qemu uses a two level [...]]]></description>
			<content:encoded><![CDATA[<p>Qemu uses softmmu to accelerate the process of finding the mapping between guest physical address and host virtual address and the mapping between guest I/O region and qemu I/O emulation functions. In this article, I assume the guest page table size is 4K.</p>
<p>1. the two level guest physical page descriptor table</p>
<p>Qemu uses a two level guest physical page descriptor table to maintain the guest memory space and MMIO space. The table is pointed by <em>l1_phys_map</em>. Bits [31:22] is used to index first level entry and bits [21:12] is used to index the second level entry. The entry of the second level table is <em>PhysPageDesc</em>.</p>
<blockquote><p>exec.c</p>
<p>146 typedef struct PhysPageDesc {      <br />147&#160;&#160;&#160;&#160; /* offset in host memory of the page + io_index in the low bits */       <br />148&#160;&#160;&#160;&#160; ram_addr_t phys_offset;       <br />149&#160;&#160;&#160;&#160; ram_addr_t region_offset;       <br />150 } PhysPageDesc;</p>
</blockquote>
<p>If the memory region is RAM, then the bits [31:12] of <em>phys_offset</em> means the offset of this page in emulated physical memory. If the memory region is memory mapped I/O, then the bits of [11:3] of <em>phys_offset</em> means the index in <em>io_mem_write/io_mem_read</em> array. When accessing this memory region, the functions in <em>io_mem_write/io_mem_read</em> of index phys_offset will be called.</p>
<p>2. register the guest physical memory</p>
<p>Function <em>cpu_register_physical_memory</em> is used to register a guest memory region. If <em>phys_offset</em> is IO_MEM_RAM then it means this region is guest RAM space. If the <em>phys_offset</em> &gt;IO_MEM_ROM, then it means this memory region is MMIO space.</p>
<blockquote><p>898 static inline void cpu_register_physical_memory(target_phys_addr_t start_addr,      <br />899&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ram_addr_t size,       <br />900&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ram_addr_t phys_offset)       <br />901 {       <br />902&#160;&#160;&#160;&#160; cpu_register_physical_memory_offset(start_addr, size, phys_offset, 0);       <br />903 }</p>
</blockquote>
<p>Function <em>cpu_register_physical_memory_offset</em> will first find the PhysPageDesc in table <em>l1_phys_map</em> using the given guest physical address<em>.</em> If finding the entry, qemu will update the entry. If not finding the entry, then qemu creates a new entry and updates its value and insert this entry to the table at last.</p>
<p>In malta emulation, the following is the code to register malta RAM space.</p>
<blockquote><p>hw/mips_malta.c</p>
<p>811&#160;&#160;&#160;&#160; cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);</p>
</blockquote>
<p>3. register the mmio space</p>
<p>Before registering mmio space using <em>cpu_register_physical_memory,</em> qemu uses the function <em>cpu_register_io_memory</em> to register the I/O emulation functions to array <em>io_mem_write/io_mem_read.</em> </p>
<blockquote><p>exec.c</p>
<p>2851 int cpu_register_io_memory(int io_index,      <br />2852&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; CPUReadMemoryFunc **mem_read,       <br />2853&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; CPUWriteMemoryFunc **mem_write,       <br />2854&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; void *opaque)</p>
</blockquote>
<p>This function will return the index in array io_mem_write/io_mem_read and this index will be passed to function <em>cpu_register_physical_memory</em> via parameter <em>phys_offset</em>.</p>
<blockquote><p>hw/mips_malta.c</p>
<p>malta = cpu_register_io_memory(0, malta_fpga_read,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; malta_fpga_write, s); </p>
<p>cpu_register_physical_memory(base, 0x900, malta);</p>
</blockquote>
<p>4. softmmu</p>
<p>Given the guest virtual address, how does qemu find the corresponding host virtual address? First qemu needs to translate the guest virtual address to guest physical address. Then qemu needs to find the PhysPageDesc entry in table <em>l1_phys_map</em> and get the <em>phys_offset</em>. At last qemu should add <em>phys_offset</em> to <em>phys_ram_base</em> to get the host virtual address.</p>
<p>Qemu uses a softmmu model to speed up this process. Its main idea is storing the offset of guest virtual address to host virtual address in a TLB table. When translating the guest virtual address to host virtual address, it will search this TLB table firstly. If there is an entry in the table, then qemu can add this offset to guest virtual address to get the host virtual address directly. Otherwise, it needs to search the l1_phys_map table and then fill the corresponding entry to the TLB table. The index of this TLB table is bits [19:12] of guest virtual address and there is no asid field in tlb entry. This means the TLB table needs to be flushed in process switch!</p>
<p>This TLB table idea is just like the most traditional hardware TLB. However, to MIPS cpu, there is another mmu model in qemu. Unlike x86 cpu, MIPS does <strong>NOT</strong> care about hardware page table. Instead it uses hardware TLB which is <strong>NOT</strong> transparent to software. Maybe It is another topic I will explain in another article. What we need to understand here is that the softmmu model in this article is not the mmu model of MIPS cpu itself.</p>
<p>Moreover, besides helping speed up the process of translating guest virtual address to host virtual address, this softmmu model can speed up the process of dispatching I/O emulation functions according to guest virtual address too. In this case, the idex of I/O emulation functions in <em>io_mem_write/io_mem_read</em> is stored in iotlb.</p>
<p>The format of TLB entry is as flowing:</p>
<blockquote><p>cpu-defs.h</p>
<p>176&#160;&#160;&#160;&#160; CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE];&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \      <br />177&#160;&#160;&#160;&#160; target_phys_addr_t iotlb[NB_MMU_MODES][CPU_TLB_SIZE];&#160;&#160; </p>
</blockquote>
<blockquote><p>108 typedef struct CPUTLBEntry {      <br />109&#160;&#160;&#160;&#160; /* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address       <br />110&#160;&#160;&#160;&#160;&#160;&#160;&#160; bit TARGET_PAGE_BITS-1..4&#160; : Nonzero for accesses that should not       <br />111&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; go directly to ram.       <br />112&#160;&#160;&#160;&#160;&#160;&#160;&#160; bit 3&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; : indicates that the entry is invalid       <br />113&#160;&#160;&#160;&#160;&#160;&#160;&#160; bit 2..0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; : zero       <br />114&#160;&#160;&#160;&#160; */       <br />115&#160;&#160;&#160;&#160; target_ulong addr_read;       <br />116&#160;&#160;&#160;&#160; target_ulong addr_write;       <br />117&#160;&#160;&#160;&#160; target_ulong addr_code;       <br />124&#160;&#160;&#160;&#160; target_phys_addr_t addend;       <br />131 } CPUTLBEntry;</p>
</blockquote>
<p>Field <em>addr_read/write/code</em> stores the guest virtual address for TLB entry. It is the tag of this entry. Filed a<em>ddend</em> is the offset of host virtual address to guest virtual address. We can add this value to guest virtual address to get the host virtual address.</p>
<blockquote><p>addend = host_virtual_address – guest_virtual_address</p>
<p>host_virtual_address = <em><strong>phys_ram_base(qemu variable)</strong></em> + guest_physical_address – guest_physical_address_base(0 in MIPS) </p>
</blockquote>
<p>The <em>iotlb</em> stores the index of I/O emulation function in <em>io_mem_write/io_mem_read.</em></p>
<p>Function __ldb_mmu/__ldl_mmu/__ldw_mmu is used to translating the guest virtual address to host virtual address or dispatching guest virtual address to I/O emulation functions.</p>
<blockquote><p>softmmu_template.h</p>
<p> 86 DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,     <br /> 87&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; int mmu_idx)      <br /> 88 {      <br /> 89&#160;&#160;&#160;&#160; DATA_TYPE res;      <br /> 90&#160;&#160;&#160;&#160; int index;      <br /> 91&#160;&#160;&#160;&#160; target_ulong tlb_addr;      <br /> 92&#160;&#160;&#160;&#160; target_phys_addr_t addend;      <br /> 93&#160;&#160;&#160;&#160; void *retaddr;      <br /> 94       <br /> 95&#160;&#160;&#160;&#160; /* test if there is match for unaligned or IO access */      <br /> 96&#160;&#160;&#160;&#160; /* XXX: could done more in memory macro in a non portable way */      <br /> 97&#160;&#160;&#160;&#160; index = (addr &gt;&gt; TARGET_PAGE_BITS) &amp; (CPU_TLB_SIZE - 1);      <br /> 98&#160; redo:      <br /> 99&#160;&#160;&#160;&#160; tlb_addr = env-&gt;tlb_table[mmu_idx][index].ADDR_READ;      <br />100&#160;&#160;&#160;&#160; if ((addr &amp; TARGET_PAGE_MASK) == (tlb_addr &amp; (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {      <br />101&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (tlb_addr &amp; ~TARGET_PAGE_MASK) {      <br />102&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; /* IO access */      <br />103&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ((addr &amp; (DATA_SIZE - 1)) != 0)      <br />104&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; goto do_unaligned_access;      <br />105&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; retaddr = GETPC();      <br />106&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; addend = env-&gt;iotlb[mmu_idx][index];      <br />107&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; res = glue(io_read, SUFFIX)(addend, addr, retaddr);      <br />108&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } else if (((addr &amp; ~TARGET_PAGE_MASK) + DATA_SIZE - 1) &gt;= TARGET_PAGE_SIZE) {      <br />109&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; /* slow unaligned access (it spans two pages or IO) */      <br />110&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; do_unaligned_access:      <br />111&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; retaddr = GETPC();      <br />112 #ifdef ALIGNED_ONLY      <br />113&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);      <br />114 #endif      <br />115&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr,      <br />116&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mmu_idx, retaddr);      <br />117&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } else {      <br />118&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; /* unaligned/aligned access in the same page */      <br />119 #ifdef ALIGNED_ONLY      <br />120&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ((addr &amp; (DATA_SIZE - 1)) != 0) {      <br />121&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; retaddr = GETPC();      <br />122&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);      <br />123&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />124 #endif      <br />125&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; addend = env-&gt;tlb_table[mmu_idx][index].addend;      <br />126&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend));      <br />127&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />128&#160;&#160;&#160;&#160; } else {      <br />129&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; /* the page is not in the TLB : fill it */      <br />130&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; retaddr = GETPC();      <br />131 #ifdef ALIGNED_ONLY      <br />132&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ((addr &amp; (DATA_SIZE - 1)) != 0)      <br />133&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);      <br />134 #endif      <br />135&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);      <br />136&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; goto redo;      <br />137&#160;&#160;&#160;&#160; }      <br />138&#160;&#160;&#160;&#160; return res;      <br />139 }      </p>
</blockquote>
<p>In this function, it will get the index of TLB table and compare the guest virtual address with the address stored in this tlb entry(line 97-100). If these two addresses match, it means this guest virtual address hits the tlb entry. Then qemu will determine this virtual address is a MMIO address or RAM address. If it is a MMIO address, get the index of IO emulation functions from <em>env-&gt;iotlb </em>and call these functions(line 103-117). If it is a RAM space, add the guest virtual address to addend to get the host virtual address(line 118-128). If there is no matched tlb entry, then fietch the entry from table <em>l1_phys_map </em>and insert the entry to tlb table(line 135).</p>
<p>5. an example</p>
<p>When fetching code from guest memory, the whole code path is as flowing:</p>
<blockquote><p>cpu_exec-&gt;tb_find_fast-&gt;tb_find_slow-&gt;get_phys_addr_code-&gt;(if tlb not match)ldub_code(softmmu_header.h)-&gt;__ldl_mmu(softmmu_template.h)-&gt;tlb_fill-&gt;cpu_mips_handle_mmu_fault-&gt;tlb_set_page-&gt;tlb_set_page_exec</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/07/10/qemu-internal-part-2-softmmu/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>qemu internal part 1: the code path of memory load emulation</title>
		<link>http://vm-kernel.org/blog/2009/07/08/qemu-internal-part-1-the-code-path-of-memory-load-emulation/</link>
		<comments>http://vm-kernel.org/blog/2009/07/08/qemu-internal-part-1-the-code-path-of-memory-load-emulation/#comments</comments>
		<pubDate>Wed, 08 Jul 2009 06:58:00 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[emulation]]></category>
		<category><![CDATA[qemu]]></category>
		<category><![CDATA[tcg]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/07/08/qemu-internal-part-1-the-code-path-of-memory-load-emulation/</guid>
		<description><![CDATA[In qemu, there are two different meanings of target. The first meaning of ‘target’ means the emulated target machine architecture. For example, when emulating mips machine on x86, the target is mips and host is x86. However, in tcg(tiny code generator), target has a different meaning. It means the generated binary architecture. In the example [...]]]></description>
			<content:encoded><![CDATA[<p>In qemu, there are two different meanings of target. The first meaning of ‘target’ means the emulated target machine architecture. For example, when emulating mips machine on x86, the target is mips and host is x86. However, in tcg(tiny code generator), target has a different meaning. It means the generated binary architecture. In the example of emulating mips on x86, in tcg the target means x86 because tcg will generate x86 binary.</p>
<p>This article is based on qemu version 0.10.5 and target machine emulated is little endian mips. I will summarize the code path of mips lw instruction emulation in qemu.</p>
<p>Function <i>decode_opc</i> is used for decoding all the fetched instructions before tcg generating the target binary.</p>
<blockquote><p>target-mips/translate.c</p>
<p>7566 static void decode_opc (CPUState *env, DisasContext *ctx)</p>
<p>7960&#160;&#160;&#160;&#160; case OPC_LB ... OPC_LWR: /* Load and stores */      <br />7961&#160;&#160;&#160;&#160; case OPC_SB ... OPC_SW:       <br />7962&#160;&#160;&#160;&#160; case OPC_SWR:       <br />7963&#160;&#160;&#160;&#160; case OPC_LL:       <br />7964&#160;&#160;&#160;&#160; case OPC_SC:       <br />7965&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; gen_ldst(ctx, op, rt, rs, imm);       <br />7966&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
</blockquote>
<p>It will call function <em>gen_ldst</em> which is also in <em>target-mips/translate.c</em>.</p>
<blockquote style="margin-right: 0px" dir="ltr"><p>target-mips/translate.c</p>
<p>973 static void gen_ldst (DisasContext *ctx, uint32_t opc, int rt,      <br />974&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; int base, int16_t offset)</p>
<p>1046&#160;&#160;&#160;&#160; case OPC_LW:      <br />1047&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; op_ldst_lw(t0, ctx);       <br />1048&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; gen_store_gpr(t0, rt);       <br />1049&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; opn = &quot;lw&quot;;       <br />1050&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
</blockquote>
<p>Function <em>op_ldst_lw</em> will generate the target binary which fetches the value from the emulated guest memory and <em>gen_store_gpr</em> will store this value to the emulated cpu’s general register rt.</p>
<p>Function <em>op_ldst_lw </em>is generated by the macro <em>OP_LD</em>.</p>
<blockquote><p>target-mips/translate.c</p>
<p>901 #define OP_LD(insn,fname)&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \      <br />902 static inline void op_ldst_##insn(TCGv t0, DisasContext *ctx)&#160;&#160;&#160; \       <br />903 {&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \       <br />904&#160;&#160;&#160;&#160; tcg_gen_qemu_##fname(t0, t0, ctx-&gt;mem_idx);&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \       <br />905 }</p>
<p>910 OP_LD(lw,ld32s);</p>
</blockquote>
<p>We can find that <em>op_ldst_lw </em>is a function which calls function <em>tcg_gen_qemu_ld32s</em>. It will output the OPC(<em>INDEX_op_qemu_ld32u</em>) and args to <em>gen_opc_ptr</em>.</p>
<blockquote><p>tcg/tcg-op.h</p>
<p>1793 static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index)      <br />1794 {       <br />1795 #if TARGET_LONG_BITS == 32       <br />1796&#160;&#160;&#160;&#160; tcg_gen_op3i_i32(INDEX_op_qemu_ld32u, ret, addr, mem_index);       <br />1797 #else       <br />1798&#160;&#160;&#160;&#160; tcg_gen_op4i_i32(INDEX_op_qemu_ld32u, TCGV_LOW(ret), TCGV_LOW(addr),       <br />1799&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TCGV_HIGH(addr), mem_index);       <br />1800&#160;&#160;&#160;&#160; tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);       <br />1801 #endif       <br />1802 }</p>
<p>99 static inline void tcg_gen_op3i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2,      <br />100&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TCGArg arg3)       <br />101 {       <br />102&#160;&#160;&#160;&#160; *gen_opc_ptr++ = opc;       <br />103&#160;&#160;&#160;&#160; *gen_opparam_ptr++ = GET_TCGV_I32(arg1);       <br />104&#160;&#160;&#160;&#160; *gen_opparam_ptr++ = GET_TCGV_I32(arg2);       <br />105&#160;&#160;&#160;&#160; *gen_opparam_ptr++ = arg3;       <br />106 }</p>
</blockquote>
<p>The path of generation of target binary code of tcg is as following.</p>
<blockquote><p>cpu_gen_code-&gt;tcg_gen_code-&gt;tcg_gen_code_common-&gt;tcg_reg_alloc_op-&gt;tcg_out_op</p>
</blockquote>
<blockquote><p>tcg/i386/tcg-target.c</p>
<p>856 static inline void tcg_out_op(TCGContext *s, int opc,      <br />857&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; const TCGArg *args, const int *const_args)</p>
<p>1041&#160;&#160;&#160;&#160; case INDEX_op_qemu_ld32u:     <br />1042&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; tcg_out_qemu_ld(s, args, 2);      <br />1043&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;</p>
<p>431 static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,     <br />432&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; int opc)</p>
<p>508 #if TARGET_LONG_BITS == 32     <br />509&#160;&#160;&#160;&#160; tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EDX, mem_index);      <br />510 #else      <br />511&#160;&#160;&#160;&#160; tcg_out_mov(s, TCG_REG_EDX, addr_reg2);      <br />512&#160;&#160;&#160;&#160; tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index);      <br />513 #endif      <br />514&#160;&#160;&#160;&#160; tcg_out8(s, 0xe8);      <br />515&#160;&#160;&#160;&#160; tcg_out32(s, (tcg_target_long)qemu_ld_helpers[s_bits] -       <br />516&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (tcg_target_long)s-&gt;code_ptr - 4);</p>
</blockquote>
<p>In line 514, tcg outputs 0xe8 which means a call instruction in x86. It will call the functions in array <em>qemu_ld_helpers.</em> The args to the functions is passed by registers EAX,EDX and ECX.</p>
<blockquote><p>tcg/i386/tcg-target.c</p>
<p>413 static void *qemu_ld_helpers[4] = {     <br />414&#160;&#160;&#160;&#160; __ldb_mmu,      <br />415&#160;&#160;&#160;&#160; __ldw_mmu,      <br />416&#160;&#160;&#160;&#160; __ldl_mmu,      <br />417&#160;&#160;&#160;&#160; __ldq_mmu,      <br />418 };</p>
</blockquote>
<p>These functions <em>__ldb_mmu/__ldw_mmu</em> are defined in softmmu_template.h.</p>
<blockquote><p>softmmu_tempate.h</p>
<p>DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; int mmu_idx)</p>
</blockquote>
<p>In sum, function <em>gen_ldst</em> outputs the OPC(INDEX_op_qemu_ld32u) to <em>gen_opc_ptr</em> and <em>tcg_out_op</em> will generates the target binary according to the OPC. In the lw instruction emulation, it will generate the x86 binary calls the functions in <em>softmmu_template.h</em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/07/08/qemu-internal-part-1-the-code-path-of-memory-load-emulation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
