Email me: jollen # jollen.org

more: Jollen 的 Embedded Linux 教育訓練

« March 2007 | (回到Blog入口) | May 2007 »

April 2007 歸檔

April 4, 2007

Embedded System 與使用者的互動:關於 Event-Driven 架構

關於 embedded system 的應用程式架構,最廣為人知的模型就是「event-driven」的做法。傳統的應用程式若是採用「結構化」的方式來設計,是否能適合 embedded system 的應用還需要進一步評估。

典型的 embedded system 裝置經常需要處理圖形顯示與使用者互動,通常使用者是透過像是 touch panel 或是 GPIO 的硬體介面與應用程式溝通,再由應用程式更新圖形顯示。

因此,有沒有任何做法,可以讓我們在 Linux 系統下以直觀的「事件」方式設計程式呢?經常看到的實作方式是使用 pthread 的方式來實作,透過 pthread 來產生多個執行緒,每個執行緒負責設定一個以上的事件;所有的事件都在 parent process 裡的 software loop 做判斷處理。這是最簡本的做法。

上述所提的 parent process 負責「case」(C 的 swith 敘述)所有的事件,並做出相對應的處理,這個 process 通常也被叫做 thread manager,或是 event manager。以一個簡單的 scenario 來說明此架構的應用。

小明想寫一個程式,當使用者按下 ARM9 開發板上的按鍵後,螢幕隨即出現一張圖片;這時,小明必須先定義「按鍵事件」的變數:

int EVENT_KEY_DOWN = 0x01;

我們必須產生一個 thread,來 "polling" 硬體上的按鍵,如果底層的驅動程式是 blocking read 的實作,那麼我們只需要一個簡單的 read() 即可。當 thread 判斷到按鍵被按下後,便將事件指定為 EVENT_KEY_DOWN;此時,您的系統應該有一個主程式(也就是 thread manager)在背景執行,並不停的做 loop-and-case 事件的動作:

while (1) {
   switch (EVENT) {
      case EVENT_KEY_DOWN: ... break;
      case ...
   }
}

許多軟體,像是 Nano-X,就是採取這種實作策略。不過,在特定只有「圖形顯示 v.s. 互動」的應用場合裡,這種架構並不容易寫出「互動性」良好的程式。(待續)

April 7, 2007

Embedded System 與使用者的互動:Event-Triggered or Not?

Event-Driven(或 Event-Trigger)的架構研究,通常伴隨嵌入式系統與控制的主題出現,我們以「嵌入式控制系統(Embedded Control Systems)」來泛指相關的研究主題。目前,在 embedded control system 的研究領域中,以 event-trigger 和 feedback scheduling 的技術為主;embedded control system 目前是 pervasive computing 的當紅題目。相關的研究,可在 [Feng XIA] 的網站中找到許多有用的 paper,建議可先由 "Event-Triggered Feedback Scheduling of Embedded Control Systems [1]" 此篇 paper 著手,以了解更多這方面的最新研究。

不過,我的目的並不是在了解 embedded control system 的設計理論,而是希望由相關的研究文獻中,找到一些適合「顯示與互動式」的嵌入式軟體架構。大略分享一下目前的初步研究如下。

互動是指使用者與機器的互動,而我想要的架構在於能簡化與有效設計「顯示(GUI)與互動(user interaction)」的軟體;其中,使用者的輸入或點擊(即互動),都可以表示成 event。因此,在人機界面的設計層,我們可以想辦法把整個人機界面表示成一組事件,並做細分。

根據 [1] 所提出的 event-triggered feedback scheduling structure 基礎架構,feedback scheduler 分為 event logic(time-triggered)與 scheduling algorithm(event-triggered);其中,time-trigger 是以一段固定的時間間隔(time interval)來觸發執行,並發出 event-trigger 請求給 scheduling algorithm。另外,此架構中,real-time control tasks 共用 CPU。

根據 [1] 的說明,feedback scheduler 的目的在於有效地排程 real-time control tasks,並且根據 [1] 的摸擬與結論得到,event-driven 的架構似乎能取得較佳的 real-time control 能力。以 event-driven 的基礎來做 real-time control 或許會比較容易規劃與測試整個系統,由於,我們只需要在特定事件被觸發(驅動)後,在規定的到期時間(deadline)內做完 control tasks 的工作即可達到軟即時(soft real-time)的要求,不管到期時間是長是短(microseconds, seconds or minutes),都不必像以往的 real-time system(or time-triggered)實作,必須強迫 task 在一個標準的時間內做完,因此,較容易實作出可接受的即時系統。

更進一步,甚致可以讓不同的 control tasks 定義不同的 deadline。目前,我針對相關的 event-driven & real-time control 理論架構與觀念,正設法簡化太過複雜的理論架構,或是太過華麗不切實際的理論基礎,希望能擬出一套精簡的 event-driven 的軟體架構。此架構的原則與目的與大家分享如下:

