qemu-omap3 信号丢失问题的解决

Nov 25th, 2008 | Posted by yajin | Filed under OMAP, emulation

经过几天的debug,在很多地方加入 判断是否有SIG pending的语句,结果都是没有信号pending。那么唯一的可能就根本没有产生SIGALRM信号!
继续追踪。Qemu中提供了很多中定时器的实现方法,见数组alarm_timers。

1: static struct qemu_alarm_timer alarm_timers[] = {
2: #ifndef _WIN32
3: #ifdef __linux__
4:     {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer,
5:      dynticks_stop_timer, dynticks_rearm_timer, NULL},
6:     /* HPET - if available - is preferred */
7:     {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL},
8:     /* ...otherwise try RTC */
9:     {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL},
10: #endif
11:     {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL},
12: #else
13:     {"dynticks", ALARM_FLAG_DYNTICKS, win32_start_timer,
14:      win32_stop_timer, win32_rearm_timer, &alarm_win32_data},
15:     {"win32", 0, win32_start_timer,
16:      win32_stop_timer, NULL, &alarm_win32_data},
17: #endif
18:     {NULL, }
19: };


在初始化的时候,qemu会依次读取这个数组中所有的timer类型,只到某一个timer被初始化成功。在qemu-omap3中,dyntics会被初始化成功。也就是说,负责产生SIGALRM信号的就是dynticks。其启动timer和重新设置超时时间的是函数 dynticks_start_timer和dynticks_rearm_timer。

在dynticks_rearm_timer中会计算并设置超时时间,函数为qemu_next_deadline_dyntick。问题就在 qemu_next_deadline_dyntick中。

这个函数一开始计算virtualtime的超时时间,然后在存在 realtime的timer的时候,才计算realtime的超时时间,然后取两者最小值。关键在于当没有realtime的timer的时候,得到的 virtualtimer值过大,然后设置了这个很多的值作为下一次的超时时间。导致很长时间都不会出现超时信号。现在在函数 qemu_next_deadline_dyntick中增加了一个最大值的限制。

1: if (delta>MAX_TIMER_REARM_US)
2:  delta = MAX_TIMER_REARM_US;

问题解决了。现在qemu-omap3可以接收键盘输入了。

Tags:

qemu中信号丢失

Nov 18th, 2008 | Posted by yajin | Filed under OMAP, emulation

那在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 | grep qemu

root      2945  101  3.2 196468  8404 pts/1    R+   09:36   0:14 ./qemu-system-arm -M beagle -mtdblock beagle-nand.bin

root      2947  0.0  0.2   2996   692 pts/0    R+   09:36   0:00 grep qemu

kill-bill: # kill -s SIGALRM 2945

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

qemu

接下来就需要查找为什么qemu没有收到SIGALRM!

Tags:

First step of qemu-omap3

Nov 15th, 2008 | Posted by yajin | Filed under OMAP, emulation

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
 
下面是x-load运行的信息:
 
# ./qemu-system-arm -M beagle -mtdblock beagle-nand.bin -serial stdio -nographic

Texas Instruments X-Loader 1.41

Starting OS Bootloader...
 
运行到u-boot的时候还有点问题。go on working.
Tags:

ARM和MIPS中Load 32位数据的差别

Nov 8th, 2008 | Posted by yajin | Filed under ARM/MIPS

ARM和MIPS都是Load-store结构,也就是说CPU只处理寄存器中的数据,而不会直接去处理memory中的数据。要想处理memory中的数据,必须先把memory中的数据load到寄存器中。

那如何load一个32bit的立即数到寄存器中呢?我们知道,ARM和MIPS的指令长度都是32bit,也就是说,不可能用一条指令来直接load一个32bit数据到寄存器中(如果可以的话,数据占了32bit,指令该如何编码?)

ARM中采用了ldr指令来实现这个功能。

LDR {cond} <Rd>,<addressing mode>

ldr 指令将一个memory中的数据load到寄存器Rd中。因此,如果需要load一个32bit立即数的话,先在memory中存放这个数据,然后调用 ldr指令来load这个数据到寄存器中。参考下面的代码中Line3。为了load 0x49020000到r0中,先在地址0x40202428中存入这个立即数,然后在调用ldr将这个数load到寄存器中。

