Google Custom Search
THE LEADER IN HARDWARE/SOFTWARE CO-VERIFICATION EVE English Website

Linuxの起動

このデモでは(フラッシュバージョンはこちら)、組込みプロセッサコア上でLinuxを起動し、ZeBuエミュレータのソフトウェア、ハードウェアのビューを使ってデバッグに成功するところをご紹介します。

設定

SOCはTensilica Diamond 232Lプロセッサコアと、メモリ・サブシステムとUARTコントローラから構成されます。これはLinuxを起動するのに必要な最小構成です。ソフトウェアについては、Monta Vista社製のカーネル2.6ベースのLinuxディストリビューションを使用します。Linux起動時に必要な構成を作成するのにはinitramfsファイルシステムを利用し、コンパクトな形でLinuxコマンドを使うためにBusyBoxを採用します。
SOC全体はZeBu-UF 0.5でエミュレートします。

課題

プロセッサにLinuxイメージを読み込むと、コンソールの表示の通り、Linuxの起動が始まります。しかし、initプロセスがシェルを起動しようとする段階で、エラーメッセージが表示され、コンソールでの表示はそこで停止してしまいます。

 ZeBu-Diamond Console
 Linux version 2.6.10_mvl401-xt2000-xtensa_linux_le  
 (alain@treasure5.us.eve) (gcc version 3.4.3 
 (MontaVista 3.4.3-25.0.135.0702842  2007-03-23)) 
 #76 Tue May 1 11:02:33 PDT 2007
 On node 0 totalpages: 8192
 DMA zone: 8192 pages,  LIFO batch:2
 Normal zone: 0 pages,  LIFO batch:1
 HighMem zone: 0 pages,  LIFO batch:1
 Built 1 zonelists
 Kernel command line: console=ttyS0 mem=64M debug init=/sbin/init  root=/dummy
 PID hash table entries: 256 (order: 8, 4096 bytes)
 Console: colour dummy device 80x25
 Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
 Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
 Memory: 30936k/32768k available (606k kernel code, 1784k  reserved, 
 80k data, 528k init 0k highmem)
 Calibrating delay loop... 15.76 BogoMIPS (lpj=78848)
 Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
 spawn_desched_task(00000000)
 desched cpu_callback 3/00000000
 ksoftirqd started up.
 desched cpu_callback 2/00000000
 desched thread 0 started up.
 Linux NoNET1.0 for Linux 2.6
 Initializing Cryptographic API
 Serial: 8250/16550 driver $Revision: 1.90 $ 6 ports, IRQ sharing  disabled
 Registering platform device 'serial8250'. Parent at platform
 ttyS0 at MMIO 0x0 (irq = 4) is a ST16650
 ttyS1 at MMIO 0x0 (irq = 5) is a ST16650
 io scheduler noop registered
 io scheduler anticipatory registered
 io scheduler deadline registered
 io scheduler cfq registered
 loop: loaded (max 8 devices)
 mice: PS/2 mouse device common for all mice
 Freeing unused kernel memory: 528k freed
 Laun serial8250: too much  work for irq4

ドライバのソースコードの確認