1. 必須夠簡單、精簡,程式碼要「少」;
2. 必須易於實作;
3. 適合應用在嵌入式系統的使用者互動軟體設計上;
4. Full user-space architecture, based-on POSIX threads;
5. 可以很容易套用,縮短「行前教育」時間;
6. 設法以「definition」或「generation」方式勾勒框架(framework),而不是以程式實作(implementation)方式寫框架。
7. 特定使用在顯示與互動的嵌入式軟體,所以改用 "acceptable time" 來取代 "real-time";希望換個 term 來區別二者的差異;
8. ...(more)

對於使用者互動與顯示的應用,其實比較容易設計基礎架構,但是我希望程式碼方面可以越簡化越好,所以決定 re-write 我的 prototype ;-)

參考資料

1. Feng Xia, and Youxian Sun: Event-Triggered Feedback Scheduling of Embedded Control Systems. Proc. 1st National Conference on Pervasive Computing, Kunming, China, Oct. 2005, pp.660-666.

April 8, 2007

關於 Acceptable Time 概念:運用在我的 Event-Driven 架構中

前一篇日記提到,「feedback scheduler」是 time-triggered 模式,但我所規劃的架構為「full user-space」的實作,因此,怎麼在 user-space 計算時間,便成為一個核心議題;考慮到我們所定義的「acceptable time」有別於真實的系統時間(clock),或是即時性(real-time),因此我定義了一個基本時間單位:T。

若下一個時間單位為 T2,則 T_interval = T2 - T1,也就是每個 T 時間可以取得一個真實的系統時間,即 T_interval,但這不是我們想要的「時間」;假設 T_interval = 10ms,那個在我的系統中,每個基本時間單位 T 會等於真實時間的 10ms

我們定義 TS(n) 為:每個事件到達後,一直到它的「control task」被「啟動」所需的花費時間(latency)。再定義 TD(n) 為:control task 啟動後,到執行結束所需要的時間長度。由於在這個系統裡,是不考慮真實時間的,只用「幾個單位」來表示所謂的「時間」,所以可以簡單將此系統的概念表示成:

TS(n) = n * T
TD(n) = n * T

這樣有什麼用呢?例如,TS(3) 表示某一事件到達後,需要 3 個時間單位(3 個 T)來啟動 control task;TD(15) 表示該 control task 執行需要 15 個 T。那麼,假設我們的 embedded Linux 系統有一個按鍵,按下後會出現目前電池用量資訊,若此按鍵的事件稱為 battery_key,便能根據使用經驗將 battery_key 的 TS(n) 定義為 100,寫成:

TS_battery_key(100)

也就是若 battery_key 使用上述的 "TS(n)" 系統,則 TS_battery_key(100) = TS(100) = 100T。換算成真實時間可能是 100*10ms = 1000ms,也就是 1 秒;我們定義按鍵按下到「開始出現畫面」的最長間隔是 1 秒,我們認為這是使用者視覺上可接受的時間,此外,對使用者來說,1 秒與 0.9 或是 0.8 秒感覺都是一樣的,所以我們用 "T",也就是「時間單位」的概念來陳述。

接著,要「秀好畫面」,也就是 control task 要做完整個工作的時間(execution time)如果是 150 個 T 的話,寫成 TD_battery_key,同樣也是使用與 TS_battery_key 相同的時間系統,那麼:


Turn around time of event 'battery_key' = TS_battery_key(100) + TD_battery_key(150) = T(100) + T(150) = 100T + 150T = 250T

我們「定義」整個動作在 300 個時間單位內完成都是可被「使用者接受」,或是「難以被使用者查覺」的,寫成 Taccept_battery_key(300),當然,acceptable time 也必須與上述二個時間使用相同的時間系統;最後得到,我的排程器必須設法做到:

Turn around time of event 'battery_key' <= Taccept_battery_key(300) = T(300)

對於在我的 event-driven 架構中所使用的 "acceptable time" 概念,先簡單介紹到這裡。(待續)

來源:Jollen's Blog

qemu-neo1973 / openmoko-emulator / jk2410-emulator