1: 40202418 <serial_getc>:
2: 40202418:  e52de004   push  {lr}    ; (str lr, [sp, #-4]!)
3: 4020241c:  e59f0004   ldr  r0, [pc, #4]  ; 40202428 <serial_getc+0x10>
4: 40202420:  eb000042   bl  40202530 <NS16550_getc>
5: 40202424:  e49df004   pop  {pc}    ; (ldr pc, [sp], #4)
6: 40202428:  49020000   .word  0x49020000

而MIPS采用了不同的做法。MIPS中采用lui和addiu的方面来load 32bit数。指令lui是将一个16bit的数据load到寄存器的高16字节,然后再调用addiu加上32bit数据的低16bit。

1: 80000430:  3c02804b   lui  v0,0x804b
2: 80000434:  3c06804b   lui  a2,0x804b
3: 80000438:  24453474   addiu  a1,v0,13428 

如上面代码的Line1和Line3,实现了将32bit数据0x804b3474 load到寄存器a1的功能。

比较ARM和MIPS的两种实现方法,由于ARM多了一个memory 读的动作,因因此在时间效率中,MIPS要领先ARM。在空间效率上,两种方法都占用了8个字节。这一点上是相同的

Tags:

抢占式调度和休眠

Oct 29th, 2008 | Posted by yajin | Filed under kernel

在LDD3上有一个例子,代码如下:

1: /* Wait for space for writing; caller must hold device semaphore. On
2:  * error the semaphore will be released before returning. */
3: static int scull_getwritespace(struct scull_pipe *dev, struct file *filp)
4: {
5:
6:         while (spacefree(dev) == 0)
7:         { /* full */
8:                 DEFINE_WAIT(wait);
9:
10:                 up(&dev->sem);
11:                 if (filp->f_flags & O_NONBLOCK)
12:                         return -EAGAIN;
13:
14:                 PDEBUG("\"%s\" writing: going to sleep\n",current->comm);
15:                 prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);
16:                 if (spacefree(dev) == 0)
17:                         schedule();
18:                 finish_wait(&dev->outq, &wait);
19:                 if (signal_pending(current))
20:                 /* signal: tell the fs layer to handle it */
21:                         return -ERESTARTSYS;
22:                 if (down_interruptible(&dev->sem))
23:                         return -ERESTARTSYS;
24:         }
25:         return 0;
26:
27: }
28:

上面代码见LDD3 Page 159。

在书上面,解释了为什么需要Line 16的判断语句。因为在prepare_to_wait后,并没有真正让出CPU,只是改变了进程的状态。而这个时候,可能缓冲区又已经可用了(行10和 15之间read函数释放了缓冲区),那么我就不需要调用schedule让出处理器资源而休眠了。试想,如果不判断而调用schedule,而这个时候 read函数已经唤醒过了,那么谁来唤醒我呢?

可是仍然有一个疑问,因为2.6内核是可抢占的。那么调用假设prepare_to_wait和if判断之间也就是行15和16之间,发生了内核抢占,调度了另外的进程来运行。并且在行10和15之间,read 函数已经释放了缓冲区。那么由于不可能有进程来唤醒我,我就一直在休眠了!!!!!

实际上,这里就涉及到内核抢占的问题。在内核抢占的时候,schedule并不是被直接调用的,而是通过 preempt_schedule,preempt_schedule_irq和__cond_resched间接调用的,这三个例程都会在调用 schedule之前给当前的抢占计数加上PREEMPT_ACTIVE:

1: add_preempt_count(PREEMPT_ACTIVE);
2: ...
3: schedule();
4: ...
5: sub_preempt_count(PREEMPT_ACTIVE);

而schedule函数内部会检查PREEMPT_ACITVE位是否被设置,只有当此位没有被设置的时候才会将不是TASK_RUNNING状态的进程移除运行队列:

1:  if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
2:         switch_count = &prev->nvcsw;
3:         if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
4:                 unlikely(signal_pending(prev))))
5:             prev->state = TASK_RUNNING;
6:         else {
7:             if (prev->state == TASK_UNINTERRUPTIBLE)
8:                 rq->nr_uninterruptible++;
9:             deactivate_task(prev, rq);
10:         }
11:     }

因此,被抢占的进程始终会回到被抢占的点继续执行,无论它被抢占的时候状态为何。这样即使在行15和16之间发生抢占,该进程仍然在运行队列,下次仍然有机会被投入运行(被投入运行的条件不仅仅是被唤醒了,因为其在运行队列中)。因此,仍然会去执行下面的判断语句。在这篇邮件中也有相关的说明:

See the PREEMPT_ACTIVE logic.

If a task is preempted it is marked PREEMPT_ACTIVE and it skips the

runqueue removal logic in schedule(). So even if it is !TASK_RUNNING it

will run again.

You can see this in schedule() and preempt_schedule(), both in

kernel/sched.c.

        Robert Love

[参考]

1.http://blog.chinaunix.net/u/5251/showart_450988.html

2.http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-10/2391.html

