
このラボでは、カスタムプログラムを実行する機能を実装します。 つまり プロセスおよびすべての依存インフラストラクチャ。 最初に、特権コードから切り替える方法、プロセスコンテキストを切り替える方法を理解します。 次に、単純なラウンドロビンスケジューラ、システムコール、および仮想メモリ管理を実装します。 最後に、シェルをカーネル空間からユーザー空間に移動します。
有用性
- Rust v2を予約します。 このコースの一部として必要なRustに関するすべての情報。
- Rust標準ライブラリドキュメント
- BCM2837ドキュメント BCM2837を修正したBCM2835ドキュメントの修正。
- ARMv8リファレンスマニュアル ARMv8アーキテクチャリファレンスガイド これは、アーキテクチャ全体を網羅した完全なガイドです。 特定のアーキテクチャの実装については、ARM Cortex A53マニュアルを参照してください。
- ARM Cortex-A53マニュアル ARMv8アーキテクチャ(v8.0-A)の特定の実装ガイド。 それはラズベリーで使用されているものです。
- ARMv8-Aプログラマーズガイド 。 高レベルARMv8-Aプロセスプログラミングガイド。
- AArch64手続き呼び出し標準
AArch64アーキテクチャの標準標準手順。 - ARMv8 ISAチートシート 。 このラボで提供されるARMv8ビルド手順の簡単な説明。 グリフィンディーツによって作成されました。
フェーズ0:はじめに
前の部分と同様に、保証された作業には以下が必要です。
- 最新のUnix:Linux、BSDまたはmacOSを搭載したマシン。
- 64ビットOS。
- USBポートの存在。
- 以前のリリースからインストールされたソフトウェア。
コード検索
カブ3-spawn
では質問以外の何物もありませんが、誰も邪魔しません:
git clone https://web.stanford.edu/class/cs140e/assignments/3-spawn/skeleton.git 3-spawn
その後、役に立たないため、ディレクトリ構造は次のようになります。
cs140e ├── 0-blinky ├── 1-shell ├── 2-fs ├── 3-spawn └── os
しかし、 os
rep内では、 3-spawn
ブランチに切り替える必要があり3-spawn
。
cd os git fetch git checkout 3-spawn git merge 2-fs
ほとんどの場合、再びマージの競合が発生します。 このようなもの:
Auto-merging kernel/src/kmain.rs CONFLICT (content): Merge conflict in kernel/src/kmain.rs Automatic merge failed; fix conflicts and then commit the result.
マージの競合は、 kmain.rs
ファイルを変更して手動で解決する必要があります。 この場合、Lab 2からすべての変更を保存したことを確認する必要があります。競合を解決したら、 git add
ファイルを追加して、すべてをコミットします。 このトピックに関する詳細情報を取得するには、 githowto.comのチュートリアルを参照してください 。
ARMドキュメント
この課題では、3つの公式のARMドキュメントを常に参照します。 これら3つは:
- ARMv8リファレンスマニュアル
これは、ARMv8アーキテクチャの公式リファレンスガイドです。 アーキテクチャ全体を網羅したワンストップガイド。 ラズベリーのプロセスでこのアーキテクチャを具体的に実装するには、マニュアル2が必要です。 この大きなARMv8マニュアルのセクションは、次の形式の注釈を使用して参照します( ref :C5.2)。 この場合、これは、セクションC5.2のARMv8リファレンスマニュアルを参照する必要があることを意味します。 - ARM Cortex-A53マニュアル
これは、ロビンで使用されるARMv8(v8.0-A)の非常に具体的な実装のためのマニュアルです。 このマニュアルでは、フォームの注記( A53 :4.3.30)を参照します。 - ARMv8-Aプログラマガイド
これで、ARMv8-Aをプログラミングするためのかなり高度なマニュアルができました。 フォームのメモでそれを参照します( ガイド :10.1)
これらのマニュアルをディスクにダウンロードすることを強くお勧めします。 そのため、毎回簡単に開くことができます。 特に最初のものは非常に大きいためです。 そういえば。
どうやって読むの? 全体を読む必要はありません。 したがって、初心者にとっては、このマニュアルで何を見つけたいかを知ることが非常に重要です。 このマニュアルは、使いやすい構造になっています。 いくつかの部分に分かれています。 AArch64に興味があり、あまり深く潜ることには興味がありません(プロセッサメーカーではありません)。 ですから、私たちはこの言葉の多くの章に全く興味がありません。 実際、パートA、B、およびCとDの一部の情報で十分ですが、最初の2つのパートでは、アーキテクチャ、特にAArch64に関する一般的な概念を説明します。 パートCでは一連の指示について説明します。 この部分は、最も基本的な命令とレジスタのリファレンスとして使用します(たとえば、SIMDは今は興味がありません)。 パートDでは、AArch64の詳細の一部について説明します。 特に、割り込みとそのすべてについて。
フェーズ1:ARMとレッグ(アームとレッグ)
このフェーズでは、ARMv8アーキテクチャを検討し、特権レベルを下げて、プロセッサ例外ベクトルを構成し、タイマー割り込みとブレークポイント割り込みを処理します。 ARMアーキテクチャの例外レベルを調べてみましょう。 私たちは、これらのまさに例外と中断をキャッチする方法に主に興味があります。
サブフェーズA:ARMv8レビュー
このサブフェーズでは、ARMv8のアーキテクチャを検討します。 ここではコードを記述しませんが、セルフテストには質問があります。
ARM(Acron RISC Machine)は、30年以上の歴史を持つマイクロプロセッサアーキテクチャです。 現在、このアーキテクチャには8つのバージョンがあります。 最新のARMv8は2011年に導入されました。 BroadcomのBCM2837チップには、ARMv8.0ベースのコアであるARM Cortex-A53コアが含まれています。 Cortex-A53(など)は、アーキテクチャの実装です 。 そして、これがこのすべての部分で学習する実装です。
ARMマイクロプロセッサがモバイル市場を支配しています。
ARMは、世界のスマートフォン市場の約95%、主力スマートフォンの100%です。 Apple iPhoneまたはGoogle Pixelを含む。
これまでのところ、プロセッサアーキテクチャを回避しようとしています。 Rustがすべてを行ってくれました。 ユーザー空間でプロセスを運用するには、ある程度の作業を低レベルで行う必要があります。 プロセスのプログラミングには、このアーキテクチャのアセンブラーとそれに関連するすべての関連概念に精通する必要があります。 アーキテクチャのレビューから始め、最も基本的なアセンブリ手順を扱います。
登録
ARMv8アーキテクチャには、次のレジスタがあります( ref :D1.2.1):
-
r0
...r30
ビット汎用レジスタ。 レジスタへのアクセスは、仮名(エイリアス)によって実行されます。 レジスタx0
...x30
は、64ビットバージョン(つまり、完全なバージョン)のエイリアスです。 エイリアスw0
...w30
があります。 後者は、レジスタの下位32ビットにアクセスします。 -
lr
は64ビットの参照レジスタです。x30
エイリアス。 遷移アドレスを保存するために使用されます。bl <addr>
命令は、現在の命令カウンター(PC)をlr
保存し、addr
アドレスに移動します。ret
ステートメントはその逆を行います。 彼女はアドレスをlr
から取得してPCに割り当てます。 -
sp
はスタックポインターです。 下位32ビットはwsp
によって利用可能です。 スタックポインターは常に16バイトで整列する必要があります。 -
pc
ソフトウェアカウンター。 このレジスタは直接書き込むことはできませんが、読み取ることはできます。 割り込みが呼び出されたとき、返されたときに、遷移命令で更新されます。 -
v0
...v31
ビットSIMDおよびFPレジスタ。 これらは、ベクトルSIMD操作および浮動小数点操作に使用されます。 これらのレジスタはエイリアスで利用できます。q0
...q31
レジスタの128ビットすべてのエイリアス。d0
...d31
は下位64ビットです。 これに加えて、接頭辞s
、h
、およびb
それぞれ対応する下位32ビット、16ビット、および8ビットのエイリアスがあります。 -
xzr
はゼロケースです。 これは擬似レジスタであり、ハードウェアレジスタである場合とそうでない場合があります。 常に0
が含まれ0
。 このレジスタは読み取りのみ可能です。
さらに多くの特別な目的のレジスタがあります。 それらについては少し後で説明します。
プステート
任意の時点で、パーセントARMv8を使用すると、PSTATEという名前の擬似レジスター( ref :D1.7)を介してプログラムの状態にアクセスできます。 これは通常のレジスタではありません。 直接読み書きすることはできません。 代わりに、PSTATE擬似レジスタの一部を操作するために使用できるいくつかの専用レジスタがあります。 ARMv8.0では、これは次のとおりです。
-
NZCV
ステータスフラグ -
DAIF
例外のビットマスク。これらの例外をオンまたはオフにするために使用されます -
CurrentEL
例外の現在のレベル(後述) -
SPSel
スタックポインターセレクター(実際にはいくつかあります)
このようなレジスタは、システムまたは特殊レジスタのクラスに属します( 参照 :C5.2)。 通常のレジスタは、 ldr
を使用してRAMからldr
か、 str
を使用してメモリに書き込むことができstr
。 システムレジスタをそのように使用することはできません。 代わりに、特別なコマンドmsr
およびmsr
( ref :C6.2.162-C6.2.164)が必要です。 たとえば、 x1
NZCV
を読み取るには、次のレコードを使用する必要があります。
mrs x1, NZCV
実行状況
常に、ARMv8パーセントが特定の実行状態で実行されます。 合計で、このような状態は正確に2つあります。 AArch32-32ビットARMv7との互換モード。 そして、AArch64-64ビットARMv8モード( ガイド :3.1)。 AArch64でのみ動作します。
セーフモード
ある時点で、特定のセキュリティ状態でパーセントが実行されます(ガイド:3)。 このガベージは、セキュリティモードまたはセキュリティワールドでも検索できます。 セキュアと非 セキュアの 2つの状態のみ。 つまり 安全かつ正常。 完全に通常モードで動作します。
例外レベル
これに加えて、例外レベルもあります( ガイド :3)。 各例外レベルは、特定の特権レベルに対応しています。 例外レベルが高いほど、そのレベルで実行されているプログラムが受け取る特権が多くなります。 合計で4つのレベルがあります。
- EL0(ユーザー) -通常、カスタムプログラムの実行に使用されます。
- EL1(カーネル) -特権モード。 通常、オペレーティングシステムのカーネルはここで起動されます。
- EL2(ハイパーバイザー) -通常、仮想マシンのハイパーバイザーを実行するために使用されます。
- EL3(モニター) -一般に低レベルのファームウェアに使用されます。
Raspberry PiプロセッサがEL3で起動します。 この時点で、Raspberry Pi Foundationが提供するファームウェアが起動します。 ファームウェアはプロセッサーをEL2に切り替え、kernel8.img kernel8.img
を起動しkernel8.img
。 したがって、カーネルはEL2レベルから始まります。 少し後、EL2からEL1に切り替えて、カーネルが適切なレベルの例外で動作するようにします。
ELxレジスタ
ELR
、 SPSR
SP
などの多くのシステムレジスタは、例外レベルごとに複製されます。 同時に、接尾辞_ELn
が名前に付けられますn
は、このレジスタが属する例外のレベルです。 たとえば、 ELR_EL1
はEL1レベルの例外参照レジスタであり、 ELR_EL2
は同じですが、EL2レベルです。
例外x
ターゲットレベルからレジスタを参照する必要がある場合は、サフィックスx
(たとえばELR_ELx
)を使用します。 ターゲット例外レベルは、例外ベクトルが開始されたときにCPUが(必要に応じて)切り替える例外レベルです。
例外s
初期レベルでレジスタを参照する必要がある場合、たとえばSP_ELs
で接尾辞s
を使用します。例外s
初期レベルは、例外が発生する前にCPUが実行された例外のレベルです。
例外レベルを切り替える
除外レベルを上げるメカニズムは1つ、除外レベルを下げるメカニズムは1つです。
上位レベルから下位レベルに切り替えるには(特権の削減)、実行中のプログラムは、 eret
( ref :D1.11)を使用してこのレベルの例外から復帰する必要があります。 ELx
レベルのプロセッサでeret
を実行する場合:
- PCを特殊レジスター
ELR_ELx
からの値に設定します。 - PSTATEを特殊レジスター
SPSR_ELx
からの値に設定します。
レジスタSPSR_ELx
( ref :C5.2.18)は、とりわけ、あなたが行かなければならない例外のレベルを含んでいます。 さらに、例外レベルを変更すると、次の追加の結果に注意する価値があります。
-
ELs
に戻るとき、SPSR_ELx[0] == 1
場合はSPSR_ELx[0] == 1
設定され、SPSR_ELx[0] == 1
場合はSPSR_ELx[0] == 1
設定されSPSR_ELx[0] == 0
。
下位レベルから上位レベルへの移行は、除外の結果としてのみ発生します( ガイド :10)。 特に設定しない限り、パーセントは次のレベルの例外をキャッチします。 たとえば、EL0での動作中に割り込みを受信した場合、パーセントは例外を処理するためにEL1に切り替わります。 ELx
パーセントに切り替えると、次のことが行われます。
- すべての例外と割り込みをオフ(マスク)にします:
PSTATE.DAIF = 0b1111
。 -
PSTATE
およびすべてをSPSR_ELx
保存します。 - 戻りアドレスを
ELR_ELx
保存します( ref :D1.10.1)。 -
SPSel
が1
場合、sp
をSP_ELx
設定します。 -
ESR_ELx
例外シンドロームを設定します(これについては後で説明します)( ref :D1.10.4)。 -
pc
を例外ベクトルに対応するアドレスに設定します(後で説明します)。
例外シンドロームレジスタは、 同期例外に対してのみ有効であることに注意してください。 すべての汎用レジスタとSIMD / FPレジスタには、例外が発生したときに持っていた値が含まれます。
例外ベクトル
例外が発生すると、CPUは例外ベクトルがある場所に制御を移します( ref :D1.10.2)。 例外には4つのタイプがあり、それぞれに4つの例外のソースが含まれています。 つまり 合計16の例外ベクトル。 以下に4つのタイプの例外を示します。
- 同期
svc
やbrk
などの命令によって引き起こされる例外。 まあ、一般的に、プログラマーが有罪となるすべてのイベントに対して。 - IRQ-外部ソースからの非同期割り込み。
- FIQ-外部ソースからの非同期割り込み。 迅速な処理のためのバージョン。
- SError-タイプ「システムエラー」の割り込み。
以下に、割り込みの4つのソースを示します。
-
SP = SP_EL0
現在の例外レベル -
SP = SP_ELx
現在の例外レベル - AArch64が実行される低い例外レベル
- AArch32が実行される低い例外レベル
マニュアルの説明から( ガイド :10.4):
例外が発生すると、プロセッサは例外に一致するハンドラコードを実行する必要があります。 [例外]ハンドラーが格納されるメモリ内の場所は、例外ベクトルと呼ばれます。 ARMアーキテクチャでは、例外ベクトルは例外ベクトルテーブルと呼ばれるテーブルに格納されます。 各例外レベルには、EL3、EL2、およびEL1ごとに独自のベクターテーブルがあります。 テーブルには、[x86のように]アドレスのセットではなく、実行のための命令が含まれています。 ベクターテーブルの各エントリのサイズは16命令です。 個々の例外のベクトルは、テーブルの先頭からの固定オフセットで配置されます。 各テーブルの仮想アドレスは、[特別]ベクトルアドレスレジスタVBAR_EL3
、VBAR_EL2
、およびVBAR_EL1
ます。
これらのベクトルは、次のようにメモリ内に物理的に配置されます。
SP = SP_EL0
現在の例外レベル
VBAR_ELx
からのオフセット | 例外 |
---|---|
0x000 | 同期例外 |
0x080 | IRQ |
0x100 | FIQ |
0x180 | セロロール |
SP = SP_ELx
現在の例外レベル
VBAR_ELx
からのオフセット | 例外 |
---|---|
0x200 | 同期例外 |
0x280 | IRQ |
0x300 | FIQ |
0x380 | セロロール |
AArch64が実行される低い例外レベル
VBAR_ELx
からのオフセット | 例外 |
---|---|
0x400 | 同期例外 |
0x480 | IRQ |
0x500 | FIQ |
0x580 | セロロール |
AArch32が実行される低い例外レベル
VBAR_ELx
からのオフセット | 例外 |
---|---|
0x600 | 同期例外 |
0x680 | IRQ |
0x700 | FIQ |
0x780 | セロロール |
まとめ
今のところ、ARMv8アーキテクチャについて知る必要があるのはこれだけです。 続行する前に、これらの質問に答えてみてください。 自己テスト用。
x30
エイリアスとは何ですか? [arm-x30]
0xFFFF
をx30
レジスタに書き込むと、この値を抽出するためにこのレジスタの他の2つの名前を使用できますか?
PC値を特定のアドレスに変更するにはどうすればよいですか? [arm-pc]
ret
ステートメントを使用してアドレスA
PCを設定するにはどうすればよいですか?eret
命令を使用してPCをアドレスA
に設定する方法は? これを達成するために変更するレジスタを指定します。
例外の現在のレベルを確認するにはどうすればよいですか? [arm-el]
現在の除外レベルを判断するには、どのような具体的な指示に従いますか?
スタックポインターをどのように変更して例外をスローしますか? [arm-sp-el]
実行中のプログラムのスタックポインターは、例外が発生した時点でA
です。 例外を処理した後、プログラムが実行されていた場所に戻りたいが、スタックポインターをB
に変更したい どうやってやるの?
低いELからのシステムコールに使用されるベクトルはどれですか? [arm-svc]
ユーザープロセスはEL0で実行されます。 このプロセスはsvc
呼び出します。 経営陣はどの住所に移転されますか?
下位ELからの割り込みに使用されるベクトルはどれですか? [arm-int]
ユーザープロセスはEL0で実行されます。 この時点で、タイマー割り込みが発生します。 経営陣はどの住所に移転されますか?
IRQ例外処理を有効にするにはどうすればよいですか? [アームマスク]
IRQ割り込みのロックを解除するには、どのレジスタにどの値を書き込む必要がありますか?
eret
を使用してAArch32モードを有効にするにはどうしますか? [arm-aarch32]
例外ソースはAArch64です。 この例外のハンドラーもAArch64にあります。 どのレジスタのどの値を変更すると、eret
を介して例外から戻るときeret
パーセントが実行モードAArch32に切り替わりますか?
ヒント :ウォッチ( ガイド :10.1)
サブフェーズB:アセンブラー命令