[OpenMoko] 將 [GTA01] 硬體模擬功能加入到 qemu 裡了。這是由 OpenMoko 與 OpenedHand 所貢獻的 patch,稱為 qemu-neo1973,可由 OpenMoko 的 svn 下載 [http://svn.openmoko.org/trunk/src/host/qemu-neo1973/]。此外,[OrzLab] 日前也提供一份 "openmoko-emulator" 的模擬器,這是 qemu-neo1973 的分支版本,由 OrzLab 負責維護,並且也對 qemu 本身做了瘦身的動作。其它訊息可延伸閱讀 OrzLab 的 blog:

OpenMoko/Neo1973硬體模擬

另外,我也將 OrzLab 所維護的 openmoko-emulator 另外再做了一個分支稱為 "jk2410-emulator",也就是 Jollen-Kit! 開發板(JK2410)的模擬器。以下是初步的模擬畫面,上圖是 LCD panel,下圖是 serial 畫面。

jk2410-emu-01.jpg

jk2410-emu-02.jpg

由於 Jollen-Kit! 與 OpenMoko 目前所採用的硬體(GTA01)都是基於 S3C2410A SoC,且週邊也有許多可共用之處,因此 jk2410-emulator 將會以 openmoko-emulator 做為分支(branch)對象。

關於 OpenMoko 模擬的部份,請參閱上述 OrzLab 的 Blog。感謝 OpenMoko 的精神與貢獻,這就是 open source / free software 令人驚奇與感動的地方。模擬器技術的進步,確實對 Embedded Linux 的系統發展與流程產生不少改變,甚致,對測試與除錯的方式也有相當大的貢獻。

April 10, 2007

分享一下最近修改 jk2410-emulator 的心得

來分享一下最近修改 jk2410-emulator 的心得,簡單紀錄重點,不過應該對有志研究 qemu 的 developer 有些啟發作用;OrzLab 有意舉辦 qemu 的讀書會,到時一定要跟大家多多請教,了解這個模擬器的明日之星。

對於 qemu internal 的研究,我還算是新手,幾天前從 jserv 那拿來了 openmoko-emulator 後,再參照 qemu-neo1973,做了一些小修改。最近在研究 qemu PC emulator,由於 qemu 可以研究的地方真的很多,像是:

- dynamic translator(解譯 target CPU opcode 的核心)
- accelerator(kqemu)
- translation cache
- MMU emulation
- Linux system call 的處理
- System emulator(vl)
- (...more)

所以如果不能結合大家的力量來 hacking 的話,的確還挺辛苦的。不過,其中 system emulator 的實作因為和週邊硬體的模擬有關,所以花了比較多時間在這個部份。以 S3C2410A 這顆 SoC 的模擬來說,target 是使用 'target-arm/' ,不過,由於官方發佈的 mainstram 版本似乎沒有定義 ARM920T 的 CPU ID,所以由 OpenMoko & OpenedHand 所實作的 'qemu-neo1973' 在 'target-arm/cpu.h' 加了相關定義:

#define ARM_CPUID_ARM920T 0x41129200

在 qemu system emulator 初始化「machine」時,會需要指定 CPU ID;其它的部份,像是 SoC 的 controller 部份,qemu-neo1973 已經對 qemu 註冊好 IO 與 memory 的資訊,對於 S3C2410A 這顆玻璃製品的支援算是完備。

最後,則是 machine 的模擬部份。Qemu system emulator 提供一個介面如下:

int qemu_register_machine(QEMUMachine *m)
{
QEMUMachine **pm; pm = &first_machine; while (*pm != NULL) pm = &(*pm)->next; m->next = NULL; *pm = m; return 0; }

可參考 hw/neo1973.c 的實作:

QEMUMachine neo1973_machine = {
    "neo",
    "Neo1973 phone aka FIC GTA01 aka OpenMoko (S3C2410A)",
    neo_init,
};

'Machine' 的註冊是寫在 system emulator 裡,即 vl.c,並透過 qemu-system-arm 的 '-M' 參數來指定所要模擬的 machine。關於 machine 的部份,雖然同樣是基於 S3C2410A 的 PCB,但是週邊與 GPIO 的規格還是有差異,因此 jk2410-emulator 必須修改此部份。

20070410-1.JPG

到這裡就只剩實作問題了,不過,由於 qemu-neo1973 並沒有完整模擬所有 S3C2410 controller 的行為(register read/write),所以可能要先自己再實作一小部份,或是等待 OrzLab 更新 openmoko-emulator

另外,順帶一提,qemu 的 ELF loader(elfload.c)是由 kernel 移植而來:

/* This is the Linux kernel elf-loading code, ported into user space */

可由 elfload.c:elf_exec() 看起。

April 11, 2007

小聊 qemu 的 CPUState

今天繼續來聊 qemu 的內部實作。在 qemu 裡頭,有一個 object 叫做 CPUState,這是一個重要的 qemu data structure,其實作如下(cpu-all.h):

#elif defined(TARGET_ARM)
#define CPUState CPUARMState
...

我們以 ARM 做為 target,再找到 target-arm/cpu.h

typedef struct CPUARMState {
    /* Regs for current mode.  */
    uint32_t regs[16];
    /* Frequently accessed CPSR bits are stored separately for efficiently.
       This contains all the other bits.  Use cpsr_{read,write} to access
       the whole CPSR.  */
    uint32_t uncached_cpsr;
    uint32_t spsr;

    /* Banked registers.  */
    uint32_t banked_spsr[6];
    uint32_t banked_r13[6];
    uint32_t banked_r14[6];
    
    /* These hold r8-r12.  */
    uint32_t usr_regs[5];
    uint32_t fiq_regs[5];
    
    /* cpsr flag cache for faster execution */
    uint32_t CF; /* 0 or 1 */
    uint32_t VF; /* V is the bit 31. All other bits are undefined */
    uint32_t NZF; /* N is bit 31. Z is computed from NZF */
    uint32_t QF; /* 0 or 1 */

    int thumb; /* 0 = arm mode, 1 = thumb mode */

    /* System control coprocessor (cp15) */
    struct {
        uint32_t c0_cpuid;
        uint32_t c0_cachetype;
        uint32_t c1_sys; /* System control register.  */
        uint32_t c1_coproc; /* Coprocessor access register.  */
        uint32_t c2; /* MMU translation table base.  */
        uint32_t c3; /* MMU domain access control register.  */
        uint32_t c5_insn; /* Fault status registers.  */
        uint32_t c5_data;
        uint32_t c6_insn; /* Fault address registers.  */
        uint32_t c6_data;
        uint32_t c9_insn; /* Cache lockdown registers.  */
        uint32_t c9_data;
        uint32_t c13_fcse; /* FCSE PID.  */
        uint32_t c13_context; /* Context ID.  */
        uint32_t c15_cpar; /* XScale Coprocessor Access Register */
    } cp15;

    /* Coprocessor IO used by peripherals */
    struct {
        ARMReadCPFunc *cp_read;
        ARMWriteCPFunc *cp_write;
        void *opaque;
    } cp[15];

    /* Internal CPU feature flags.  */
    uint32_t features;

    /* exception/interrupt handling */
    jmp_buf jmp_env;
    int exception_index;
    int interrupt_request;
    int user_mode_only;
    int halted;

    /* VFP coprocessor state.  */
    struct {
        float64 regs[16];

        uint32_t xregs[16];
        /* We store these fpcsr fields separately for convenience.  */
        int vec_len;
        int vec_stride;

        /* Temporary variables if we don't have spare fp regs.  */
        float32 tmp0s, tmp1s;
        float64 tmp0d, tmp1d;
        
        float_status fp_status;
    } vfp;

    /* iwMMXt coprocessor state.  */
    struct {
        uint64_t regs[16];
        uint64_t val;

        uint32_t cregs[16];
    } iwmmxt;

#if defined(CONFIG_USER_ONLY)
    /* For usermode syscall translation.  */
    int eabi;
#endif

    CPU_COMMON

} CPUARMState;

這個 object 是 qemu 用來紀錄 ARM 處理器狀態的資料結構,舉例來說,CPUState.regs 紀錄了 ARM 的所有 register 狀態。相關應用,例如,我們只要觀察 r15(pc)暫存器的值,就可以知道現在這台機器的程式執行位置。實際上的應用,像是 qemu 所實作的 gdbserver 即是透過此 object 的資訊,來回覆 gdb client 的命令。

April 16, 2007

ELF 之 Dynamic Linking 觀念教學, #1: 前言與簡介

接續前些時日的「Program Loading」專欄,本系列日 記將會介紹「dynamic linking」的核心觀念。首先,我們由 dynamic segment,即 .dynamic section 切入;如果您還不清楚為何以此做切入,建議先行閱讀 'Program Loading' 專欄,以建立基礎概念。

Dynamic section 是整個 dynamic linking 體 系的觀念核心,絕對不能放過;在 System V ABI 規格中,有詳細的 dynamic section 介紹。Dynamic section 裡頭包含多個 'entry',每一個 entry 紀載不同的資訊。首先,我們先找出 dynamic section 的資料結構定義如下:

/* Dynamic section entry.  */

typedef struct
{
  Elf32_Sword   d_tag;                  /* Dynamic entry type */
  union
    {
      Elf32_Word d_val;                 /* Integer value */
      Elf32_Addr d_ptr;                 /* Address value */
    } d_un;
} Elf32_Dyn;

'd_tag' 用來標記該 entry 的用途,'d_val' 或 'd_ptr' 則是該 entry 的內容。完整的 dynamic section 有哪些 entry?以及每一個 entry 的用途說明為何?此部 份請參閱 System V ABI 文件的說明,該文件有一份列表整理出所有的 dynamic section entry 及其說明,在此不再重覆刊載。不過,我們可由 elf.h 來快速了解 dynamic section 裡的主要(generic, processor-independent) entry:

/* Legal values for d_tag (dynamic entry type).  */

#define DT_NULL         0               /* Marks end of dynamic section */
#define DT_NEEDED       1               /* Name of needed library */
#define DT_PLTRELSZ     2               /* Size in bytes of PLT relocs */
#define DT_PLTGOT       3               /* Processor defined value */
#define DT_HASH         4               /* Address of symbol hash table */
#define DT_STRTAB       5               /* Address of string table */
#define DT_SYMTAB       6               /* Address of symbol table */
#define DT_RELA         7               /* Address of Rela relocs */
#define DT_RELASZ       8               /* Total size of Rela relocs */
#define DT_RELAENT      9               /* Size of one Rela reloc */
#define DT_STRSZ        10              /* Size of string table */
#define DT_SYMENT       11              /* Size of one symbol table entry */
#define DT_INIT         12              /* Address of init function */
#define DT_FINI         13              /* Address of termination function */
#define DT_SONAME       14              /* Name of shared object */
#define DT_RPATH        15              /* Library search path (deprecated) */
#define DT_SYMBOLIC     16              /* Start symbol search here */
#define DT_REL          17              /* Address of Rel relocs */
#define DT_RELSZ        18              /* Total size of Rel relocs */
#define DT_RELENT       19              /* Size of one Rel reloc */
#define DT_PLTREL       20              /* Type of reloc in PLT */
#define DT_DEBUG        21              /* For debugging; unspecified */
#define DT_TEXTREL      22              /* Reloc might modify .text */
#define DT_JMPREL       23              /* Address of PLT relocs */
#define DT_BIND_NOW     24              /* Process relocations of object */
#define DT_INIT_ARRAY   25              /* Array with addresses of init fct */
#define DT_FINI_ARRAY   26              /* Array with addresses of fini fct */
#define DT_INIT_ARRAYSZ 27              /* Size in bytes of DT_INIT_ARRAY */
#define DT_FINI_ARRAYSZ 28              /* Size in bytes of DT_FINI_ARRAY */
#define DT_RUNPATH      29              /* Library search path */
#define DT_FLAGS        30              /* Flags for the object being loaded */
#define DT_ENCODING     32              /* Start of encoded range */
#define DT_PREINIT_ARRAY 32             /* Array with addresses of preinit fct*/
#define DT_PREINIT_ARRAYSZ 33           /* size in bytes of DT_PREINIT_ARRAY */
#define DT_NUM          34              /* Number used */

本系列專欄將針對以 下幾個重要的 entry type 做觀念上的分享:

  • DT_NEEDED     1 /* Name of needed library */
  • DT_HASH          4 /* Address of symbol hash table */
  • DT_STRTAB      5 /* Address of string table */
  • DT_SYMTAB     6 /* Address of symbol table */

我們可使用 Linux 常見的 ELF 工具 - 'readelf' 來觀 察 ELF executables 的 dynamic segment 資訊:

# readelf -d test

Dynamic segment at offset 0x404 contains 20 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8048230
 0x0000000d (FINI)                       0x80483d0
 0x00000004 (HASH)                       0x8048128
 0x00000005 (STRTAB)                     0x80481a0
 0x00000006 (SYMTAB)                     0x8048150
 0x0000000a (STRSZ)                      75 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x80494e0
 0x00000002 (PLTRELSZ)                   16 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048220
 0x00000011 (REL)                        0x8048218
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x80481f8
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x80481ec
 0x00000000 (NULL)                       0x0

對於 dynamic linking 的說明,後續會先行整理這 4 個 entry 的主要觀念,然後再針對「如何找到程式所需(depends)的程式庫」做分析,並談論有關符號(symbol)的連結(relocation)處 理機制。

來源: Jollen's Blog

April 17, 2007

ELF 之 Dynamic Linking 觀念教學, #2: DT_NEEDED 基本概念

所謂「如何找到程式所需(depends)的程式庫」並非一件很技術面的事情,程式經常會呼叫程式庫裡的函數,並且由於 Linux 是 shared library 系統,因此 程式執行時,如何找到程式需要的程式庫,其實是一件很自然的事情。

程式所需的程式庫,稱為相依性程式庫,即 library dependencies;並且,程式庫也會再相依其他的程式庫,因此,找出 library dependencies 是一種拓樸邏輯關係。

由 dynamic linker 的實作角度來說,程式的 library dependencies 紀錄在 dynamic segment 的 DT_NEEDED entry 裡,dynamic linker 便是由 DT_NEEDED 為起點,建立 library dependencies 關係圖,此圖又稱做 NEEDED list。

例如,若程式相依 libm 與 libc,那麼,DT_NEEDED entry 的內容就會是:

# readelf -d test

Dynamic segment at offset 0x494 contains 21 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libm.so.6]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x80482a4
 0x0000000d (FINI)                       0x8048460
