LoginSignup
5
3

More than 5 years have passed since last update.

QEMUのRaspberry Pi 3モデルで割り込み

Last updated at Posted at 2018-05-25

QEMUのRaspberry Pi 3モデルで割り込みを動かしてみました。

QEMUのRaspberry Pi 2モデルで割り込み ができたので、 Raspberry Pi 3 aarch64 で試してみました。

QEMU 2.12を使いました。

ソースコード : https://github.com/eggman/raspberrypi/tree/master/qemu-raspi3/int01

割り込み動作トレース

QEMUを使ったト割り込み動作のトレースです。

$ make runasm
qemu-system-aarch64 -M raspi3 -m 128 -serial mon:stdio -nographic -kernel kernel.elf -d in_asm
...
IN: 
0x0008088c:  d65f03c0  ret      

----------------
IN: kernel_main
0x000818d4:  d503207f  wfi      

----------------
IN: 
0x00081080:  a9bf07e0  stp      x0, x1, [sp, #-0x10]!
0x00081084:  a9bf0fe2  stp      x2, x3, [sp, #-0x10]!
0x00081088:  a9bf17e4  stp      x4, x5, [sp, #-0x10]!
0x0008108c:  a9bf1fe6  stp      x6, x7, [sp, #-0x10]!
0x00081090:  a9bf27e8  stp      x8, x9, [sp, #-0x10]!

プログラムを起動して、キーを入力すると0x00081080 にあるIRQの割り込みベクタにジャンプします。

例外ベクタテーブル

aarch64の例外ベクタテーブルの構造

Address Exception Type Description
VBAR_ELn + 0x000 Synchronous Current EL with SP0
+0x080 IRQ/vIRQ 同上
+0x100 FIQ/vFRQ 同上
+0x180 SError/vSError 同上
+0x200 Synchronous Current EL with SPx
+0x280 IRQ/vIRQ 同上
+0x300 FIQ/vFRQ 同上
+0x380 SError/vSError 同上
+0x400 Synchronous Lower EL using AArch64
+0x480 IRQ/vIRQ 同上
+0x500 FIQ/vFRQ 同上
+0x580 SError/vSError 同上
+0x600 Synchronous Lower EL using AArch32
+0x680 IRQ/vIRQ 同上
+0x700 FIQ/vFRQ 同上
+0x780 SError/vSError 同上
  • 例外ベクタテーブルの一つのテーブルのサイズは128(0x80)バイト
  • 例外ベクタテーブルのサイズは2048(0x800)バイト。
  • 例外ベクタテーブルは2048バイトアラインした場所に配置する。
  • 例外ベクタテーブルの場所はそれぞれVBAR_EL3, VBAR_EL2、VBAR_EL1で指定する。 つまり3つある。
  • with SP0 と with SPxの違いはSPが切り替わらないプロセッサモードと、SPが切り替わるモードでベクタが異なる。
  • Lower EL..はよくわからない。

アセンブラで例外ベクタテーブルを記述

aarch64では例外ベクタテーブルの中に、ハンドラ処理を記述することができます。

.balign 2048
vector:
.balign 128
    b hang
.balign 128
    stp   x0,  x1,  [sp, #-16]!
    stp   x2,  x3,  [sp, #-16]!
    stp   x4,  x5,  [sp, #-16]!
    stp   x6,  x7,  [sp, #-16]!
    stp   x8,  x9,  [sp, #-16]!
    stp   x10, x11, [sp, #-16]!
    stp   x12, x13, [sp, #-16]!
    stp   x14, x15, [sp, #-16]!
    stp   x16, x17, [sp, #-16]!
    stp   x18, x19, [sp, #-16]!
    // call c handler.
    bl    c_irq_handler

    ldp   x18, x19, [sp], #16
    ldp   x16, x17, [sp], #16
    ldp   x14, x15, [sp], #16
    ldp   x12, x13, [sp], #16
    ldp   x10, x11, [sp], #16
    ldp   x8,  x9,  [sp], #16
    ldp   x6,  x7,  [sp], #16
    ldp   x4,  x5,  [sp], #16
    ldp   x2,  x3,  [sp], #16
    ldp   x0,  x1,  [sp], #16
    eret
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang

ベクターテーブルの位置を設定するレジスタVBAR_EL1レジスタにアドレスを設定する。

    // set vector address in EL1.
    ldr x0, =vector
    msr vbar_el1, x0 

割り込みの有効・無効

aach64では、割り込み有効にdaifclrレジスタ、割り込み無効にdaifetを使います。

IRQは1ビット目が対応するので、2をオペランドに渡しています。

.globl enable_irq
enable_irq:
    msr   daifclr, #2
    ret

.globl disable_irq
disable_irq:
    msr   daifset, #2
    ret

割り込みのルーティングについて

以前の記事 QEMUのRaspberry Pi 2モデルで割り込み で説明しています。

UARTの受信割みをCORE0にルーティングする設定をしました。

#define UART0_IMSC ((volatile unsigned int*)(0x3F201038))
#define GPU_INTERRUPTS_ROUTING ((volatile uint32_t *)(0x4000000C))
#define CORE0_INTERRUPT_SOURCE ((volatile uint32_t *)(0x40000060))

    // enable UART RX interrupt.
    *UART0_IMSC = 1 << 4;

    // UART interrupt routing.
    *IRQ_ENABLE2 = 1 << 25;

    // IRQ routeing to CORE0.
    *GPU_INTERRUPTS_ROUTING = 0x00;

割り込みハンドラ

割り込みハンドラはアセンブラとCで記述しました。 割り込みハンドラのアセンブラ部分は、例外テーブルのところで提示しました。

割り込みハンドラのC言語部分です。

割り込みハンドラの処理内容

  • まずCPUの割り込みを無効にする
  • 割り込み要因を上位の割り込みステータスから順々に確認しUARTからの割り込みだと判定する。
  • UART受信割り込みと判明したら、割り込み要因をクリアのために、UARTのデータレジスタをリードする。
  • 割り込みを要因をクリアしたので、CPUの割り込みを有効にする。
  • デバッグのために文字列を出力
void c_irq_handler(void)
{   
    char c;
    disable_irq();
    // check inteerupt source
    if (*CORE0_INTERRUPT_SOURCE & (1 << 8)) {
        if (*IRQ_PEND2 & (1 << 25)) {
            if (*UART0_MIS & (1 << 4)) { 
                c = (unsigned char) *UART0_DR; // read for clear tx interrupt.
                enable_irq();
                uart_putc(c);
                uart_puts(" c_irq_handler\n");
                return;
            }
        }
    }
    enable_irq();
    return;
}

割り込みでよくわからないところ

  • SPが切り替わる、SP切り替わらないの動作を確認してみた
SP_EL1 を 0x70000
SP     を 0x80000
に設定した時のスタックポインタ

EL2h の時のスタックポインタ
7FFF0
7FF30
割り込みでスタックポインタが切り替わらない

EL1t の時のスタックポインタ
7FFF0  main
6FF40  handler
割り込みでスタックポインタが切り替わる。
  • SPが切り替わるモード とSPが切り替わらないモードがあるが、どういいう意図で使い分ければ良いのか分からない。
    • 特権モードがアプリにも必要なシステムの場合、EL1h(カーネル) EL1t(アプリ)という組み合わせで使う。
    • XilinxのCortex-A53用のFreeRTOSの移植ではEL0は使わず、カーネルはEL1h, タスクはEL1tを使っている。
  • EL0はIRQでEL1になるが、EL1、EL2, EL3 は割り込みでもELは同じなのか?
  • 多重割り込み

参考情報

5
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3