aarch64でEL (Exception Level)を変更してみました。
あまり良く分かっていないのですが、aarch64でOSをブートするためにはExceptin Levelを変更する必要があります。
ELについて
EL : Exception Level 例外レベル
ARMv8では4つの例外レベルが定義されている。
EL | 説明 |
---|---|
EL0 | アプリ |
EL1 | OS |
EL2 | ハイパーバイザ |
EL3 | セキュアモニタ |
分かったこと
- Aarch32の User modeがEL0
- Aarch32のIRQ Mode, FIQ ModeがEL1
- ELの数値が下がるほど、アクセス可能なシステムレジスタが少なくなる。
- CPUはEL3で動作開始する。
- Linux起動時は事前にEL2/EL1に設定しておく。
Raspberry Pi 3Bについて
- kernel_old=1 で起動するとEL3で起動する。
- kernel_old=1 を指定しないと、EL2で起動する。
ELを確認する。
ELの数値をレジスタx0に代入するアセンブリコード。
mrs x0, CurrentEL
bit3とbit2がELの値でb、it31-4とbit1とbit0は0となる。
つまり、EL3なら0xC, EL2なら0x8, EL1なら0x4, EL0なら0x0となる。(だがEL0からはこのレジスタにアクセスできない。EL0の判定はどうやるのか?)
CurrentELの仕様は、ARMv8のアーキテクチャリファレンスマニュアル C5.2.1 にある。
ELを変更する。
ELを変更するアセンブリーコードです。
EL3 to EL2
EL3からEL2に変更するアセンブリコード。
#define MODE_AARCH64_EL2H 0x9
mov x0, #0x4b1 // RW=1, SMD=1, RES=1, NS=1
msr scr_el3, x0
adr x0, start_el2
msr elr_el3, x0
mov x0, #MODE_AARCH64_EL2H
msr spsr_el3, x0
eret
EL2に変更する例外を発生
- EL3の例外リンクアドレスレジスタ elr_el3,に、EL2に変更できた際の開始アドレス(start_el2)を設定する。
- EL3のセーブドプログラムステータスレジスタ spsr_el3に例外の戻り先のモードをEL2hに設定する。
とするとeretの後にELがEL3からEL2hに変更になる。
この時に指定するモードは同じELでも2種類あるEL2hとEL2tがある。EL2tだとEL変更後スタックポインタSP0を使う。
EL2hだとEL変更後はSP_EL2を使う
elr_el3の仕様は、ARMv8のアーキテクチャリファレンスマニュアル C5.2.7 にある。
spsr_el3の仕様は、ARMv8のアーキテクチャリファレンスマニュアル C5.2.20 にある。
EL2 to EL1
ほぼ同様です。
#define MODE_AARCH64_EL1H 0x5
mov x2, #(1 << 31) // AArch64
orr x2, x2, #(1 << 1) // SWIO hardwired on Pi3
msr hcr_el2, x2
mrs x0, hcr_el2
adr x0, start_el1
msr elr_el2, x0
mov x0, #MODE_AARCH64_EL1H
msr spsr_el2, x0
eret
EL1 to EL0
ほぼ同様です。
#define MODE_AARCH64_EL0 0x0
adr x0, start_el0
msr elr_el1, x0
mov x0, #MODE_AARCH64_EL0
msr spsr_el1, x0
eret
ERETによるEL変更
ERETでは数字が同じか小さいいELに変更できる
- EL3はEL3, EL2、EL1、EL0に変更できる。
- EL2はEL2, EL1、EL0に変更できる。
- EL1はEL1, EL0 に変更できる
数字が小さいELから大きいELに変更するためには、IRQ、FIQなどの外部割り込みを発生させるか、SVC, HVC, SMC命令を使う。
ELを変更する実行例
コードをQEMUで実行した場合のトレース。
- 1回目のeretの後にelr_el3に設定した0x30番地にジャンプしている。
- 2回目のeretの後にelr_el2に設定した0x54番地にジャンプしている。
- 3回目のeretの後にelr_el1に設定した0x68番地にジャンプしている。
0x00000010: 58000441 ldr x1, #0x98
0x00000014: d2809622 movz x2, #0x4b1
0x00000018: d51e1102 msr scr_el3, x2
0x0000001c: d2800122 movz x2, #0x9
0x00000020: d51e4002 msr spsr_el3, x2
0x00000024: 10000062 adr x2, #0x30
0x00000028: d51e4022 msr elr_el3, x2
0x0000002c: d69f03e0 eret
0x00000030: d2b00002 movz x2, #0x8000, lsl #16
0x00000034: b27f0042 orr x2, x2, #2
0x00000038: d51c1102 msr hcr_el2, x2
0x0000003c: d53c1100 mrs x0, hcr_el2
0x00000040: d28000a2 movz x2, #0x5
0x00000044: d51c4002 msr spsr_el2, x2
0x00000048: 10000062 adr x2, #0x54
0x0000004c: d51c4022 msr elr_el2, x2
0x00000050: d69f03e0 eret
0x00000054: d2800002 movz x2, #0
0x00000058: d5184002 msr spsr_el1, x2
0x0000005c: 10000062 adr x2, #0x68
0x00000060: d5184022 msr elr_el1, x2
0x00000064: d69f03e0 eret
0x00000068: 9100003f mov sp, x1
このトレースはqemuの-d in_asm で出力しました。この機能は便利です。
参考にしたページ
- Switch between exception level 3 to exception level 2
- ARMv8_Overview.pdf