...

使用 ldd 也能觀察到 library dependencies:

# ldd test
        libm.so.6 => /lib/tls/libm.so.6 (0x40026000)
        libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

熟悉 embedded Linux 系統建置的朋友想必對這個部份不感到陌生,現在我們終於知道 library dependencies 是紀錄在 dynamic segment 的 DT_NEEDED 裡,對於 dynamic linking 的研究又跨出了一大步。

來源: Jollen's Blog

April 18, 2007

再聊 CPUState、qemu 的 gdbserver

前一則日記「小聊 qemu 的 CPUState」提到:

相關應用,例如,我們只要觀察 r15(pc)暫存器的值,就可以知道現在這台機器的程式執行位置。實際上的應用,像是 qemu 所實作的 gdbserver 即是透過此 object 的資訊,來回覆 gdb client 的命令。

以下展示實際的操作畫面。

下圖是利用 jk2410-emulator 模擬 Jollen-Kit! Pro. 的開機畫面,使用的 kernel 是 2.6.20.4:

在 kernel 檢查 root filesystem 是否為 initramfs 的時候,我們啟動了 gdb client,並透過 socket 連到 qemu 的 gdbserver;為了方便進行 source-level debug,我們使用 cgdb 來操作,下圖是 debug 畫面:

我們可以看到,此時 kernel 正執行到 lib/inflate.c 裡的第 59x 行程式,我們利用 next 做單步執行。這種「一邊跑 kernel,一邊看 kernel 跑到哪一個原始檔的哪一行」正是使用 qemu 做「整體系統」除錯的關鍵突破之一。