コンソールにエラーメッセージが表示されているので、ソースコードはすぐに見つかります。drivers/serial/8250.cには次のようなコードがあります。

   static irqreturn_t serial8250_interrupt
   (int irq, void *dev_id,  struct pt_regs *regs)
   {
         struct irq_info *i  = dev_id;
         struct list_head  *l, *end = NULL;
         int pass_counter =  0, handled = 0;
 
          DEBUG_INTR("serial8250_interrupt(%d)...", irq);


          spin_lock(&i->lock);
 
         l = i->head;
         do {
                 struct  uart_8250_port *up;
                 unsigned  int iir;
 
                 up =  list_entry(l, struct uart_8250_port, list);
 
 #ifdef CONFIG_TSI108_BRIDGE /* for TSI108_REV_Z1 errata U2 */
                 /* read IIR  as part of 32-bit word */
                 iir = (in_be32((u32  *)(up->port.membase + UART_RX)) >> 8) & 0xff;
 #else
                 iir =  serial_in(up, UART_IIR);
 #endif
                 if (!(iir  & UART_IIR_NO_INT)) {
                          spin_lock(&up->port.lock);
                          serial8250_handle_port(up, regs);
                          spin_unlock(&up->port.lock);
                           handled = 1;
                          end  = NULL;
                 } else if  (end == NULL)
                         end  = l;
                  l =  l->next;
                 if (l ==  i->head && pass_counter++ > PASS_LIMIT) {

                         /*  If we hit this, we're dead. */
                          printk(KERN_ERR "serial8250: too much work for "
                                 "irq%d\n",  irq);
                          break;
                 }
         } while (l != end);

コードが示しているのは、ドライバは文字が送られる必要があるときには、それを扱うために割り込みが入るのをループしながら待つということです。デバイスからの割り込みがタイミング良く入らないと、ドライバはループを抜けて、さっきコンソールに表示されたエラーメッセージを出します。特に問題がありそうなのはUARTプロトコルの一部であるIIRレジスタ(割り込み要因レジスタ)です。

UARTの波形の抽出

次のステップとして、ハードウェアのデバッグに移り、ドライバからエラーが出された時点の周辺でのUARTコントローラの波形を抽出します。

ハードウェア・トリガ

最初の関門は、ハードウェア・サイクルの観点で、問題が発生する時期を合理的に推測することです。実際、Linux起動自体は10億サイクルの間続くので、その期間全体のDUTをトレースすることは合理的ではありません。問題の部分に素早く到達するために、ZeBuランタイム・ハードウェア環境の1機能であるハードウェア・トリガを使用します。エミュレータはプロセッサのPCが特定の値に等しくなるサイクル時点であればいつでも停止できます。ソフトウェアのブレークポイント(gdbなどのソフトウェアデバッガにある)とは違い、ハードウェア・トリガはプロセッサコアとそれ以外のSOC全体を両方とも停止させます。この機能はコアとそれ以外のSOC部位との間の割り込みや非同期イベントをデバッグするのに重要です。ハードウェア・トリガはVerilogシミュレータと同じように機能します。待機状態では時間が停止し、DUTの振舞いにまったく影響を与えません。

System.mapの使用

作業を簡単にするために、Linuxカーネルのコンパイル時に生成されるSystem.mapファイルを利用して、ドライバの関数名(serial8250_interrupt)とそのハードウェア・アドレスのマッピングを調べます。

      $ grep serial8250_interrupt System.map
d008769c t serial8250_interrupt

次に、Diamondプロセッサを積んだPCがそのアドレスに到達したときにZeBuを停止するトリガを設定します。仕事をさらに簡単にするために、zRunのGUIにTCLの行を少し追加し、関数名を直接入力できるようにして、さらにGUIが自動的にSystem.mapを検索して実際のハードウェアドレスを探し出すようにしました。
System.mapファイルを付随するアレイczrAddFromSymに解析したら、既存のzRunのTCLコードに次の2行(太字部分)を追加します。

   proc getDynamicTrigger { } {
   global logFile
   global selectedTrig ret  prog
   set prog  [ZEBU_Trigger_getExpr $selectedTrig]
   set ret  1
 
   set okCmd     { global ret
                   global  prog logFile
                   # use system.map
                   set simprog  "Diamond_PC\[31:0\]==$czrAddFromSym($prog)"
                   set ret  [ZEBU_Trigger_setExpr $selectedTrig "$simprog"]
                   destroy  .la.progDynaTrig
                 }

zRun GUIにフィールドをもう1つ追加して実行中のLinuxカーネルの関数名が継続的に表示されるようにします。カーネルの動作にしたがって関数名が変化していくのを眺めるだけでも参考になります。

linux-boot-demo

波形の取得

何か問題がありそうな場所でZeBuが停止したら、ダイナミックプローブを起動し、DUT内部の信号を標準の波形フォーマットにトレースします。今回のケースでは、UARTコントローラの信号をすべて、特にIIRレジスタと割り込みラインをトレースします。

linux-boot-demo_clip_image004.gif

波形を観ると、最初の数文字は実際正しくデータバスに送られていることがわかります。しかしその後、コントローラから割り込みが入らず、そのため文字が送られなくなります。これでドライバから出されたエラーメッセージの説明がつきます。

バグの修正

上記の情報から、UARTの仕様を再点検してみると、曖昧な点が見つかりました。UARTは自分のバッファが空のときにはいつでも割り込みを送るようになっています。しかし、バッファが空で、かつ空になった後にUARTが割り込みモードに入ったときに割り込みを発行するのかどうかが明確ではありませんでした。実際においては、英語の仕様書で、「バッファが空のときに割り込みを発行する」か、「バッファが空になったとき」なのかで、解釈の問題が発生していました。
期待されるソフトウェア・ドライバの動作と波形から考えて、バッファが空のときには毎回割り込みを発生させるようにUARTの動作を変更しました。

結果

UARTのロジックを変更した後には、コンソールには次のように表示されます。

 ZeBu-Diamond Console
 Linux version 2.6.10_mvl401-xt2000-xtensa_linux_le  
 (alain@treasure5.us.eve) (gcc version 3.4.3 
 (MontaVista 3.4.3-25.0.135.0702842  2007-03-23)) 
 #76 Tue May 1 11:02:33 PDT 2007
 On node 0 totalpages: 8192
 DMA zone: 8192 pages,  LIFO batch:2
 Normal zone: 0 pages,  LIFO batch:1
 HighMem zone: 0 pages,  LIFO batch:1
 Built 1 zonelists
 Kernel command line: console=ttyS0 mem=64M debug init=/sbin/init  root=/dummy
 PID hash table entries: 256 (order: 8, 4096 bytes)
 Console: colour dummy device 80x25
 Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
 Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
 Memory: 30936k/32768k available 
 (606k kernel code, 1784k  reserved, 80k data, 528k init 0k highmem)
 Calibrating delay loop... 15.76 BogoMIPS (lpj=78848)
 Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
 spawn_desched_task(00000000)
 desched cpu_callback 3/00000000
 ksoftirqd started up.
 desched cpu_callback 2/00000000
 desched thread 0 started up.
 Linux NoNET1.0 for Linux 2.6
 Initializing Cryptographic API
 Serial: 8250/16550 driver $Revision: 1.90 $ 6 ports, IRQ sharing  disabled
 Registering platform device 'serial8250'. Parent at platform
 ttyS0 at MMIO 0x0 (irq = 4) is a ST16650
 ttyS1 at MMIO 0x0 (irq = 5) is a ST16650
 io scheduler noop registered
 io scheduler anticipatory registered
 io scheduler deadline registered
 io scheduler cfq registered
 loop: loaded (max 8 devices)
 mice: PS/2 mouse device common for all mice
 Freeing unused kernel memory: 528k freed
 init started:  BusyBox  v1.4.2 (2007-04-26 04:18:24 PDT) multi-call binary
 Starting pid 15, console /dev/console: '/bin/hostname'
 Starting pid 16, console /dev/console: '/bin/login'
 zebudemo login: demo

 
 BusyBox v1.4.2 (2007-04-26 04:18:24 PDT) Built-in shell (ash)
 Enter 'help' for a list of built-in commands.

 $ pwd
 /home/demo
 $

Linuxの起動はシェルまでずっと進行しました。カーネル自身は2秒以内でZeBu-UF 0.5に読み込まれました。それからカーネルがディスクイメージを解凍してinitプロセスを実行するのにおよそ10秒要し、そこからログインプロセスとシェルが起動します。

結論

なぜUARTが正しく動作しないせいで、Linuxの起動は完了に近いところまで進行しながら、そこで止まってしまったのでしょうか?Linuxは2つのUARTドライバを使っていて、その1つ(serial8250_early)は起動の前半に、割り込みを使わずに文字を表示させますが、これは安全でした。もう1つは「実際の」UARTドライバですが、カーネルが初期化されたときにのみ有効になります。Linuxの起動が完了する前にハードウェアテストを停止し、前者のドライバだけを見ていたら、この問題は時間内に発見できなかったでしょう。その場合、テープアウト後のソフトウェアの修正により、I/O性能を犠牲にしてこの問題に対処していた可能性もあったでしょう。