<?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; emulation</title>
	<atom:link href="http://vm-kernel.org/blog/category/emulation/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 mips msub instruction emulation bug</title>
		<link>http://vm-kernel.org/blog/2009/10/15/qemu-mips-msub-instruction-emulation-bug/</link>
		<comments>http://vm-kernel.org/blog/2009/10/15/qemu-mips-msub-instruction-emulation-bug/#comments</comments>
		<pubDate>Thu, 15 Oct 2009 05:06:22 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[ARM/MIPS]]></category>
		<category><![CDATA[emulation]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/10/15/qemu-mips-msub-instruction-emulation-bug/</guid>
		<description><![CDATA[It's really a long time since last post. Now I am working on the android mips porting project. I want to run android on the MIPS emulator.
The problem is that when I run mips-android on qemu, it hangs when executing init program in the initramfs root file-system. Then I use the remote gdb to debug [...]]]></description>
			<content:encoded><![CDATA[<p>It's really a long time since last post. Now I am working on the android mips porting project. I want to run android on the MIPS emulator.</p>
<p>The problem is that when I run mips-android on qemu, it hangs when executing init program in the initramfs root file-system. Then I use the remote gdb to debug the init and finds out that it it because pa_workspace is not initiated.</p>
<p>Function ashmem_create_region will open /dev/ashmem and return the fd if succeed. However, it returns -1 and the errno is 19 which means NO SUCH DEVICES.</p>
<blockquote><p>fd = ashmem_create_region("system_properties", size);</p></blockquote>
<p>The problem is who is responsible for creating /dev/ashmem?</p>
<p>In fact, in android it uses udev mechanism to create devices in /dev when executing function device_init. The full cold patch to create a device is as following:</p>
<blockquote><p>device_init-&gt;coldboot-&gt;do_coldboot-&gt;write(fd, "add\n", 4)-&gt;handle_device_fd-&gt;handle_device_event-&gt;make_device</p></blockquote>
<p>In function parse_event, it will parse the uevent msg and then pass uevent to handle_device_event. However, I find that the uevent message is a little weird. I use remote gdb to dump this message.</p>
<blockquote><p>0x7ff3d250:     "add@/class/tty/console"<br />
0x7ff3d267:     "ACTION=add"<br />
0x7ff3d272:     "DEVPATH=/class/tty/console"<br />
0x7ff3d28d:     "SUBSYSTEM=tty"<br />
0x7ff3d29b:     "MAJOR=+"<br />
0x7ff3d2a3:     "MINOR=/"<br />
0x7ff3d2ab:     "SEQNUM=31+"<br />
0x7ff3d2b6:     ""<br />
0x7ff3d2b7:     ""<br />
0x7ff3d2b8:     ""</p></blockquote>
<p>You see, in the message the MAJOR is +. It will confuse the parse_event so that the corresponding device won't be created.</p>
<p>It looks the kernel passes wrong uevent message to user space. So the question is who has messed up the uevent message?</p>
<p>Then I recall that when booting linux kernel, there are some weird messages.</p>
<blockquote><p>Primary instruction cache .kB, VIPT, 2-way, linesize 1* bytes.<br />
Primary data cache .kB, 2-way, VIPT, no aliases, linesize 1* bytes</p></blockquote>
<p>You see the instruction cache is .kB and linesize is 1* bytes, not a valid number at all.</p>
<p>Then I suspect that something is wrong in kernel when parsing the numbers. So I use the remote gdb to debug the kernel again.</p>
<blockquote><p>r4k_cache_init-&gt;probe_pcache-&gt;printk-&gt;vprintk-&gt;vscnprintf-&gt;vsnprintf-&gt;number-&gt;put_dec-&gt;put_dec_trunc</p></blockquote>
<p>Function put_dec_trunc uses a unsigned int between[0,99999] as input and outputs the number as a string. But I find that when the input is 2, the output is '.', not the expected '2'. So maybe this function is the bad boy.</p>
<p>In the following function I assume q=2.</p>
<blockquote><p>277 static char* put_dec_trunc(char *buf, unsigned q)<br />
278 {<br />
279         unsigned d3, d2, d1, d0;<br />
280         d1 = (q&gt;&gt;4) &amp; 0xf;            /*d1=0*/<br />
281         d2 = (q&gt;&gt;8) &amp; 0xf;            /*d2=0*/<br />
282         d3 = (q&gt;&gt;12);                 /*d3=0*/<br />
283<br />
284         d0 = 6*(d3 + d2 + d1) + (q &amp; 0xf);  /*d0=2*/<br />
285         q = (d0 * 0xcd) &gt;&gt; 11;              /*q=0*/<br />
286         d0 = d0 - 10*q;                     /*d==2*/<br />
287         *buf++ = d0 + ''; /* least significant digit */<br />
288         d1 = q + 9*d3 + 5*d2 + d1;   /*d1=0*/<br />
289         if (d1 != 0) {               /* it is false so we won't get into here.*/<br />
290                 q = (d1 * 0xcd) &gt;&gt; 11;<br />
291                 d1 = d1 - 10*q;<br />
292                 *buf++ = d1 + ''; /* next digit */<br />
293<br />
294                 d2 = q + 2*d2;<br />
295                 if ((d2 != 0) || (d3 != 0)) {<br />
296                         q = (d2 * 0xd) &gt;&gt; 7;<br />
297                         d2 = d2 - 10*q;<br />
298                         *buf++ = d2 + ''; /* next digit */<br />
299<br />
300                         d3 = q + 4*d3;<br />
301                         if (d3 != 0) {<br />
302                                 q = (d3 * 0xcd) &gt;&gt; 11;<br />
303                                 d3 = d3 - 10*q;<br />
304                                 *buf++ = d3 + '';  /* next digit */<br />
305                                 if (q != 0)<br />
306                                         *buf++ = q + '';  /* most sign. digit */<br />
307                         }<br />
308                 }<br />
309         }    <br />
310         return buf;<br />
311 }</p></blockquote>
<blockquote><p>/*  MIPS uses a0/a1 to pass arguments.<br />
 *  a0= address of bu<br />
 *  a1= q = 2<br />
 */<br />
8015a040 &lt;put_dec_trunc&gt;:<br />
8015a040:    00051202     srl    v0,a1,0x8        /* v0= q&gt;&gt;8*/<br />
8015a044:    00053102     srl    a2,a1,0x4        /* a2= q&gt;&gt;4*/<br />
8015a048:    30c6000f     andi    a2,a2,0xf        /* a2= (q&gt;&gt;4) &amp; 0xf = d1 in line 280*/<br />
8015a04c:    3048000f     andi    t0,v0,0xf        /* t0 = (q&gt;&gt;8) &amp; 0xf = d2 in line 281*/<br />
8015a050:    00054b02     srl    t1,a1,0xc        /* t1= (q&gt;&gt;12) = d3 in line 282*/<br />
8015a054:    00c81021     addu    v0,a2,t0        <br />
8015a058:    00491021     addu    v0,v0,t1          /* v0 = d1+d2+d3 in line 284*/<br />
8015a05c:    24030006     li    v1,6<br />
8015a060:    70433802     mul    a3,v0,v1          /*a3= 6*(d3 + d2 + d1)*/<br />
8015a064:    30a5000f     andi    a1,a1,0xf         /*a1= q &amp; 0xf*/<br />
8015a068:    24020009     li    v0,9               /*v0=9*/<br />
8015a06c:    00e56021     addu    t4,a3,a1          /*t4= 6*(d3 + d2 + d1) + (q &amp; 0xf) = d0 in line 284*/<br />
8015a070:    240b00cd     li    t3,205            /*t3= 0xcd*/<br />
8015a074:    71223802     mul    a3,t1,v0          /*a3= 9*d3 in line 288. Apparently gcc has reordered the code.*/   <br />
8015a078:    718b1802     mul    v1,t4,t3          /*v1= (d0*0xcd)*/<br />
8015a07c:    24020005     li    v0,5<br />
8015a080:    00e62821     addu    a1,a3,a2          /*a1= 9*d3 + d1 in line 288*/<br />
8015a084:    71023002     mul    a2,t0,v0          /*a2= 5*d2 in line 288*/<br />
8015a088:    00031ac2     srl    v1,v1,0xb         /*v1= (d0*0xcd)&gt;&gt;11 in line 285*/<br />
8015a08c:    240a000a     li    t2,10             /*t2=10*/<br />
8015a090:    01800013     mtlo    t4                /*put t4-&gt;lo. lo=t4= 6*(d3 + d2 + d1) + (q &amp; 0xf) = d0 in line 284 */<br />
8015a094:    706a0004     msub    v1,t2             /*hilo = v1*t2 - hilo = 10*(q) - hilo in line 286*/<br />
8015a098:    00c51021     addu    v0,a2,a1<br />
8015a09c:    00002812     mflo    a1                /*lo-&gt;a1*/    <br />
8015a0a0:    00431821     addu    v1,v0,v1<br />
8015a0a4:    24a20030     addiu    v0,a1,48         <br />
8015a0a8:    00803821     move    a3,a0<br />
8015a0ac:    a0820000     sb    v0,0(a0)</p></blockquote>
<p>We just need to see the instruction in 0x8015a094, it is a msub instruction. The defination of msub is as following:</p>
<blockquote><p>(HI,LO) = (HI,LO) - (GRP[RS]*GPR[RT])</p></blockquote>
<p>Then after executing the instruction in 0x8015a094, the HI/LO should be 0/2. But qemu produces the value 0xffffffff/0xfffffffe, which is -2 indeed. Maybe this is the problem.</p>
<p>Then I need to find how qemu emulates msub instruction.</p>
<blockquote><p>2178     case OPC_MSUB:<br />
2179         {<br />
2180             TCGv r_tmp1 = tcg_temp_new(TCG_TYPE_I64);<br />
2181             TCGv r_tmp2 = tcg_temp_new(TCG_TYPE_I64);<br />
2182             TCGv r_tmp3 = tcg_temp_new(TCG_TYPE_I64);<br />
2183<br />
2184             tcg_gen_ext32s_tl(t0, t0);<br />
2185             tcg_gen_ext32s_tl(t1, t1);<br />
2186             tcg_gen_ext_tl_i64(r_tmp1, t0);<br />
2187             tcg_gen_ext_tl_i64(r_tmp2, t1);<br />
2188             tcg_gen_mul_i64(r_tmp1, r_tmp1, r_tmp2);  /*r_tmp1= gpr[rs]*gpr[rt] */<br />
2189             gen_load_LO(t0, 0);                /*t0 &lt;- lo*/<br />
2190             gen_load_HI(t1, 0);                /*t1 &lt;- hi*/<br />
2191             tcg_gen_extu_tl_i64(r_tmp2, t0);   /*r_tmp2 = 64bit expand of lo*/<br />
2192             tcg_gen_extu_tl_i64(r_tmp3, t1);   /*r_tmp3 = 64bit expand of hi*/<br />
2193             tcg_gen_shli_i64(r_tmp3, r_tmp3, 32);<br />
2194             tcg_gen_or_i64(r_tmp2, r_tmp2, r_tmp3);<br />
2195             tcg_temp_free(r_tmp3);<br />
2196             tcg_gen_sub_i64(r_tmp1, r_tmp1, r_tmp2); /*r_tmp1= r_tmp1 - r_tmp2 = gpr[rs]*gpr[rt] - HI/LO */<br />
2197             tcg_temp_free(r_tmp2);<br />
2198             tcg_gen_trunc_i64_tl(t0, r_tmp1);<br />
2199             tcg_gen_shri_i64(r_tmp1, r_tmp1, 32);<br />
2200             tcg_gen_trunc_i64_tl(t1, r_tmp1);<br />
2201             tcg_temp_free(r_tmp1);<br />
2202             tcg_gen_ext32s_tl(t0, t0);<br />
2203             tcg_gen_ext32s_tl(t1, t1);<br />
2204             gen_store_LO(t0, 0);<br />
2205             gen_store_HI(t1, 0);<br />
2206         }<br />
2207         opn = "msub";<br />
2208         break;</p></blockquote>
<p>You see, qemu makes an error emulation of msub instruction. It uses gpr[rs]*gpr[rt]-HI/LO and then put the results to HI/LO, which is different from the defination of msub instruction. I patched the qemu code and it works.</p>
<p>BTW: MIPS32 4KTM Processor Core Family Software User’s Manual version MD00016 gives an error operation of msub instruction on papge 253.</p>
<blockquote><p>Operation:<br />
   temp ← (HI || LO) - (GPR[rs] * GPR[rt])<br />
   HI ← temp63..32<br />
   LO ← temp31..0</p></blockquote>
<p>Maybe this misleads the qemu developers. The latest qemu version has fixed this bug. We can see this instruction emulation in qemu-svn-20091014.</p>
<blockquote><p>2195     case OPC_MSUB:<br />
2196         {<br />
2197             TCGv_i64 t2 = tcg_temp_new_i64();<br />
2198             TCGv_i64 t3 = tcg_temp_new_i64();<br />
2199<br />
2200             tcg_gen_ext_tl_i64(t2, t0);<br />
2201             tcg_gen_ext_tl_i64(t3, t1);<br />
2202             tcg_gen_mul_i64(t2, t2, t3);  /*t2=GPR[RS]*GPR[RT] */<br />
2203             tcg_gen_concat_tl_i64(t3, cpu_LO[0], cpu_HI[0]);  /*t3= HI/LO*/<br />
2204             tcg_gen_sub_i64(t2, t3, t2); /*t2= HI/LO - GPR[RS]*GPR[RT] */<br />
2205             tcg_temp_free_i64(t3);<br />
2206             tcg_gen_trunc_i64_tl(t0, t2);<br />
2207             tcg_gen_shri_i64(t2, t2, 32);<br />
2208             tcg_gen_trunc_i64_tl(t1, t2);<br />
2209             tcg_temp_free_i64(t2);<br />
2210             tcg_gen_ext32s_tl(cpu_LO[0], t0);<br />
2211             tcg_gen_ext32s_tl(cpu_HI[0], t1);<br />
2212         }<br />
2213         opn = "msub";<br />
2214         break;</p></blockquote>
<p>See <a href="http://www.archivum.info/qemu-devel@nongnu.org/2009-07/00011/%5BQemu-devel%5D_%5BPATCH%5D_target-mips:_fix_MADD_and_MSUB_MSUBU_instructions" target="_blank">this link</a> for more information.</p>
<p>So finding this bug is really not easy. I have to dig dig and dig from userland to linux kernel and then to qemu until catching this bad qemu bug. Thanks to the remote gdb and gdb stub in qemu, it makes life easier.</p>
<p>Following is the patch of qemu.</p>
<blockquote><p>diff --git a/target-mips/translate.c b/target-mips/translate.c<br />
index 3dded6c..0a1b461 100644<br />
--- a/target-mips/translate.c<br />
+++ b/target-mips/translate.c<br />
@@ -2193,7 +2193,11 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc,<br />
             tcg_gen_shli_i64(r_tmp3, r_tmp3, 32);<br />
             tcg_gen_or_i64(r_tmp2, r_tmp2, r_tmp3);<br />
             tcg_temp_free(r_tmp3);<br />
-            tcg_gen_sub_i64(r_tmp1, r_tmp1, r_tmp2);<br />
+            /* msub means HI/LO = HI/LO - GPR[RS]*GPR[RT],<br />
+             * not HI/LO = GPR[RS]*GPR[RT] - HI/LO<br />
+             */<br />
+            //tcg_gen_sub_i64(r_tmp1, r_tmp2, r_tmp2);<br />
+            tcg_gen_sub_i64(r_tmp1, r_tmp2, r_tmp1);<br />
             tcg_temp_free(r_tmp2);<br />
             tcg_gen_trunc_i64_tl(t0, r_tmp1);<br />
             tcg_gen_shri_i64(r_tmp1, r_tmp1, 32);</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/10/15/qemu-mips-msub-instruction-emulation-bug/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>
		<item>
		<title>a little progress on qemu-loongson</title>
		<link>http://vm-kernel.org/blog/2009/06/10/a-little-progress-on-qemu-loongson/</link>
		<comments>http://vm-kernel.org/blog/2009/06/10/a-little-progress-on-qemu-loongson/#comments</comments>
		<pubDate>Wed, 10 Jun 2009 03:15:00 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[emulation]]></category>
		<category><![CDATA[emulatuion]]></category>
		<category><![CDATA[qemu]]></category>
		<category><![CDATA[qemu-loongson]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/07/08/a-little-progress-on-qemu-loongson/</guid>
		<description><![CDATA[Hi guys, it is about one month since posting last blog entry. These days I am really very busy preparing the GRE and Tofel test. Moreover I have to work to support my life. So I have to spend less time on qemu-loongson.
Anyway, there are progress these days. 

Rewrite the GPIO I2C emulation for gdium. [...]]]></description>
			<content:encoded><![CDATA[<p>Hi guys, it is about one month since posting last blog entry. These days I am really very busy preparing the GRE and Tofel test. Moreover I have to work to support my life. So I have to spend less time on qemu-loongson.</p>
<p>Anyway, there are progress these days. </p>
<ul>
<li>Rewrite the GPIO I2C emulation for gdium. Now it is more clear than before.</li>
<li>add st4180 rtc emulation to qemu</li>
<li>add stds75 temperature sensor emulation to qemu</li>
<li>change a little in uart emulation to satisfy pmon’s uart probing process</li>
<li>fix a little bug in pflash_cfi02.c</li>
<li>fix gdb stub bug in qemu to support mips64</li>
<p> Follow is the uart output of qemu-loongson.</p>
</ul>
<ul>
<blockquote dir="ltr" style="margin-right: 0px;">
<p>kill-bill:/home/root/sdd/gdium/qemu-loongson/mips64el-softmmu# ./qemu-system-mips64el -M gdium -pflash gzrom.bin.gdb -nographic -S -s <br />Register sst39vf040&nbsp; size 80000&nbsp; at offset 08800000 addr 1fc00000 'pflash0' 80        <br />devfn 70         <br />unassigned_mem_readl Unassigned mem read 000000001fbffffc        <br />unassigned_mem_readl Unassigned mem read 000000001fbffffc        <br />new_sm502_mm_io 7000000 pci_mem _base 10000000        <br />PMON2000 MIPS Initializing. Standby...        <br /> PRID=00006302        <br />enable register space of MEMORY        <br />DDR2 config begin_whd        <br />DIMM read        <br />0000008000000008read DIMM number of rows        <br />read number of cols        <br />module data width        <br />DIMM SIZE=20000000        <br />cols rows:        <br />04030940DDR2 config end        <br />DDR2 DLL locked        <br />00000004        <br />disable register space of MEMORY        <br />jlliu : rom speed reg : 0x00000f8c        <br />Init SDRAM Done!        <br />Sizing caches...        <br />Init caches...        <br />godson2 caches found        <br />Init caches done, cfg = 00030932        <br />Copy PMON to execute location...        <br />copy text section done.        <br />Copy PMON to execute location done.        <br />sp=80ffc000...............new_sm502_mm_io 6000000 pci_mem _base 10000000        <br />cmd 7        <br />mmio 6000000        <br />FREQ        <br />FREI        <br />DONE        <br />DEVI        <br />ENVI        <br />MAPV        <br />nvram=bfc00000        <br />NVRAM is invalid!        <br />NVRAM@bfc00000        <br />STDV        <br />80100000: heap is already above this point        <br />SBDD        <br />P12PCIH</p>
</blockquote>
</ul>
<p></p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/06/10/a-little-progress-on-qemu-loongson/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>I2C emulation in qemu-loongson</title>
		<link>http://vm-kernel.org/blog/2009/04/22/i2c-emulation-in-qemu-loongson/</link>
		<comments>http://vm-kernel.org/blog/2009/04/22/i2c-emulation-in-qemu-loongson/#comments</comments>
		<pubDate>Wed, 22 Apr 2009 03:52:11 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[emulation]]></category>
		<category><![CDATA[loongson]]></category>
		<category><![CDATA[gdium]]></category>
		<category><![CDATA[i2c]]></category>
		<category><![CDATA[qemu-loongson]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/04/22/i2c-emulation-in-qemu-loongson/</guid>
		<description><![CDATA[I think most of you may think it is easy to emulate I2C device in qemu, for qemu has provided a framework of i2c, both the master and slave devices. You are right. Emulating the I2C is not difficult in qemu.
What I want to post here is not emulating I2C device directly, but emulating GPIO [...]]]></description>
			<content:encoded><![CDATA[<p>I think most of you may think it is easy to emulate I2C device in qemu, for qemu has provided a framework of i2c, both the master and slave devices. You are right. Emulating the I2C is not difficult in qemu.</p>
<p>What I want to post here is not emulating I2C device directly, but emulating GPIO logic which pmon uses to emulate I2C. A little confused? Me too. Ok, let me talk the story from the began.</p>
<p>In pmon, it uses sm502 gpio pin 6 and 13 to emulate I2C master devices, which communicates with other slave device, such as SPD EEPROM and temperature sensors. I do not know why lemote uses gpio to emulate i2c, instead of using the I2C master in sm502. When the data is written to gpio pins according to I2C state machine, pmon expects the gpio pins acting like the I2C master devices. So qemu needs to emulate the I2C cycle accurate state machine, not only the I2C function.</p>
<p>I summarize the I2C emulation code logic of pmon here for reference.</p>
<p>(1) Start Bit</p>
<p>SCL=0 -&gt; SDA=1 -&gt; SCL=1 -&gt; SDA=0 -&gt; SCL=0</p>
<p>(2)Stop Bit</p>
<p>SCL=0 -&gt; SDA=0 -&gt;SCL=1 -&gt;SDA=1 -&gt; SCL=0</p>
<p>(3)Write a bit</p>
<p>Put data on sda -&gt; SCL=1 -&gt; SCL=0</p>
<p>(4)Write a byte</p>
<p>repeating (3) 8 times -&gt; SDA=1</p>
<p>(5)Recv ACK</p>
<p>SCL=1 -&gt;read sda until sda=0 -&gt; SCL=0</p>
<p>(6)Read a bit</p>
<p>SCL=1 -&gt; read data on SDA -&gt; SCL=0</p>
<p>(7)Read a byte</p>
<p>repeating (6) 8 times</p>
<p>(8)Send ACK</p>
<p>put ACK on sda -&gt; SCL=1 -&gt; SCL=0</p>
<p>(9)Send a buffer</p>
<p>Do the following steps until the buffer is empty</p>
<ol>
<li>START </li>
<li>Send slave address </li>
<li>Recv ACK </li>
<li>Send reg address </li>
<li>Recv ACK </li>
<li>Send one byte </li>
<li>Recv ACK </li>
<li>STOP </li>
</ol>
<p>(10) Recv many bytes from slave deviceDo the following steps until finished </p>
<ol>
<li>START </li>
<li>Send slave address </li>
<li>Recv ACK </li>
<li>Send reg address </li>
<li>Recv ACK</li>
<li>Repeat START </li>
<li>Send slave address+1(tell slave we want to recv) </li>
<li>Recv ACK </li>
<li>Recv a byte </li>
<li>Send ACK </li>
<li>STOP </li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/04/22/i2c-emulation-in-qemu-loongson/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>QEMU Does NOT Support MIPS Host Anymore</title>
		<link>http://vm-kernel.org/blog/2009/03/26/qemu-does-not-support-mips-host-anymore/</link>
		<comments>http://vm-kernel.org/blog/2009/03/26/qemu-does-not-support-mips-host-anymore/#comments</comments>
		<pubDate>Thu, 26 Mar 2009 06:42:03 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[emulation]]></category>
		<category><![CDATA[loongson]]></category>
		<category><![CDATA[gdium]]></category>
		<category><![CDATA[MIPS]]></category>
		<category><![CDATA[qemu]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/03/26/qemu-does-not-support-mips-host-anymore/</guid>
		<description><![CDATA[New version of qemu does not support MIPS host anymore.
For the sake of portability, the new version of qemu uses TCG, a tiny code generator, instead of dyngen to generate host code. That means one backend is needed for each host architecture. There are i386,x86_64,ppc,ppc64 and hppa backend in TCG, but MIPS is not on [...]]]></description>
			<content:encoded><![CDATA[<p>New version of qemu does not support MIPS host anymore.</p>
<p align="justify">For the sake of portability, the new version of qemu uses TCG, a tiny code generator, instead of dyngen to generate host code. That means one backend is needed for each host architecture. There are i386,x86_64,ppc,ppc64 and hppa backend in TCG, but MIPS is not on the list.</p>
<p align="justify">I confirm this conclusion on the <a href="http://www.nongnu.org/qemu/status.html">status page of qemu web site</a>. The MIPS's status is 'Not Supported'. Perhaps these days the most emergent work is to add MIPS host support, instead of loongson guest support to qemu.</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/03/26/qemu-does-not-support-mips-host-anymore/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Cause register bug in qemu-jz</title>
		<link>http://vm-kernel.org/blog/2009/01/16/cause-register-bug-in-qemu-jz/</link>
		<comments>http://vm-kernel.org/blog/2009/01/16/cause-register-bug-in-qemu-jz/#comments</comments>
		<pubDate>Fri, 16 Jan 2009 11:24:39 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[JZ]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[MIPS]]></category>
		<category><![CDATA[qemu-jz]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/01/16/cause-register-bug-in-qemu-jz/</guid>
		<description><![CDATA[In MIPS, cause register is responsible for telling CPU which interrupt is happening. CPU read the IP bits in cause register and dispatch interrupt to interrupt service routine.
One question: who is responsible for clear IP bits in cause register? That is interrupt handler. Interrupt handler must clear the corresponding IP bit in cause register, othewise [...]]]></description>
			<content:encoded><![CDATA[<p>In MIPS, cause register is responsible for telling CPU which interrupt is happening. CPU read the IP bits in cause register and dispatch interrupt to interrupt service routine.</p>
<p>One question: who is responsible for clear IP bits in cause register? That is interrupt handler. Interrupt handler must clear the corresponding IP bit in cause register, othewise next time, interrupt is reenabled, CPU thinks the interrupt is still happening!</p>
<p>In linux, the function to clear IP bit in mask_and_ack_intc_irq. In JZ4740, it is:</p>
<blockquote><p>50 static void mask_and_ack_intc_irq(unsigned int irq)     <br />51 {      <br />52&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; __intc_mask_irq(irq);      <br />53&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; __intc_ack_irq(irq);      <br />54 }</p>
</blockquote>
<blockquote><p>1372 #define __intc_mask_irq(n)&#160;&#160;&#160;&#160;&#160; ( REG_INTC_IMSR = (1 &lt;&lt; (n)) )     <br />1373 #define __intc_ack_irq(n)&#160;&#160;&#160;&#160;&#160;&#160; ( REG_INTC_IPR = (1 &lt;&lt; (n)) )</p>
</blockquote>
<p>Linux writes to REG_INTC_IPR to clear pending interrupt and JZ4740 interrupt controller will clear the interrupt signal to MIPS CPU.</p>
<p>In qemu-jz, when writing to REG_INTC_IPR, the interrupt to MIPS cpu is not cleared and linux runs in a dead loop.</p>
<p>The following is the interrupt handling process in MIPS CPU.</p>
<p>1. Set ECP=PC and CPU jumps to 0x80000200 and then to handle_int. The interrupt is disabled.</p>
<p>2. Function handle_int will save all the register(including EPC so that interrupt can be nested) to stack and call plat_irq_dispatch. </p>
<p>3. After the irq is processed, it will reenable the in function __do_softirq. If a new interrupt is coming at this time(check the IP bits cause register), go to 1. otherwise go to 4.</p>
<p>4. Call ret_from_irq and restore all the register(including EPC) and using ERET to set PC=EPC.</p>
<p>5. Runs from PC.</p>
<p>Because of the IP bits in cause register is not cleared, linux will run into a dead loop between 1 and 3.</p>
<p>Thanks to the log function and remote gdb. Really hard to find this bug in qemu-jz.</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/01/16/cause-register-bug-in-qemu-jz/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Current status of qemu-omap3</title>
		<link>http://vm-kernel.org/blog/2009/01/05/current-status-of-qemu-omap3/</link>
		<comments>http://vm-kernel.org/blog/2009/01/05/current-status-of-qemu-omap3/#comments</comments>
		<pubDate>Mon, 05 Jan 2009 08:46:52 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[qemu-omap3]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2009/01/05/current-status-of-qemu-omap3/</guid>
		<description><![CDATA[1. u-boot and linux kernel can boot from mmc image
2. Because twl4030 emulation is not complete, mmc device can not be found in linux kernel.
Next step:
1. twl4030 emulation. Big effort is needed.
2. dss/dma emulation
3. usb
]]></description>
			<content:encoded><![CDATA[<p>1. u-boot and linux kernel can boot from mmc image</p>
<p>2. Because twl4030 emulation is not complete, mmc device can not be found in linux kernel.</p>
<p>Next step:</p>
<p>1. twl4030 emulation. Big effort is needed.</p>
<p>2. dss/dma emulation</p>
<p>3. usb</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2009/01/05/current-status-of-qemu-omap3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>nand flash emulation in qemu-omap3</title>
		<link>http://vm-kernel.org/blog/2008/12/24/nand-flash-emulation-in-qemu-omap3/</link>
		<comments>http://vm-kernel.org/blog/2008/12/24/nand-flash-emulation-in-qemu-omap3/#comments</comments>
		<pubDate>Wed, 24 Dec 2008 04:44:43 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[nand flash]]></category>
		<category><![CDATA[qemu-omap3]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/12/24/nand-flash-emulation-in-qemu-omap3/</guid>
		<description><![CDATA[Qemu currently supports nand flash emulation in hw/nand.c. But it is for spi flash and it's not ready for big page nand flash, 2k page nand flash for example. I add the big page nand flash emulation to it, but it has some bugs. When I boots jffs2 rootfs from qemu-omap, some error messages appear.
&#60;4&#62;mtd-&#62;read(0x1f9f4 [...]]]></description>
			<content:encoded><![CDATA[<p>Qemu currently supports nand flash emulation in hw/nand.c. But it is for spi flash and it's not ready for big page nand flash, 2k page nand flash for example. I add the big page nand flash emulation to it, but it has some bugs. When I boots jffs2 rootfs from qemu-omap, some error messages appear.</p>
<blockquote><p>&lt;4&gt;mtd-&gt;read(0x1f9f4 bytes from 0x2060c) returned ECC error</p>
<p>&lt;4&gt;mtd-&gt;read(0x1f9f4 bytes from 0x2060c) returned ECC error</p>
<p>&lt;5&gt;jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00020800: 0xb1e0 instead</p>
<p>jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00020800: 0xb1e0 instead</p>
</blockquote>
<p>After hours of debuging, I find that the message is related to bugs of nand flash emulation.But I do not want to change nand.c code too much because I am afraid it will have some sideeffect to other type nand flash emulation. So I decide to wirte a new nand flash emulation for beagle board or other big page nand flash. After that, rootfs can be booted from nand flash.</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/12/24/nand-flash-emulation-in-qemu-omap3/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>I have issues when using qemu-omap3</title>
		<link>http://vm-kernel.org/blog/2008/12/19/i-have-issues-when-using-qemu-omap3/</link>
		<comments>http://vm-kernel.org/blog/2008/12/19/i-have-issues-when-using-qemu-omap3/#comments</comments>
		<pubDate>Fri, 19 Dec 2008 03:15:55 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[emulation]]></category>
		<category><![CDATA[qemu-omap3]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/?p=102</guid>
		<description><![CDATA[If you have issues when using qemu-omap3, please post it on issues page of qemu-omap3 on googlecode. A google groups of qemu-omap3 is also available. Thanks.
]]></description>
			<content:encoded><![CDATA[<p>If you have issues when using qemu-omap3, please post it on <a href="http://code.google.com/p/qemu-omap3/issues/list" target="_blank">issues page of qemu-omap3 </a>on googlecode. <a href="http://groups.google.com/group/qemu-omap3" target="_blank">A google groups of qemu-omap3 </a>is also available. Thanks.</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/12/19/i-have-issues-when-using-qemu-omap3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux is running on qemu-omap3</title>
		<link>http://vm-kernel.org/blog/2008/12/15/linux-is-running-on-qemu-omap3/</link>
		<comments>http://vm-kernel.org/blog/2008/12/15/linux-is-running-on-qemu-omap3/#comments</comments>
		<pubDate>Mon, 15 Dec 2008 09:05:36 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[beagle board]]></category>
		<category><![CDATA[qemu]]></category>
		<category><![CDATA[qemu-omap3]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/12/15/linux-is-running-on-qemu-omap3/</guid>
		<description><![CDATA[Hi everyone, a good news for you. Linux in running successfully on qemu-omap3. Following is the screenshot.

However, MMC/USB/audio/LCD display/keyboard emulation are not ok . I will work on it.
In the following I will give you some informations of how to run qemu-omap3.
1. checkout qemu-omap3 from svn. Tag v0.01 is working.
svn checkout http://qemu-omap3.googlecode.com/svn/tags/v0.01 qemu-omap3
Or you can [...]]]></description>
			<content:encoded><![CDATA[<p>Hi everyone, a good news for you. Linux in running successfully on qemu-omap3. Following is the screenshot.</p>
<p><a href="http://picasaweb.google.com/lh/photo/SG8C2_rqSfuuH_xMvqTiLg"><img src="http://lh4.ggpht.com/_vf_uV7bji2A/SUYcM3m6jLI/AAAAAAAAANo/9nKq87MosAg/s400/bb2.jpg" alt="" /></a></p>
<p>However, MMC/USB/audio/LCD display/keyboard emulation are not ok . I will work on it.</p>
<p>In the following I will give you some informations of how to run qemu-omap3.</p>
<p>1. checkout qemu-omap3 from svn. Tag v0.01 is working.</p>
<blockquote><p>svn checkout <a href="http://qemu-omap3.googlecode.com/svn/tags/v0.01">http://qemu-omap3.googlecode.com/svn/tags/v0.01</a> qemu-omap3</p></blockquote>
<p>Or you can download it from <a title="http://qemu-omap3.googlecode.com/files/qemu-omap3-v0.01.tar.bz2" href="http://qemu-omap3.googlecode.com/files/qemu-omap3-v0.01.tar.bz2">http://qemu-omap3.googlecode.com/files/qemu-omap3-v0.01.tar.bz2</a> .</p>
<p>2. make qemu-omap3</p>
<blockquote><p>cd qemu-omap3<br />
./configure --target-list=arm-softmmu<br />
make</p></blockquote>
<p>qemu-system-arm will be generated in directory qemu-omap3/arm-softmmu.</p>
<p>3. download the x-loader/u-boot/kernel image.</p>
<blockquote><p>cd qemu-omap3/arm-softmmu<br />
wget <a href="http://qemu-omap3.googlecode.com/files/image-v0.01.tar.bz2">http://qemu-omap3.googlecode.com/files/image-v0.01.tar.bz2</a><br />
tar jxvf image-v0.01.tar.bz2</p></blockquote>
<p>4. download the rootfs from beagle board site.</p>
<blockquote><p>wget <a title="http://beagleboard.googlecode.com/files/rd-ext2-8M.bin" href="http://beagleboard.googlecode.com/files/rd-ext2-8M.bin">http://beagleboard.googlecode.com/files/rd-ext2-8M.bin</a></p></blockquote>
<p>5. make nand flash image</p>
<blockquote><p>cd qemu-omap3/arm-softmmu<br />
cp ../bb_nandflash.sh .<br />
cp ../bb_nandflash_ecc .<br />
./bb_nandflash.sh x-load.bin.ift beagle-nand.bin x-loader<br />
./bb_nandflash.sh u-boot.bin beagle-nand.bin u-boot<br />
./bb_nandflash.sh uImage beagle-nand.bin kernel<br />
./bb_nandflash.sh rd-ext2-8M.bin  beagle-nand.bin  rootfs<br />
./bb_nandflash_ecc beagle-nand.bin 0x0 0xe80000</p></blockquote>
<p>6. run qemu-omap3</p>
<blockquote><p>./qemu-system-arm -M beagle -mtdblock beagle-nand.bin</p></blockquote>
<p>7. During the graphical emulation, you can use the following keys:<br />
Ctrl-Alt-f<br />
Toggle full screen<br />
Ctrl-Alt-n<br />
Switch to virtual console 'n'. Standard console mappings are:<br />
1<br />
Target system display<br />
2<br />
Monitor<br />
3<br />
Serial port<br />
Ctrl-Alt<br />
Toggle mouse and keyboard grab.</p>
<p>Using Ctrl-Alt-3 to switch to beagle board serial port. You can see x-loader and u-boot booting message. When u-boot command line appears, type the following commands.</p>
<blockquote><p>OMAP3 beagleboard.org # nand read 0x80000000 0x280000 0x400000<br />
NAND read: device 0 offset 0x280000, size 0x400000<br />
4194304 bytes read: OK<br />
OMAP3 beagleboard.org # nand read 0x81600000 0x680000 0x800000<br />
NAND read: device 0 offset 0x680000, size 0x800000<br />
8388608 bytes read: OK<br />
OMAP3 beagleboard.org # setenv bootargs 'console=ttyS2,115200n8 ramdisk_size=8192 root=/dev/ram0 rw rootfstype=ext2 initrd=0x81600000,8M nohz=0ff'<br />
OMAP3 beagleboard.org # bootm 0x80000000</p></blockquote>
<p>Linux will boot with some debug information of qemu-omap3. Enjoy it.</p>
<p>Update: 2010-02-20</p>
<p>I am too busy to do omap3 emulation these days. The guy at maemo has made great improvement in qemu omap3 emulation. Go to <a rel="nofollow" href="https://garage.maemo.org/scm/?group_id=877">this page</a> for more information. I do not know whether the steps in this artilcle can be used in the new version of qemu-omap3.</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/12/15/linux-is-running-on-qemu-omap3/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
		<item>
		<title>low level debug of linux omap</title>
		<link>http://vm-kernel.org/blog/2008/12/12/low-level-debug-of-linux-omap/</link>
		<comments>http://vm-kernel.org/blog/2008/12/12/low-level-debug-of-linux-omap/#comments</comments>
		<pubDate>Fri, 12 Dec 2008 05:02:28 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[low level debug]]></category>
		<category><![CDATA[printascii]]></category>
		<category><![CDATA[qemu-omap3]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/12/12/low-level-debug-of-linux-omap/</guid>
		<description><![CDATA[Working on qmeu-omap3. A starange problem. If I use the kernel image downloaded from http://beagleboard.googlecode.com/files/uImage_OTG, some kernel boot informations can be displayed in uart3. But when I use the kernel source from linux omap git and build it by myself, there are NO linux kernel boot informations at all.
Linux kernel uses printk to output informations [...]]]></description>
			<content:encoded><![CDATA[<p align="justify">Working on qmeu-omap3. A starange problem. If I use the kernel image downloaded from <a title="http://beagleboard.googlecode.com/files/uImage_OTG" href="http://beagleboard.googlecode.com/files/uImage_OTG">http://beagleboard.googlecode.com/files/uImage_OTG</a>, some kernel boot informations can be displayed in uart3. But when I use the kernel source from <a href="http://source.mvista.com/git/gitweb.cgi?p=linux-omap-2.6.git;a=summary" target="_blank">linux omap git</a> and build it by myself, there are NO linux kernel boot informations at all.</p>
<p align="justify">Linux kernel uses printk to output informations to console and the console struct is initialized in function console_init(). So why the linux can be displayed before function console_init()?</p>
<p align="justify">The answer is that there is another way to output informations to console. That is low level debug. It uses printascii to print informations. The following is the code in vprintk.</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">556 #ifdef  CONFIG_DEBUG_LL
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">557         printascii(printk_buf);
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">558 #endif</pre>
</pre>
<p>So if we want to output debug information as early as possible, define CONFIG_DEBUG_LL first. </p>
<p>The default configuration of beagle board in <a title="http://www.beagleboard.org/uploads/2.6_kernel_revb-v2.tar.gz" href="http://www.beagleboard.org/uploads/2.6_kernel_revb-v2.tar.gz">http://www.beagleboard.org/uploads/2.6_kernel_revb-v2.tar.gz</a> defines this macro while the source code from&#160; <a href="http://source.mvista.com/git/gitweb.cgi?p=linux-omap-2.6.git;a=summary" target="_blank">linux omap git</a> does not define this macro.</p>
<p>Function printascii is defined in include/asm-arm/arch-omap/debug-macro.S.</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/12/12/low-level-debug-of-linux-omap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>add ecc value to nand flash image</title>
		<link>http://vm-kernel.org/blog/2008/12/06/add-ecc-value-to-nand-flash-image/</link>
		<comments>http://vm-kernel.org/blog/2008/12/06/add-ecc-value-to-nand-flash-image/#comments</comments>
		<pubDate>Sat, 06 Dec 2008 06:38:59 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[beagle board]]></category>
		<category><![CDATA[qemu-omap3]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/12/06/add-ecc-value-to-nand-flash-image/</guid>
		<description><![CDATA[I use qemu-omap3 to boot linux kernel from nand. The linux image is downloaded from http://beagleboard.googlecode.com/files/uImage_OTG.

OMAP3 beagleboard.org #
OMAP3 beagleboard.org # nand read 0x80000000 0x280000 0x400000
NAND read: device 0 offset 0x280000, size 0x400000
 4194304 bytes read: ERROR
OMAP3 beagleboard.org # bootm 0x80000000
## Booting kernel from Legacy Image at 80000000 ...
   Image Name:   Linux-2.6.22.18-omap3
 [...]]]></description>
			<content:encoded><![CDATA[<p align="justify">I use qemu-omap3 to boot linux kernel from nand. The linux image is downloaded from <a href="http://beagleboard.googlecode.com/files/uImage_OTG">http://beagleboard.googlecode.com/files/uImage_OTG</a>.</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">OMAP3 beagleboard.org #</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">OMAP3 beagleboard.org # nand <span style="color: #0000ff;">read</span> 0x80000000 0x280000 0x400000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">NAND <span style="color: #0000ff;">read</span>: device 0 offset 0x280000, size 0x400000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;"> 4194304 bytes <span style="color: #0000ff;">read</span>: ERROR</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">OMAP3 beagleboard.org # bootm 0x80000000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">## Booting kernel from Legacy Image at 80000000 ...</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Image Name:   Linux-2.6.22.18-omap3</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Image Type:   ARM Linux Kernel Image (uncompressed)</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Data Size:    1856616 Bytes =  1.8 MB</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Load Address: 80008000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Entry Point:  80008000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Verifying Checksum ... Bad Data CRC</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">ERROR: can't get kernel image!</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">OMAP3 beagleboard.org #</pre>
</pre>
<p align="justify">It means crc error in linux image. I use qemu console command 'memsave' to dump the data from address 0x80000000 to a file and compare it to uImage_OTG. The result is that linux image in ram(from 0x80000000) is different from uImage_OTG. In every block(256 bytes), there is one bit different from uImage_OTG. The linux image is changed when using 'nand read' command to read it from nand flash image to sram.</p>
<p align="justify">What has happened when reading linux image from nand flash image into sram??</p>
<p>After debuging about half a day, I find that the data are changed by the correct_data function. I <strong>*DID NOT*</strong> save ecc value in nand flash image so u-boot thinks the ecc value in oob is wrong and correct it. Ecc can only correct one bit in an block, that is why a bit has been changed in 256 bytes.</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">ecc_status = this-&gt;correct_data(mtd, &amp;data_poi[datidx], &amp;ecc_code[j], &amp;ecc_calc[j]);</pre>
</pre>
<p>I write a file(<a href="http://code.google.com/p/qemu-omap3/source/browse/trunk/bb_nandflash_ecc.c" target="_blank">bb_nandflash_ecc.c</a>) to generate ecc value for nand flash image. Now I can boot linux image using u-boot command.</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">OMAP3 beagleboard.org # nand <span style="color: #0000ff;">read</span> 0x80000000 0x280000 0x400000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">NAND <span style="color: #0000ff;">read</span>: device 0 offset 0x280000, size 0x400000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;"> 4194304 bytes <span style="color: #0000ff;">read</span>: OK</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">OMAP3 beagleboard.org # bootm 0x80000000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">## Booting kernel from Legacy Image at 80000000 ...</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Image Name:   Linux-2.6.22.18-omap3</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Image Type:   ARM Linux Kernel Image (uncompressed)</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Data Size:    1856616 Bytes =  1.8 MB</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Load Address: 80008000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Entry Point:  80008000</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Verifying Checksum ... OK</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">   Loading Kernel Image ... OK</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">OK</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">Starting kernel ...</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">smc 80e80414  insn 1600070</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">Uncompressing Linux.......................................................................................................................... done, booting the kernel.</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">&lt;5&gt;Linux version 2.6.22.18-omap3 (root@fedoraserver) (gcc version 4.2.1 (CodeSourcery Sourcery G++ Lite 2007q3-51)) #1 Thu Jul 24 15:29:36 IST 2008</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">CPU: ARMv7 Processor [411fc082] revision 2 (ARMv7), cr=00003837</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">Machine: OMAP3 Beagle board</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">Memory policy: ECC disabled, Data cache writeback</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">&lt;7&gt;On node 0 totalpages: 32768</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">&lt;7&gt;  DMA zone: 256 pages used <span style="color: #0000ff;">for</span> memmap</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">&lt;7&gt;  DMA zone: 0 pages reserved</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">&lt;7&gt;  DMA zone: 32512 pages, LIFO batch:7</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">&lt;7&gt;  Normal zone: 0 pages used <span style="color: #0000ff;">for</span> memmap</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">Unassigned mem readl 4830a204</pre>
</pre>
<p align="justify">Because of omap3 emulation is not complete, linux kernel will stop after linux kernel booting. I will work on it.</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/12/06/add-ecc-value-to-nand-flash-image/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LCD is working in qemu-omap3</title>
		<link>http://vm-kernel.org/blog/2008/12/04/lcd-is-working-in-qemu-omap3/</link>
		<comments>http://vm-kernel.org/blog/2008/12/04/lcd-is-working-in-qemu-omap3/#comments</comments>
		<pubDate>Thu, 04 Dec 2008 12:31:45 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>
		<category><![CDATA[beagle board]]></category>
		<category><![CDATA[qemu]]></category>
		<category><![CDATA[qemu-omap3]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/12/04/lcd-is-working-in-qemu-omap3/</guid>
		<description><![CDATA[Good news. LCD is working in qemu-omap3
Beagle board has an DVI transmitter TFP 410 and it outputs ditigal signal to LCD panel which has DVI connector. However, we do not care TFP 410 because it is just a pipe for LCD output data. What we care about is the LCD output data itself.
According to the [...]]]></description>
			<content:encoded><![CDATA[<p>Good news. LCD is working in <a href="http://code.google.com/p/qemu-omap3" target="_blank">qemu-omap3</a></p>
<p align="justify">Beagle board has an DVI transmitter TFP 410 and it outputs ditigal signal to LCD panel which has DVI connector. However, we do not care TFP 410 because it is just a pipe for LCD output data. What we care about is the LCD output data itself.</p>
<p align="justify">According to the datasheet of OMAP3530, it has three interfaces for LCD output:Parellel Interface, SDI interface and DSI interface. In the case of parellel interface, it can work in two modes: RFBI mode and RFBI bypass mode.</p>
<p align="justify">In RFBI mode, the datapath for LCD output data is display controller and RFBI chip. In RFBI bypass mode, the data is output to LCD panel or other interfaces directly. Beagle board uses TFP 410 to receive 24 bit LCD data and then transmits the data to LCD through DVI interface.</p>
<p align="justify">Qemu already has omap dss emulation. But it is not complete and only for RFBI mode(Nokia N800 uses RFBI). I just follow the Blizzard template and write my own omap3_lcd_panel_template. And then add the sdl update function omap3_lcd_panel_update_display to omap_dss.c. Also some other utility functions are needed. It works now, the beagle and penguin are displayed in my LCD panel. The following is the screenshot.</p>
<p align="justify">
<table style="width:auto;">
<tr>
<td><a href="http://picasaweb.google.com/lh/photo/jGNZQCmgvq2cbOTImFntqA"><img src="http://lh4.ggpht.com/_vf_uV7bji2A/STfwNadnFHI/AAAAAAAAANE/BxfA_n1ZBkM/s288/omap3-dss.JPG" /></a></td>
</tr>
</table>
<p align="justify">I have tested the LCD emulation speed and the result is not bad. It can reach about 20-30 frames per second. I think it is good enough for an emulator.</p>
<p align="justify">The next steps for qemu-omaps are: running linux kernel and distribution(Ångström for example). However the most exciting part is the 3D virtualization for OMAP3. That is you can run 3D application in qemu by using the host 3D accelerator hardware.</p>
<p align="justify">Please be patient. I am working on it.</p>
<p align="justify">
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/12/04/lcd-is-working-in-qemu-omap3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Qemu omap gp-timer bug</title>
		<link>http://vm-kernel.org/blog/2008/11/28/qemu-omap-gp-timer-bug/</link>
		<comments>http://vm-kernel.org/blog/2008/11/28/qemu-omap-gp-timer-bug/#comments</comments>
		<pubDate>Fri, 28 Nov 2008 11:48:39 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/11/28/qemu-omap-gp-timer-bug/</guid>
		<description><![CDATA[在u-boot中，采用函数udelay来进行延时。这是一个单纯等待的算法。在beagle board的u-boot中，udelay会不断去读OMAP general purpose timer的count值，然后根据general purpose timer的输入时钟来得到已经经过的时间。
在u-boot中，gp timer2的输入时钟为系统时钟，beagle board中为13MHZ。并且时钟经过了256分频。因此，每一秒钟的时间，gp timer2的count的值会增加1*(13MHZ/256)。
对于qemu来说，必须要保证当u-boot去读gptimer寄存器值的时候，返回的count值是正确的。也就是说，必须是实际经过的时间*13MHZ/256。
在qemu中，ticks_per_sec的值为1000000000。函数qemu_get_clock可以得到当前时间的ticks。因此，当u-boot来读gptimer寄存器值的时候，返回的count值应该为：

(qemu_get_clock/ticks_per_sec)*(13MHZ/256)

也就是

(qemu_get_clock*13MHZ)/(ticks_per_sec*256)

见函数omap_gp_timer_read

distance = muldiv64(distance, timer-&#62;rate, timer-&#62;ticks_per_sec);

这里，timer-&#62;ticks_per_sec= ticks_per_sec*256。
可是问题的关键在于，函数muldiv64的原型为

/* compute with 96 bit intermediate result: (a*b)/c */
uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)

也就是说，参数b和c都是32bit的。而timer-&#62;ticks_per_sec已经超过了32bit。导致distance的计算出错，后果是u-boot中的延时函数是不准的。
解决方法：
首先判断一下ticks_per_secis有没有超过32bit，然后根据情况分别计算。

/*if ticks_per_secis bigger than 32bit, we can not use muldiv64 anymore!*/
if (timer-&#62;ticks_per_sec&#62;0xffffffff){
  distance = distance/(ticks_per_sec/1000);  /*distance ms*/
  rate = timer-&#62;rate &#62;&#62; (timer-&#62;pre ? timer-&#62;ptv [...]]]></description>
			<content:encoded><![CDATA[<p align="justify">在u-boot中，采用函数udelay来进行延时。这是一个单纯等待的算法。在beagle board的u-boot中，udelay会不断去读OMAP general purpose timer的count值，然后根据general purpose timer的输入时钟来得到已经经过的时间。</p>
<p align="justify">在u-boot中，gp timer2的输入时钟为系统时钟，beagle board中为13MHZ。并且时钟经过了256分频。因此，每一秒钟的时间，gp timer2的count的值会增加1*(13MHZ/256)。</p>
<p align="justify">对于qemu来说，必须要保证当u-boot去读gptimer寄存器值的时候，返回的count值是正确的。也就是说，必须是实际经过的时间*13MHZ/256。<br />
在qemu中，ticks_per_sec的值为1000000000。函数qemu_get_clock可以得到当前时间的ticks。因此，当u-boot来读gptimer寄存器值的时候，返回的count值应该为：</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">(qemu_get_clock/ticks_per_sec)*(13MHZ/256)</pre>
</pre>
<p>也就是</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">(qemu_get_clock*13MHZ)/(ticks_per_sec*256)</pre>
</pre>
<p>见函数omap_gp_timer_read</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">distance = muldiv64(distance, timer-&gt;rate, timer-&gt;ticks_per_sec);</pre>
</pre>
<p>这里，timer-&gt;ticks_per_sec= ticks_per_sec*256。</p>
<p>可是问题的关键在于，函数muldiv64的原型为</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;"><span style="color: #008000;">/* compute with 96 bit intermediate result: (a*b)/c */</span></pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)</pre>
</pre>
<p>也就是说，参数b和c都是32bit的。而timer-&gt;ticks_per_sec已经超过了32bit。导致distance的计算出错，后果是u-boot中的延时函数是不准的。</p>
<p>解决方法：</p>
<p>首先判断一下ticks_per_secis有没有超过32bit，然后根据情况分别计算。</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;"><span style="color: #008000;">/*if ticks_per_secis bigger than 32bit, we can not use muldiv64 anymore!*/</span></pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;"><span style="color: #0000ff;">if</span> (timer-&gt;ticks_per_sec&gt;0xffffffff){</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">  distance = distance/(ticks_per_sec/1000);  <span style="color: #008000;">/*distance ms*/</span></pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">  rate = timer-&gt;rate &gt;&gt; (timer-&gt;pre ? timer-&gt;ptv + 1 : 0);</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">  distance = muldiv64(distance, rate, 1000);</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;"> }</pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;"><span style="color: #0000ff;">else</span></pre>
<pre style="margin: 0em; font-size: 12px; width: 100%; font-family: consolas,'Courier New',courier,monospace; background-color: #e9e9e9;">  distance = muldiv64(distance, timer-&gt;rate, timer-&gt;ticks_per_sec);</pre>
</pre>
<p>现在U-boot中的时间是准确的了。</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/11/28/qemu-omap-gp-timer-bug/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>qemu-omap3 信号丢失问题的解决</title>
		<link>http://vm-kernel.org/blog/2008/11/25/qemu-omap3-%e4%bf%a1%e5%8f%b7%e4%b8%a2%e5%a4%b1%e9%97%ae%e9%a2%98%e7%9a%84%e8%a7%a3%e5%86%b3/</link>
		<comments>http://vm-kernel.org/blog/2008/11/25/qemu-omap3-%e4%bf%a1%e5%8f%b7%e4%b8%a2%e5%a4%b1%e9%97%ae%e9%a2%98%e7%9a%84%e8%a7%a3%e5%86%b3/#comments</comments>
		<pubDate>Tue, 25 Nov 2008 04:54:59 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/11/25/qemu-omap3-%e4%bf%a1%e5%8f%b7%e4%b8%a2%e5%a4%b1%e9%97%ae%e9%a2%98%e7%9a%84%e8%a7%a3%e5%86%b3/</guid>
		<description><![CDATA[
经过几天的debug，在很多地方加入 判断是否有SIG pending的语句，结果都是没有信号pending。那么唯一的可能就根本没有产生SIGALRM信号！    继续追踪。Qemu中提供了很多中定时器的实现方法，见数组alarm_timers。

1: static struct qemu_alarm_timer alarm_timers[] = {

2: #ifndef _WIN32

3: #ifdef __linux__

4:     {&#34;dynticks&#34;, ALARM_FLAG_DYNTICKS, dynticks_start_timer,

5:      dynticks_stop_timer, dynticks_rearm_timer, NULL},

6:     /* HPET - if available - is preferred */

7:     {&#34;hpet&#34;, 0, hpet_start_timer, hpet_stop_timer, NULL, NULL},

8: [...]]]></description>
			<content:encoded><![CDATA[<p align="justify">
<p align="justify">经过几天的debug，在很多地方加入 判断是否有SIG pending的语句，结果都是没有信号pending。那么唯一的可能就根本没有产生SIGALRM信号！    <br />继续追踪。Qemu中提供了很多中定时器的实现方法，见数组alarm_timers。</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">1: <span style="color: #0000ff">static</span> <span style="color: #0000ff">struct</span> qemu_alarm_timer alarm_timers[] = {
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">2: #ifndef _WIN32
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">3: #ifdef __linux__
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">4:     {&quot;<span style="color: #8b0000">dynticks</span>&quot;, ALARM_FLAG_DYNTICKS, dynticks_start_timer,
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">5:      dynticks_stop_timer, dynticks_rearm_timer, NULL},
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">6:     <span style="color: #008000">/* HPET - if available - is preferred */</span>
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">7:     {&quot;<span style="color: #8b0000">hpet</span>&quot;, 0, hpet_start_timer, hpet_stop_timer, NULL, NULL},
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">8:     <span style="color: #008000">/* ...otherwise try RTC */</span>
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">9:     {&quot;<span style="color: #8b0000">rtc</span>&quot;, 0, rtc_start_timer, rtc_stop_timer, NULL, NULL},
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">10: #endif
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">11:     {&quot;<span style="color: #8b0000">unix</span>&quot;, 0, unix_start_timer, unix_stop_timer, NULL, NULL},
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">12: #<span style="color: #0000ff">else</span>
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">13:     {&quot;<span style="color: #8b0000">dynticks</span>&quot;, ALARM_FLAG_DYNTICKS, win32_start_timer,
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">14:      win32_stop_timer, win32_rearm_timer, &amp;alarm_win32_data},
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">15:     {&quot;<span style="color: #8b0000">win32</span>&quot;, 0, win32_start_timer,
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">16:      win32_stop_timer, NULL, &amp;alarm_win32_data},
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">17: #endif
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">18:     {NULL, }
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">19: };</pre>
</pre>
<p align="justify">
  <br />在初始化的时候，qemu会依次读取这个数组中所有的timer类型，只到某一个timer被初始化成功。在qemu-omap3中，dyntics会被初始化成功。也就是说，负责产生SIGALRM信号的就是dynticks。其启动timer和重新设置超时时间的是函数 dynticks_start_timer和dynticks_rearm_timer。</p>
<p align="justify">在dynticks_rearm_timer中会计算并设置超时时间，函数为qemu_next_deadline_dyntick。问题就在 qemu_next_deadline_dyntick中。</p>
<p align="justify">这个函数一开始计算virtualtime的超时时间，然后在存在 realtime的timer的时候，才计算realtime的超时时间，然后取两者最小值。关键在于当没有realtime的timer的时候，得到的 virtualtimer值过大，然后设置了这个很多的值作为下一次的超时时间。导致很长时间都不会出现超时信号。现在在函数 qemu_next_deadline_dyntick中增加了一个最大值的限制。</p>
<pre style="border-right: #cecece 1px solid; padding-right: 5px; border-top: #cecece 1px solid; padding-left: 5px; min-height: 40px; padding-bottom: 5px; overflow: auto; border-left: #cecece 1px solid; padding-top: 5px; border-bottom: #cecece 1px solid; background-color: #e9e9e9">
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">1: <span style="color: #0000ff">if</span> (delta&gt;MAX_TIMER_REARM_US)
</pre>
<pre style="font-size: 12px; margin: 0em; width: 100%; font-family: consolas,&#39;Courier New&#39;,courier,monospace; background-color: #e9e9e9">2:  delta = MAX_TIMER_REARM_US;</pre>
</pre>
<p align="justify">问题解决了。现在qemu-omap3可以接收键盘输入了。</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/11/25/qemu-omap3-%e4%bf%a1%e5%8f%b7%e4%b8%a2%e5%a4%b1%e9%97%ae%e9%a2%98%e7%9a%84%e8%a7%a3%e5%86%b3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>qemu中信号丢失</title>
		<link>http://vm-kernel.org/blog/2008/11/18/qemu%e4%b8%ad%e4%bf%a1%e5%8f%b7%e4%b8%a2%e5%a4%b1/</link>
		<comments>http://vm-kernel.org/blog/2008/11/18/qemu%e4%b8%ad%e4%bf%a1%e5%8f%b7%e4%b8%a2%e5%a4%b1/#comments</comments>
		<pubDate>Tue, 18 Nov 2008 04:53:28 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/11/18/qemu%e4%b8%ad%e4%bf%a1%e5%8f%b7%e4%b8%a2%e5%a4%b1/</guid>
		<description><![CDATA[那在qemu中，有一个gui timer会定时刷新gui的信息。现在gui的tile没有变化，就说明这个gui timer 根本没有被触发。
而在qemu中的定时器是通过函数host_alarm_handler来触发运行的，函数host_alarm_handler又是通过信号来进行触发的。那么问题的根源就是信号没有被发送或者被block(还没有查出具体原因)。
我尝试在终端中向qemu发生SIGALRM信号，果然，qemu接收到SIGALRM信号后，执行host_alarm_handler然后会执行所有注册了的定时器的超时函数(包括display_state.gui_timer)。
kill-bill: # ps aux &#124; grep qemu
root&#160;&#160;&#160;&#160;&#160; 2945&#160; 101&#160; 3.2 196468&#160; 8404 pts/1&#160;&#160;&#160; R+&#160;&#160; 09:36&#160;&#160; 0:14 ./qemu-system-arm -M beagle -mtdblock beagle-nand.bin
root&#160;&#160;&#160;&#160;&#160; 2947&#160; 0.0&#160; 0.2&#160;&#160; 2996&#160;&#160; 692 pts/0&#160;&#160;&#160; R+&#160;&#160; 09:36&#160;&#160; 0:00 grep qemu
kill-bill: # kill -s SIGALRM 2945

多次发送SIGALRM信号后，qemu可以正常接受终端的输入。下面是qemu运行u-boot的截图。

接下来就需要查找为什么qemu没有收到SIGALRM！
]]></description>
			<content:encoded><![CDATA[<p align="justify">那在qemu中，有一个gui timer会定时刷新gui的信息。现在gui的tile没有变化，就说明这个gui timer 根本没有被触发。</p>
<p align="justify">而在qemu中的定时器是通过函数host_alarm_handler来触发运行的，函数host_alarm_handler又是通过信号来进行触发的。那么问题的根源就是信号没有被发送或者被block(还没有查出具体原因)。</p>
<p align="justify">我尝试在终端中向qemu发生SIGALRM信号，果然，qemu接收到SIGALRM信号后，执行host_alarm_handler然后会执行所有注册了的定时器的超时函数(包括display_state.gui_timer)。</p>
<blockquote><p align="justify">kill-bill: # ps aux | grep qemu</p>
<p align="justify">root&#160;&#160;&#160;&#160;&#160; 2945&#160; 101&#160; 3.2 196468&#160; 8404 pts/1&#160;&#160;&#160; R+&#160;&#160; 09:36&#160;&#160; 0:14 ./qemu-system-arm -M beagle -mtdblock beagle-nand.bin</p>
<p align="justify">root&#160;&#160;&#160;&#160;&#160; 2947&#160; 0.0&#160; 0.2&#160;&#160; 2996&#160;&#160; 692 pts/0&#160;&#160;&#160; R+&#160;&#160; 09:36&#160;&#160; 0:00 grep qemu</p>
<p align="justify">kill-bill: # kill -s SIGALRM 2945</p>
</blockquote>
<p align="justify">多次发送SIGALRM信号后，qemu可以正常接受终端的输入。下面是qemu运行u-boot的截图。</p>
<p align="justify"><a href="http://lh5.ggpht.com/_vf_uV7bji2A/SSI_SCSiFmI/AAAAAAAAAGQ/1eym01yahqk/s1600-h/qemu%5B2%5D.jpg"><img height="156" alt="qemu" src="http://lh3.ggpht.com/_vf_uV7bji2A/SSI_S3IVKmI/AAAAAAAAAGU/buYzUck-FTI/qemu_thumb.jpg?imgmax=800" width="244" border="0" /></a></p>
<p align="justify">接下来就需要查找为什么qemu没有收到SIGALRM！</p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/11/18/qemu%e4%b8%ad%e4%bf%a1%e5%8f%b7%e4%b8%a2%e5%a4%b1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>First step of qemu-omap3</title>
		<link>http://vm-kernel.org/blog/2008/11/15/first-step-of-qemu-omap3/</link>
		<comments>http://vm-kernel.org/blog/2008/11/15/first-step-of-qemu-omap3/#comments</comments>
		<pubDate>Sat, 15 Nov 2008 04:52:49 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[OMAP]]></category>
		<category><![CDATA[emulation]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/11/15/first-step-of-qemu-omap3/</guid>
		<description><![CDATA[x-load 已经可以在qemu-omap3中运行起来。对x-load主要做了两个地方的修改:
1. 对判断外部输入时钟频率的部分，做了修改。x-load判断外部时钟频率的方法是先设置32KHZ的基准时钟，然后设置GP1的时钟源为SYS_CLK。然后读取GP1 Timer在20个cycle内的差值，进而判断时钟。这个方法在emulator中有的时候并不是很准。因为emulator除了执行指令外，还需要做其他事情。有的时候，emulator去读取GP1 Timer 的时候，实际上运行的时间已经不止ARM 20个cycle的时间，因此读取出来的counter值偏大。而x-load仍然认为刚才是跑了20个cycle。因此，得到的频率比实际的频率大。在 qemu-omap3中，目前认为外部时钟的频率是12MHZ13MHZ,因此，去掉了x-load中判断外部时钟频率的部分，在x-load中直接赋值外部时钟频率12MHZ13MHZ.
2. 去掉了nand flash中进入ECC检查的部分。也就是注释掉drivers/k9f1g08r0a.c中的宏ECC_CKECK_ENABLE


//57 #define ECC_CHECK_ENABLE



&#160;


下面是x-load运行的信息：


&#160;



# ./qemu-system-arm -M beagle -mtdblock beagle-nand.bin -serial stdio -nographic

Texas Instruments X-Loader 1.41

Starting OS Bootloader...



&#160;


运行到u-boot的时候还有点问题。go on working.

]]></description>
			<content:encoded><![CDATA[<p align="justify">x-load 已经可以在qemu-omap3中运行起来。对x-load主要做了两个地方的修改:
<p align="justify">1. 对判断外部输入时钟频率的部分，做了修改。x-load判断外部时钟频率的方法是先设置32KHZ的基准时钟，然后设置GP1的时钟源为SYS_CLK。然后读取GP1 Timer在20个cycle内的差值，进而判断时钟。这个方法在emulator中有的时候并不是很准。因为emulator除了执行指令外，还需要做其他事情。有的时候，emulator去读取GP1 Timer 的时候，实际上运行的时间已经不止ARM 20个cycle的时间，因此读取出来的counter值偏大。而x-load仍然认为刚才是跑了20个cycle。因此，得到的频率比实际的频率大。在 qemu-omap3中，目前认为外部时钟的频率是<strike>12MHZ</strike><font color="#ff0000">13MHZ</font>,因此，去掉了x-load中判断外部时钟频率的部分，在x-load中直接赋值外部时钟频率<strike>12MHZ</strike><font color="#ff0000">13MHZ</font>.
<p align="justify">2. 去掉了nand flash中进入ECC检查的部分。也就是注释掉drivers/k9f1g08r0a.c中的宏ECC_CKECK_ENABLE<br />
<blockquote>
<div align="justify">
<pre>//<a href="http://192.168.23.128/lxr/http/source/drivers/k9f1g08r0a.c#L57" name="L57">57</a> #define <a href="http://192.168.23.128/lxr/http/ident?i=ECC_CHECK_ENABLE">ECC_CHECK_ENABLE</a></pre>
</div>
</blockquote>
<div align="justify">
<pre>&nbsp;</pre>
</div>
<div align="justify">
<pre>下面是x-load运行的信息：</pre>
</div>
<div align="justify">
<pre>&nbsp;</pre>
</div>
<blockquote>
<div align="justify">
<pre># ./qemu-system-arm -M beagle -mtdblock beagle-nand.bin -serial stdio -nographic</pre>
</div>
<p align="justify">Texas Instruments X-Loader 1.41</p>
<div align="justify">
<pre>Starting OS Bootloader...</pre>
</div>
</blockquote>
<div align="justify">
<pre>&nbsp;</pre>
</div>
<div align="justify">
<pre>运行到u-boot的时候还有点问题。go on working.</pre>
</div>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/11/15/first-step-of-qemu-omap3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>opengl on qemu</title>
		<link>http://vm-kernel.org/blog/2008/10/27/opengl-on-qemu/</link>
		<comments>http://vm-kernel.org/blog/2008/10/27/opengl-on-qemu/#comments</comments>
		<pubDate>Mon, 27 Oct 2008 03:45:12 +0000</pubDate>
		<dc:creator>yajin</dc:creator>
				<category><![CDATA[emulation]]></category>

		<guid isPermaLink="false">http://vm-kernel.org/blog/2008/10/27/opengl-on-qemu/</guid>
		<description><![CDATA[讨论见这里：http://lists.gnu.org/archive/html/qemu-devel/2006-11/msg00144.html
Fabrice 的回复：http://lists.gnu.org/archive/html/qemu-devel/2006-11/msg00149.html
这里还有qemu 中3D加速的讨论：http://www.mail-archive.com/qemu-devel@nongnu.org/msg15702.html
]]></description>
			<content:encoded><![CDATA[<p align="justify">讨论见这里：<a href="http://lists.gnu.org/archive/html/qemu-devel/2006-11/msg00144.html">http://lists.gnu.org/archive/html/qemu-devel/2006-11/msg00144.html</a></p>
<p align="justify"><em>Fabrice </em>的回复：<a href="http://lists.gnu.org/archive/html/qemu-devel/2006-11/msg00149.html">http://lists.gnu.org/archive/html/qemu-devel/2006-11/msg00149.html</a></p>
<p align="justify">这里还有qemu 中3D加速的讨论：<a href="http://www.mail-archive.com/qemu-devel@nongnu.org/msg15702.html">http://www.mail-archive.com/qemu-devel@nongnu.org/msg15702.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://vm-kernel.org/blog/2008/10/27/opengl-on-qemu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