回到 qemu,找到 gdbserver 的實作 'gdbstub.c';當 host 的 gdb(gdb client)端對 target 端 gdb server 下命令時(透過 gdb remote protocol),qemu 實作的 gdb server 會先由 CPUState 讀取 register 的內容,再回應給 gdb client。gdb remote debug protocol 顯然不是我們目前的重點,所以先找到 'gdbstub.c' 關於「讀取 ARM 處理器 register」的主要程式片斷如下:

#elif defined (TARGET_ARM)
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
{
    int i;
    uint8_t *ptr;

    ptr = mem_buf;
    /* 16 core integer registers (4 bytes each).  */
    for (i = 0; i < 16; i++)
      {
        *(uint32_t *)ptr = tswapl(env->regs[i]);
        ptr += 4;
      }
    /* 8 FPA registers (12 bytes each), FPS (4 bytes).
       Not yet implemented.  */
    memset (ptr, 0, 8 * 12 + 4);
    ptr += 8 * 12 + 4;
    /* CPSR (4 bytes).  */
    *(uint32_t *)ptr = tswapl (cpsr_read(env));
    ptr += 4;

    return ptr - mem_buf;
}

gdb server 回傳給 host 端的資訊,已經由 host 端的 gdb 為我們解析,並根據 debug sections 來找到 $pc 對應的原始程式位置,即 source-level debug。