Tags:

opengl on qemu

Oct 27th, 2008 | Posted by yajin | Filed under emulation

混合使用Git SVN和Git

Oct 24th, 2008 | Posted by yajin | Filed under Git

上一篇文章中,介绍了如何在google code中使用Git svn。现在又有了新问题。比如项目qemu-omap3是想往开源项目qemu中增加一个新的功能。qemu-omap3的项目hosting是在google code上面,使用Git svn来管理。而qemu在http://repo.or.cz/w/qemu.git,使用Git来管理。

为了开发qemu-omap3,需要从某一天的qemu中拿下来所有的源码,然后在这个基础上进行开发。但问题在于qemu的开发也同时在进行。这就需要qemu-omap3中的代码能保持和qemu中的同步。也就是说,我的需求如下:

1.  使用git svn来管理qemu-omap3

2. 能得到最新的qemu的代码

3. 最新的qemu的代码不影响我的qemu-omap3

之前解决的方法是定期从qemu中拿下所有的源码,然后在qemu-omap3中建立一个branch,然后拷贝过去,进行merage。这个过程比较繁琐。

实际上,可以通过git remote命令来增加一个新的remote repo。

git remote add qemu http://repo.or.cz/r/qemu.git

git fetch qemu

这会在git中自动建立一个branch qemu并且会将所有的qemu中的branch等全部放到qemu下。下面是目前我的branch。

kill-bill:/home/root/sdc/qemu/qemu-omap/svn# git-branch -a
* master
  qemu/master
  qemu/svn/perso
  qemu/svn/tags/initial
  qemu/svn/tags/release_0_5_1
  qemu/svn/tags/release_0_6_0
  qemu/svn/tags/release_0_6_1
  qemu/svn/tags/release_0_7_0
  qemu/svn/tags/release_0_7_1
  qemu/svn/tags/release_0_8_1
  qemu/svn/tags/release_0_8_2
  qemu/svn/tags/release_0_9_0
  qemu/svn/tags/release_0_9_1
  qemu/svn/trunk
  trunk

接下来需要merage 目前的master和qemu上的master。首先新建一个local branch

git merage qemu/master

这个命令会将qemu/master 和本地的branch merage。由于我们本地的branch是在master,因此会merage到master。如果有冲突出现,有提示。然后在修改,git-add 后,冲突就没有了。

以后可以通过git fetch qemu来获取qemu的最新代码。并且通过git merage来合并远程的qemu和本地的代码。不过有一点要注意,这样merage的不仅仅是代码,而且包括log!!

然后使用git svn dcommit提交到google code上。svn上的log只会是你自己的log,而不是merage过来的很多其他log。

Tags:

编译x-load

Oct 22nd, 2008 | Posted by yajin | Filed under OMAP

x-load本质是一个U-BOOT的精简版。为什么需要x-load这个玩意呢,而不是直接用u-boot呢?那是因为U-boot太大了,塞不进内部的RAM?那为啥要把X-LOAD塞进内部的RAM,而不是load到外部的RAM呢?

问题就在这里了。当OMAP3530上电的时候,memory controller还没有初始化,怎么去读写外部的RAM呢?必须要有人能先初始化memory controller啊。任务就交给x-Load了。它必须负责初始化外部的RAM控制器,把u-boot从NAND或者MMC中读出到外部RAM,然后跳到u-boot的入口处执行。

问题又来了,那么x-load又是由谁来load的呢?OMAP3530里面带了一个内部RAM,大小为64K。当OMAP3530上电后,会从NAND Flash或者MMC中读取x-load到内部的RAM。然后执行x-load,x-load然后执行上面所说的任务,最后把控制入口交给u-boot。

要编译x-load,先安装toolchain。然后

In file include/configs/omap3530beagle.h

/* For X-loader to be flashed on to NAND disable the below macro */
//#define CFG_CMD_MMC
Comiple the x-loader as shown below
make CROSS_COMPILE=arm-none-linux-gnueabi- distclean
make CROSS_COMPILE=arm-none-linux-gnueabi- omap3530beagle_config
make CROSS_COMPILE=arm-none-linux-gnueabi-

会生成x-loader.bin。然后用SignGP工具生成可以被OMAP3530 load的bin 文件。

./singGP x-load.bin

生成x-load.bin.ift。 这个文件和x-load.bin的区别在于增加了一个4个字节的header。header的内容是4个字节的x-loader的长度和x-load被load到什么地方的地址(4个字节)。

Tags:

在google code中使用git

Oct 15th, 2008 | Posted by yajin | Filed under Git