このサブフェーズでは、ARMv8コマンドセットから最も基本的なコマンドを学習します。 今はコードを書きませんが、セルフテストにはいくつかの質問があります。
メモリアクセス
ARMv8は、RISC(命令セットが削減されたコンピューター)をロード/保存するための命令セットです。 この一連の命令の特徴は、明確に定義された命令によってのみメモリアクセスが実現できるという小さな事実です。 特に、メモリは、読み込み命令でレジスタに読み込むことによってのみ読み込むことができ、保存命令によってのみ書き込むことができます。
さまざまなバリエーションのロード/アンロード(ロード/ストア)のための多くの指示があります(ほとんどの場合、同じタイプです)。 最も単純なフォームから始めましょう。
-
ldr <ra>, [<rb>]
:アドレス<rb>
から<ra>
値をロードします。 -
str <ra>, [<rb>]
:<ra>
値を<rb>
アドレスに保存します。
レジスタ<rb>
はベースレジスタと呼ばれます。 たとえば、 r3 = 0x1234
場合:
ldr r0, [r3] // r0 = *r3 ( , r0 = *(0x1234)) str r0, [r3] // *r3 = r0 ( , *(0x1234) = r0)
さらに、ギャップ[-256, 255]
からオフセットを追加できます。
ldr r0, [r3, #64] // r0 = *(r3 + 64) str r0, [r3, #-12] // *(r3 - 12) = r0
ロードまたは保存を適用した後 、基本ケースの値を変更するポストインデックスを指定することもできます。
ldr r0, [r3], #30 // r0 = *r3; r3 += 30 str r0, [r3], #-12 // *r3 = r0; r3 -= 12
または、ロードまたは保存を適用する前にベースレジスタの値を変更する事前インデックス:
ldr r0, [r3, #30]! // r3 += 30; r0 = *r3 str r0, [r3, #-12]! // r3 -= 12; *r3 = r0
オフセット、ポストインデックス、プリインデックスは、 アドレッシングモードとして知られています 。
さらに、2つのレジスタを一度にロード/アンロードできるチームもあります。 命令ldp
およびstp
( ldp
、ストアペア)。 これらの命令は、 ldr
およびstr
と同じアドレス指定モードで使用できstr
。
// `x0` `x1` . : // // |------| <x ( SP) // | x1 | // |------| // | x0 | // |------| <- SP // stp x0, x1, [SP, #-16]! // `x0` `x1` . : // // |------| <- SP // | x1 | // |------| // | x0 | // |------| <x (original SP) // ldp x0, x1, [SP], #16 // , sub SP, SP, #16 stp x0, x1, [SP] ldp x0, x1, [SP] add SP, SP, #16 // , x0, x1, x2, x3. sub SP, SP, #32 stp x0, x1, [SP] stp x2, x3, [SP, #16] ldp x0, x1, [SP] ldp x2, x3, [SP, #16] add SP, SP, #32
値の直接読み込み
即値は、値が計算なしで既知の整数の別の名前です。 (たとえば)16ビットのイミディエイトをレジスタにロードし、オプションで特定のビット数を左にシフトするには、 mov
(移動)コマンドが必要です。 シフトで同じ16ビットをロードするために、残りのビットを置き換えることなく、 movk
(move / keep)が必要movk
。 これをすべて使用する例を次に示します。
mov x0, #0xABCD, LSL #32 // x0 = 0xABCD00000000 mov x0, #0x1234, LSL #16 // x0 = 0x12340000 mov x1, #0xBEEF // x1 = 0xBEEF movk x1, #0xDEAD, LSL #16 // x1 = 0xDEADBEEF movk x1, #0xF00D, LSL #32 // x1 = 0xF00DDEADBEEF movk x1, #0xFEED, LSL #48 // x1 = 0xFEEDF00DDEADBEEF
ロードされた値自体には接頭辞#
が付いていることに注意してください。 同時にLSL
は左へのシフトを意味します。
オプションのオフセットを持つ16ビットのみをレジスタにロードできます。 ところで、多くの場合、アセンブラは必要なシフト自体を決定できます。 mov x12, #(1 << 21)
mov x12, 0x20, LSL #16
.
<label>:
:
add_30: add x1, x1, #10 add x1, x1, #20
, , adr
ldr
:
adr x0, add_30 // x0 = add_30 ldr x0, =add_30 // x0 = add_30
ldr
. adr
.
, , mov
:
mov x13, #23 // x13 = 23 mov sp, x13 // sp = 23, x13 = 23
ELR_EL1
/ mrs
msr
.
, - msr
:
msr ELR_EL1, x1 // ELR_EL1 = x1
- mrs
:
mrs x0, CurrentEL // x0 = CurrentEL
add
sub
:
add <dest> <a> <b> // dest = a + b sub <dest> <a> <b> // dest = a - b
例:
mov x2, #24 mov x3, #36 add x1, x2, x3 // x1 = 24 + 36 = 60 sub x4, x3, x2 // x4 = 36 - 24 = 12
<b>
:
sub sp, sp, #120 // sp -= 120 add x3, x1, #120 // x3 = x1 + 120 add x3, x3, #88 // x3 += 88
and
orr
AND
OR
. add
sub
:
mov x1, 0b11001 mov x2, 0b10101 and x3, x1, x2 // x3 = x1 & x2 = 0b10001 orr x3, x1, x2 // x3 = x1 | x2 = 0b11101 orr x1, x1, x2 // x1 |= x2 and x2, x2, x1 // x2 &= x1 and x1, x1, #0b110 // x1 &= 0b110 orr x1, x1, #0b101 // x1 |= 0b101
(Branching) — . PC . , b
:
b label // jump to label
( lr
), bl
. ret
lr
:
my_function: add x0, x0, x1 ret mov x0, #4 mov x1, #30 bl my_function // lr = `mov x3, x0` mov x3, x0 // x3 = x0 = 4 + 30 = 34
br
blr
b
bl
, , :
ldr x0, =label blr x0 // bl label br x0 // b label
cmp
. , bne
(branch not equal), beq
(branch if equal), blt
(branch if less than) .. ( ref : C1.2.4)
// 1 x0 , x1, // `function_when_eq`, not_equal: add x0, x0, #1 cmp x0, x1 bne not_equal bl function_when_eq exit: ... // x0 == x1 function_when_eq: ret
:
cmp x1, #0 beq x1_is_eq_to_zero
: , .
ARMv8 . , . ( ref : C1.2.4). . ISA- Griffin Dietz. , :
memcpy
ARMv8? [arm-memcpy]
,x0
, ,x1
,x2
( 8 ).memcpy
? ,ret
: 6-7 .
0xABCDE
ELR_EL1
? [arm-movk]
,EL1
,0xABCDE
ELR_EL1
ARMv8?
: .
cbz
? [arm-cbz]
cbz
( ref : C6.2.36). ? ?
init.S
? [asm-init]
os/kernel/ext/init.S
— , ._start
0x80000
. , EL1 .
os/kernel/ext/init.S
context_save
. , , - , , . (“read cpu affinity”, “core affinity != 0”) - :
MPIDR_EL1
( ref : D7.2.74) (Aff0
), , . —setup
.wfe
.
: / , .
C: EL1
EL2 EL1. os/kernel/ext/init.S
os/kernel/src/kmain.rs
. , .
aarch64
( os/kernel/src/aarch64.rs
), . sp()
. current_el()
, . , EL2 . , kmain()
. , current_el()
unsafe
. , , EL1.
, EL1. os/kernel/ext/init.S
:
// FIXME: Return to EL1 at `set_stack`.
:
mov x2, #0x3c5 msr SPSR_EL2, x2
, . , , SPSR_EL2
eret
.
, FIXME
. , EL1 CPU set_stack
, . . , — eret
. , current_el()
1
.
: PC ?
D:
. , . , , brk #n
. kernel/ext/init.S
kernel/src/traps
.
復習
, 16 , 16 . init.S
_vectors
. , 16 , handle_exception
Rust kernel/src/traps/mod.rs
. handle_exception
. , , .
handle_exception
, Rust, , . , , info
, esr
tf
, .
, ( 2 C Rust). , , , . — , :
- . AArch64 8
r0
…r7
. - . AArch64 8
r0
…r7
. - (, ..) .
caller-saved callee-saved .
caller-saved — . , caller , .
そしてその逆。 callee-saved — . つまり , .
.
AArch64r19
...r29
SP
— callee-saved . — caller-saved . ,lr
(x30
) . SIMD/FP . , caller-saved . - . AArch64
lr
, .ret
lr
.
AArch64 ( guide : 9) procedure call standard .
Rust- handle_exception
, , .
Rust , ?
, . Rust . , Rust ,extern
.handle_exception
extern
, Rust .
, , HANDLER(source, kind)
, . HANDLER(a, b)
"", , #define
. つまり :
_vectors: HANDLER(32, 39)
:
_vectors: .align 7 stp lr, x0, [SP, #-16]! mov x0, #32 movk x0, #39, LSL #16 bl context_save ldp lr, x0, [SP], #16 eret
lr
x0
x0
32- 16 source
16 kind
. context_save
, _vectors
. , , lr
x0
.
context_save
. ret
context_restore
. context_save
, Rust.
Syndrome
(, ), ( ESR_ELx
) ( ref : D1.10.4). kernel/src/traps/syndrome.rs
. Syndrome
-. , ESR_ELx
Rust esr
. Sydnrome::from(esr)
, , .
Info
handle_exception
Info
. 16 : source
kind
. , 32- , HANDLE
x0
. , HANDLE
- , Info
.
実装
. , — brk
, .. . , , .
unsafe { asm!("brk 2" :::: "volatile"); }
:
-
_vectors
HANDLE
. ,Info
. -
handle_exception
context_save
.
, / caller-saved . 5 9 .0
tf
. .
注意してください。 AArch64 ,SP
16 , /. , . -
VBAR
, :
// FIXME: load `_vectors` addr into appropriate register (guide: 10.4)
-
handle_exception
, .
handle_exception
info
esr
, , . . , , ,aarch64::nop()
. , . . -
Syndrome::from()
Fault::from()
.
. ( ref : D1.10.4, ref : Table D1-8) , . “ISS encoding description” , , . ,brk 12
Syndrome::Brk(12)
,svc 77
Syndrome::Svc(77)
. , 32- , , . -
brk
.
Syndrome::from()
handle_exception
,brk
. , . . ,Syndrome::from()
.ESR_ELx
.
exit
.exit
, .brk
.shell()
kmain
loop { }
, .
, brk 2
kmain
Brk(2)
, source, CurrentSpElx
kind Synchronous
. . exit
, .
, , . , , svc 3
. , .
すべてが期待どおりに機能したら、次のステップに進む準備ができています。

UPD:次の部分