由於這種除錯模式,是對「整粒 ARM 處理器」做除錯,概念上很像是 jtag debug,因此會比用 kgdb 或 kdb 來得實用。原理上來看,gdb 單純是對 qemu 做除錯,而 qemu 是一個可以讓「完整 Linux 系統」在上面執行的 'system emulator'(for various processors),所以我們可以對模擬器裡頭的程式概念上「直接」進行除錯,因為對 host 端的 gdb 來說,這是通透的(transparent)。

再者,對除錯的二大重要工作:source-level debug 或是「單步執行」來說,qemu 單純只回應 $pc 給 gdb client,qemu 並不會知道現在跑的程式是什麼,同時也不需再加入(或修改)gdb stub 至 kernel 或 application,這樣的通透性,讓此種除錯模式可說是非常「萬能」的。不過,在實務上仍有一些不足的地方,例如,我們需要在 host 端編譯出具備除錯資訊的程式,才能搭配 qemu 來除錯,並且,也要小心注意 host 端的 loaded ELF image 與 run-time 的程式是否相同。

高通透性並非全然都是好的,例如,即使 loaded kernel image(vmlinux)與 run-time kernel 不同,我們還是會看到「目前跑到 source code 什麼地方」,因此目前的 source code 位置$pc 的位址是對不起來的。實務上,host gdb 透過 qemu 來除錯 kernel 的高度通透性,也帶來了一些小問題,有機會再做整理分享,或許可在 [OrzLab] 的讀書會上提出討論。

不過,如果是因為忘了重新打包 kernel,就直接跑 emulator 進行除錯,而發生上述的錯誤,實在是非戰之罪。

來源: Jollen's Blog

April 19, 2007

Qemu 模擬週邊的兩三事

這二天跟幾個朋友聊技術,大家似乎都對 qemu 如何模擬週邊硬體都很感興趣,特別是原理的部份,雖然我只簡單帶過幾句話,不過還是簡單紀錄一下,也能跟大家分享。

關於「qemu 怎麼模擬週邊硬體」,看似神秘,不過,先三不一沒有來澄清觀念:

- 不是模擬硬體的 waveform
- 不是模擬硬體的 timing
- 不是模擬硬體的 schematics
- 沒有對硬體的行為做完整模擬

那麼,qemu system emulator 倒底模擬週邊硬體什麼東西?

- 反應 driver (如 U-Boot 或 Linux device driver) 對 I/O port 或 I/O memory 的 read/write
- 保存與更新硬體狀態,並在 driver read/write I/O port 或 I/O memory 時做回應。

以上是最基本的二個觀念,在 qemu system emulator 的架構中,是透過 I/O registration 的方式達到。稍後我會補上較為明確的說明。

來源: Jollen's Blog

April 22, 2007

[教育訓練紀錄] Mmap and remap_page_range(), in the nutshell.

繼 Linux programming 課程紀錄「小談 mmap() 與 VMA」後,今天在 Linux device driver 課程再聊到有關 VMA 與 mmap driver function 的重要觀念;重點紀錄如下。