相比sf,googlecode的使用更加简单和方便。虽然在功能方面没有sf多,但是对于一个开源项目来说,基本上够用了。google code的功能有wiki,下载,svn,issue report。最近googlecode的svn中又增加了一个code review的功能,也就是说别人可以浏览SVN中代码并且留下comment。详细见google的介绍:http://code.google.com/p/support/wiki/CodeReviews

虽然google提供的SVN功能不错,但是我喜欢在本地用GIT来管理我的代码。之前我的使用方法是用svn co下来一份代码,然后git建一个本地仓库,所有的代码修改log都进入本地仓库。当完成一个功能后,用svn commit进googlecode。这样做的缺点在于本地git的log不能反应到svn中。也就是说svn中的log信息都是比较粗线条的,不能很细化的反应项目的变化情况。

于是就需要请出我们今天的主角:git-svn。首先在ubuntu中安装它。

sudo apt-get install git-svn

    安装完以后,需要checkout出google code svn中的东西来。

git-svn clone https://omap3emu.googlecode.com/svn -T trunk -b branches -t tags

这会在本地生成一个文件夹svn。里面就是google code svn中的东西。请记住,GIT的远端仓库和本地目录都是在一起的。我们来看一下目前的GIT仓库中有哪些branch。

Read more...

Tags:

device emulator V2中改进的地方

Oct 13th, 2008 | Posted by yajin | Filed under emulation

目前以shared source发布的device emulator的版本是v1,我手头的版本也就是这个版本。在MSDN的这篇文章中,Barry给出了device emulator的V2版本对于V1的改进的地方。通过这些改进,模拟速度的提升达到40%!

1. TLB 查找的优化

我们知道在带有MMU的ARM中,当访问memory的时候,需要先将虚拟地址转换成物理地址。这涉及到查TLB和页表的过程。如果能在TLB中找到表项,则不用去页表中进行查找。

在V1中,实现了TLB的查找。整个TLB的表项有64个。在硬件实现中,TLB的查找是并行的,而对于emulator来说,查找是线性的。最坏的情况需要遍历整个64个entry。因此,如果能够快速的寻找到TLB表项,无疑对于性能是一个很大的帮助。

根据程序的memory访问具有相关性(locality)的特点(也就是说,下一次访问的memory地址和上一次访问的memory地址不会相差太远),因此,在进行TLB查找的时候,总是从上一次查找成功的那个TLB Slot开始查找,而不是从TLB 表项0开始查找。

但这仍然有一个问题,最坏的情况下,仍然是线性搜索64个entry。V2做了一个改进,将线性的搜索变为hash搜索。这样,在最坏的情况下,不用遍历整个64个entry。不过具体的hash算法并没有给出。

2. Interrupt polling

这个也比较有意思。对于emulator来说,interrupt的产生是异步的,因此emulator是需要不断polling interrupt的状态。如果有interrupt pending,必须修改guest的PC,使得guest跳到异常处理程序的地方继续执行。

根据我之前开发emulator的经验,会在emulator的JIT生成的X86汇编中阶段性的去polling interrupt的状态。比如

if (g_fInterruptPending) {

SimulateHardwareInterrupt();

}

也就是说,JIT生成的X86代码需要非常多的MOV/CMP/BRANCH的序列。而在V2中,将interrupt polling 改成

void SimulateHardwareInterruptIfPending(void)

{ /* note: no code here */

}

这个函数是一个空函数。让有中断产生的时候,会patch这个函数,将这个函数变成RET到中断处理函数的地址。 这样就可以省掉MOV/CMP/BRANCH的序列。其实还可以减少translated后的代码大小。

3. 优化memcpy和memset.

emulator能识别出guest的memcpy和memset,把它变成手工优化的X86代码。
如何识别memcpy和memset,没有讲明。我想应该和所使用的 libc库以及compiler有关。

4. 窥孔优化
对IR进行窥孔优化。

str r0, [sp+10]
ldr r3, [sp+10]

变成

str r0, [sp+10]
mov r0, r3

5. 一次尽可能多的去ARM指令

也就是一次去指令的时候,至少应该可以取1K的指令。而不用每一次取指令的时候都去跑完整个memory load(guest虚拟地址->TLB->[页表->]guest物理地址->host虚拟地址->取指令)的整个过程。这个优化方法在dynamips和virtualmips中也有应用。

6. Reduce x86 processor stalls due to mixed code and data
这个应该是X86处理器相关的。不具体介绍了。

其实这些优化方面都不是很难,但是取得的性能提升确实显著的:40%。看来我要多学学怎么用工具来寻找程序的瓶颈,如何去优化。特别对于emulator来说,性能是非常非常关键的。

Tags: