Closed keigo1216 closed 11 months ago
-D qemu.log -d in_asm
をqemu
の起動時に指定してあげると、実行されたアセンブリコードを見ることができるので、幸せになれる
まだユーザー空間(EL0
レベルの領域)を実装していないので、コンテキストスイッチ終了後の新しいプロセスの実行はEL1
レベルで行っている。
今後はユーザー空間で実行できるように変更する(コンテキストスイッチはシステムコールで行うから、これが戻る時にEL0
にすればいいだけだから、変更しなくてもいいのかもしれない)
プロセスとコンテキストスイッチの実装
ARMv8ではプログラムカウンタを直接設定することは不可能なので、分岐命令とかを駆使してプログラムカウンタを設定する必要がある(めんど)
レジスタ
Callee-saved
レジスタX19
からX28
レジスタ 関数呼び出しを跨いで値を保持する必要がある場合、呼び出された関数がこれらのレジスタの値を保存し、復元する責任を持つFP(X29)
フレームポインタとして機能して、現在の関数のスタックポインタのベースアドレスを保持する これもコンテキストスイッチ時に保存する
LR(X30)
このレジスタには
ret
命令で戻るプログラムカウンタの値を保持している実装の流れ
X19
からX30
までの汎用レジスタの初期値0をプッシュする(create_process
関数)。X30
レジスタには, プログラムカウンタを切り替えるための関数(後で記述)の先頭アドレスを設定しておくことで、ret
命令が発行された時に、その関数にジャンプするstart_task
関数でEL1
のOSモードからEL0
のユーザーモードへ切り替える. この時の戻りアドレスにspに入れておいたプロセスの先頭アドレスをelr_el1
に入れ,eret
命令を打つswitch_context
関数では, まず現在動いているプロセスの汎用レジスタの値(X19
からX30
)までをprev_sp
が指しているスタック領域にプッシュする. その後,next_sp
が指しているスタック領域の先頭からX30
,X29
, ...X19
をポップする。 ここで、 PCをポップしてしまうと、start_task
でプログラムカウンタが取得できなくなるので、PCの一個手前でストップするハマったところ
EL0権限のスタックポインタの初期値の設定
ARMはそれぞれの権限で、スタックポインタのレジスタを使い分けているので、それぞれで初期化してあげる必要がある これ忘れると、コンテキストスイッチした後のプロセス内で関数呼び出しすると、例外ハンドラに飛んでしまう
疑問点
新旧のスタックポインタの渡し方
参照渡しだと通ったが, 値渡しをするとプログラムがプロセスA→プロセスBは動いたが, プロセスB→プロセスAに帰る時に例外が出た(プロセスAとプロセスBを入れ替えても同じことが起きた)
動かないプログラム
動くプログラム
考えたこと
switch_context
への値の渡し方がよろしくない?気がするスタックポインタの更新
switch_context
を実行した後は、ldp
命令などのsp
に対して行った処理は引数のnext_sp
に反映されない イメージとしてはprev_sp
: 関数呼び出し時に入っている値は考慮(アドレスはもちろん変えない)しなくて、プッシュしたspの値をただ入れるはこのようなものnext_sp
: スタックポインタの初期値を与えるだけのもので、それ以降はSP
レジスタを見ればいいわけだから、更新しない. プロセス実行中に例外が飛んできたら、prev_sp
の要領で現時点のスタックポインタを入れることで保存できる。プロセス実行時にはこのスタックポインタの値はあまり意味ないのかも(レジスタの値を見ろ!!!)参考資料
https://tc.gts3.org/cs3210/2020/spring/lab/lab4.html https://developer.arm.com/documentation/102374/0101/Procedure-Call-Standard