當 user-space 呼叫 mmap() system call wrapper function 後,kernel 會在 process address space 裡建立新的 VMA,並在 callback mmap driver function 時將「該」VMA 傳遞給我們的驅動程式。

因此,在驅動程式裡,只需要利用 remap_page_range() 將 kernel-space 的 memory:

- I/O memory

- RAM(reserved pages)

- Virtual address space(reserved pages)

對應到該 VMA 即可。最後,寫出了以下的 skeleton code:

// refer to: bttv-dirver.c
int do_card_mmap(struct vm_area_struct *vma, char *adr, unsigned long size)
{
    unsigned long start = (unsigned long)adr;
    unsigned long page, pos;

    pos = (unsigned long)BaseIOAddress;
#if 1  // page or the total size ?
    while (size >0) {
       page = pos;
       if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
	   return -EAGAIN;
       start+=PAGE_SIZE;
       pos+=PAGE_SIZE;
       size-=PAGE_SIZE;
    }
#else
    if (remap_page_range(vma, start, pos, size, PAGE_SHARED))
       return -EAGAIN;
#endif
    return 0;
}

// refer to: videodev.c
int card_mmap(struct file *filp, struct vm_area_struct *vma)
{
   do_card_mmap(vma, (char *)vma->vm_start, (unsigned long)(vma->vm_end-vma->vm_start));
   return 0;
}
/**************************************************/

struct file_operations card_fops = {
  open:card_open,
  release:card_release,
  ioctl:card_ioctl,
  mmap:card_mmap,
};

remap_page_range() 的原理是去修改 page table,但是要不要以 "page" 為最小單位做 page table 的修改(呼叫 'remap_page_range'),必須視情況而定!

同學對此部份如有觀念上的疑問,可透過 forum 討論。

來源: Jollen's Blog

[教育訓練紀錄] Spinlock in Process-Context Code

紀錄 Linux device driver 課程,關於「spinlock」的討論。

Kernel synchronization 的機制中,semaphore 有二種版本可供使用:

- Semaphore: down(), up()

- Spinlock

二種不同的 semaphore 實作的差異在於「P operation」的實作:

- down/up, 採取 sleeping 方式

- spinlock, 採取 busy loop 方式

在 interrupt handler 的實作上,由於 kernel 並非與 process context 做切換(kernel 執行的是 interrupt-context code),無法進行系統的 rescheduling,所以只能使用 spinlock 來進入 critical section。另外,我們提到,在 process-context mode 也常會看到 spinlock 的使用;在一般的 system call 實作函數裡,為什麼用 spinlock 而不是典型的 down/up 呢?道理在於,「如果 P operation 的等待時間相當短暫(semaphore holding 的時間很短),那麼使用 busy-loop 的方式便會比排程 task 為 INTERRUPTIBLE/UNINTERRUPTIBLE 狀態(sleep)來得更有效率」。

Linux 提供了三類的 spinlock API:

- IRQ don't care, 不對 local interrupt 做任何設定

- IRQ disabling, 將 local interrupt 設定為 disable

- IRQ save/restore, 儲存/還原 local interrupt 狀態

狀況一:當我們選擇在 non-interrupt mode 或是 process-context mode 使用 busy-loop 版本的 semaphore 時,若要確保 critical section 為 atomic operation,或是與 interrupt handler 做同步,此時就必須使用 IRQ disabling 的 spinlock,以確保 critical section 不會被 interrupt 打斷,且讓 processor 不會再接受任何的 interrupt。IRQ-disabling spinlock API 如下:

- spin_lock_irq

- spin_unlock_irq

同學可查閱課堂所發的補充資料,觀念上的疑問,可透過 forum 來詢問。

來源: Jollen's Blog

April 23, 2007

GNOME Mobile Platform

近期 Embedded Linux 的發展與應用,不斷出現許多新興社群;前幾日在 Embedded Linux Conference, Santa Clara, USA(April 19, 2007)上出現一則令人興奮的消息:

The GNOME Foundation announced today the creation of the GNOME Mobile & Embedded Initiative (GMAE), and a software platform for user experience development across a wide range of device profiles.

由 GNOME 所正式發佈的消息「THE GNOME FOUNDATION AND INDUSTRY LEADERS JOIN TO CREATE GNOME MOBILE & EMBEDDED INITIATIVE」。是的,創辦於 1990s 的知名 Linux 桌面環境計畫 GNOME 也正式參與嵌入式與行動裝置的開發領域了!

GNOME 是相當知名且具影響力的 Open Source /Free Software Project,如今 GNOME Project 正式成立 GNOME Mobile & Embedded Initiative 後,可預期 Embedded Linux / GUI 的發展與應用將出現重要的轉折。

GNOME Mobile & Embedded Initiative(GMAE)所提供的基礎架構自然是基於 GNOME 原本的桌面技術,目前,已經可以在 GMAE 網站上看到 GNOME Mobile Platform 的初步架構圖了!從技術面而言,GNOME 架構在 Xorg/Glib/D-Bus 的優雅環境之上,UI toolkit & widgets 採用 GTK+。

GNOME Mobile Platform 針對手機應用也嚴選出幾套卓越的 free software:

- BlueZ
- E-D-S
- Telepathy
- Avahi
- GStreamer
- Matchbox
- GConf
- GnomeVFS

許多 open source /free software 經過近十年(或是超過十年)的發展,終將邁向成熟之路,陸續看到一些重量級的軟體釋出重要的 milestone 版本,緊接著 embedded Linux hackers 也針對 embedded system 的應用為 open source / free software 進行客製化與調校,顯見 embedded Linux 的應用正快速擴張中,並且開發模式也在快速改變。Customization & Integration & Tuning 會是重要的工作之一(即平臺的觀念)。

GNOME Desktop 經過 customization/integration/tuning 成為 GNOME Mobile Platform 後,對 Linux 手機的開發再投下一枚震撼彈。從技術角度來看,許多發展中或是成熟的 handhelds 與 Linux mobile phone 軟體(例如:OpenMoko)專案非常多都是基於 GTK+,而 GMAE 的成立也對相關 project 產生相當大的助力:

- 強化 GTK+,為相關的手機專案打下穩定的基礎建設。
- 整合 GNOME desktop 至手機
- 以 GNOME Mobile Platform 為分支的商業性質 open source / free software project,能更健全地與 GNOME desktop 或是 GMAE developers 協同發展。
- GNOME Mobile Platform 基礎架構健全優雅(Xorg/Glib/D-Bus),讓整合與發展更快速。
- 更多、更多、更多的 community & developers

Open source / free software project 所提供的商業服務與支援,會是未來 Linux 手機軟體發展模式中的一個重要環節。

Jollen's Blog

April 26, 2007

全球最大 Embedded System 博覽會, Nov. 14-16, in Yokohama, Japan.

號稱全球最大的嵌入式系統博覽會 Embedded Technology Conference (ET2007),將於 Nov. 14-16 於日本 Yokohama 舉辦,今年將有 28,000 名參與人員出席。本年度 ET2007 博覽會的主軸如下:

- consumer electronics
- automotive electronics
- mobile/ubiquitous computing
- factory automation/robotics

此外,重點的技術議題包含:

- multicore processors
- WiMAX
- Linux
- real-time design
- testing strategies for complex embedded systems

現場將有 860 個攤位,展示的「產品」與「服務」涵蓋軟體、硬體、發展環境與開展工具,由於嵌入式系統的複雜度升高,再加上開發社群的興盛,嵌入式系統的開發模式將不再只是以「工具」和「產品」為主的技術,「技術服務(service)」也成為相當重要的設計環節。

詳細內容可參閱 LinuxDevices.com 的新聞 [Embedded technology show set for November in Yokohama]。ET2007 博覽會網站 [http://www.embeddedtech.net/ET/]。

April 30, 2007

充實的星期六, OpenMoko Day

最近聽到非常多與 Free / Open Source Software 有關的驚喜消息,特別與 Linux mobile phone 有關的新聞最讓我感到興趣。上週六 [jserv] 兄邀請我與 OpenMoko 創辦人餐聚,很榮幸再次與 Sean Moss-Pultz 會面。這次餐聚也見到了 Harald WelteRex,還有大眾電腦的幾位長輩;是一次非常有意義的餐聚。

在前往聚會地點的途中,我心中就暗自盤算了一個問題,打算向 Sean 請益。前幾天提到 GMAE 的成立,GMAE 對 Linux mobile phone 會是一個非常重要的 milestone;GNOME 發佈此消息後,不但吸引許多人的興趣與注意,對 UI 技術架構的發展也產生重要影響。

由於在 GMAE 的會員名單中看到了 OpenMoko,最令我感興趣的當然就是 GMAE 與 OpenMoko 的關係,也就是 OpenMoko 在 GMAE 裡所扮演的角色;餐會時沒有機會問,後來「續攤」時,便抓住時機向 Sean 請益。當然,最後得到令人滿意的答案。

當天 Harald 也分享許多有關 Free / Open Source Software 的觀念以及想法,也讓我重新架構了一些觀念,並得到一些啟發,這是令人興奮的收獲。傳說中只穿黑色衣服的 hacker,當天果然是一身黑衣打扮;不過,這種裝扮還挺適合 Harald 兄的,有一種傳教士的感覺。;-)

一個充實的週六!

附帶一提。不久前,jserv 兄才在 IRC 上跟我提到 Cacao for OpenMoko,很快地,今天在 OrzLab 已經看到 [當Java遇到OpenMoko] 的消息了,實在是太棒了。OpenMoko gets Java support!

關於 April 2007

此頁面包含了在April 2007發表於Jollen's Blog的所有日記,它們從老到新列出。

前一個存檔 March 2007

後一個存檔 May 2007

更多信息可在 主索引 頁和 歸檔 頁看到。

Top | 授權條款 | Jollen's Forum: Blog 評論、討論與搜尋
Copyright(c) 2006 www.jollen.org