マイクロカーネルOSのひとつ
- はじめにL3があり、そしてL4ができて、さらにそれを形式検証した “SEcured L4” として誕生
- 「マイクロカーネルはIPCが遅い」という言説に反論するためにL3が作られた
- カーネルのリポジトリ (seL4/seL4)
- 検証 (seL4/l4v)
spec/haskell:Haskellによる仕様定義spec/**/*.thy:Isabelle/HOL による形式検証- HOLとは?
- High Order Logic らしい(??)
- First Order Logic なども存在するそう
- Haskellで仕様定義→Isabelleで検証(ここまでl4v)→人間がHaskellの仕様に則ってC言語でカーネルを実装(これがseL4)という感じ?
- HOLとは?
tools/c-parser:SMLで書かれたCのパーサー- 結構デカい(リポジトリの使用言語2位がSMLになるレベル)
- 検証にあたってC言語による実装が正しいかどうか確認するために使ってるのかな(しらんけど)
- docs.sel4.systems
- カーネルオブジェクトとシステムコールの一覧
- CPUアーキテクチャに固有なカーネルオブジェクトも紹介されている
- カーネルオブジェクトとシステムコールの一覧
- 自作OSで学ぶマイクロカーネルの設計と実装でも題材のひとつとなっている
- https://scrapbox.io/horizon2038/seL4
- seL4チュートリアル | ドクセル
- 「ゼロから学ぶRust」や「並行プログラミング入門」の著者である高野氏による解説
- RefOS
- seL4 orgにあるリファレンスOS
capability, CSpace, CNode
https://docs.sel4.systems/Tutorials/capabilities.html
- 何にアクセスできるのかを示すもの(?)
- seL4におけるcapabilityは以下の3つ:
- カーネルオブジェクトへのアクセスを制御するもの
- カーネルオブジェクトはCapabilityによって参照・操作されるもの
- Untyped Memory に対して Retype() を行うことで生成する
- Thread Control Block (TCB) など
- カーネルオブジェクトはCapabilityによって参照・操作されるもの
- 抽象リソースへのアクセスを制御するもの
- IRQControlなど
- カーネルオブジェクトと異なり、メモリ領域に対応するのではなく、カーネルが提供する機能やハードウェア制御の権限を表す
- Untyped capability
- Untyped Memoryを操作するためのcapability
- カーネルオブジェクトへのアクセスを制御するもの
- spec/haskell/src/SEL4/Object/Structures.lhs に capability の定義がある
- 文芸的プログラミングだ……
| 種類 | 用途 |
|---|---|
| ThreadCap | TCBを操作 |
| NullCap | ? |
| NotificationCap | シグナルを操作 |
| IRQHandlerCap | 割り込みハンドラを制御 |
| EndpointCap | IPCを行うためのCapability |
| DomainCap | ? |
| Zombie | ? |
| ArchObjectCap | CPUアーキテクチャに固有なカーネルオブジェクト(x86のPageTableなど)を操作? |
| ReplyCap | ?(どうせIPCの返事に使うんだろ) |
| UntypedCap | 空メモリへのCapability |
| CNodeCap | CNodeを操作 |
| IRQControlCap | 割り込みを制御 |
- CNode (Capability Node) という配列に格納される
- カーネルオブジェクトの一種
- 前述の
Structures.lhsにおいて、CNodeCapの定義とともにカーネルオブジェクトの中身も書いてある:-
| CNodeCap { capCNodePtr :: PPtr CTE, capCNodeBits :: Int, capCNodeGuard :: Word, capCNodeGuardSize :: Int } CTEは Capability Table EntryGuardはGuardSizeのサイズをもつビットパターン- 後述の
CPtrは先頭がGuardと一致しているらしい
- 後述の
-
- CNodeの、Capabilityが格納される領域のことを CSlot と呼ぶ
- 空であるか、埋まっているかのどちらか(それはそうだろ)
CNodes[0]は慣習として空のままにされているらしい- そんなGDTの0番目 (null descriptor) みたいなことしてうれしいんだろうか
- GDTは歴史があるのでまだ理解できるが(といっても経緯を知らないので同じようなものではある)
- CNodeとCSlotの指定にはCPtrを用いる
Ptrという名前ではあるが、メモリ領域へのポインタではないlibsel4/include/sel4/simple_types.hより:seL4_CNode==seL4_CPtr==seL4_Word==seL4_Uint64- Capability Address Space におけるポインタと考えられる?
- CSpace は、1つ以上の CNode からなる集合(有向グラフ?)
- CSpace内の(CNodeに格納されている)Capabilityにアクセスする(direct addressing)際は、操作対象のCNode・CNodeにおけるCSlotのindex・CNodeをトラバースする際のdepthを指定する
- たとえば
seL4_CNode_Copy()の第1~3引数や4~6引数 - 操作対象の CNode は
seL4_CNodeとなっているが、これはseL4_CPtrと同義 - depthは基本的に
seL4_WordBits- 各archの
constants.hで、主に(sizeof(seL4_Word) * 8)としてdefineされている - RISC-Vとx86_64では
sizeofを用いた式ではなく定数になっている(なんで?)
- 各archの
- たとえば
- CSpaceに対応する構造体があるというより、あるCNodeがrootとなっていて、そこから辿れるCNodeの集合をCSpaceと呼んでいるのか?
seL4_CNode_Copy()の第4引数がseL4_CNode src_rootとなっていることから推測- CNodeにCNodeCapが格納されていれば、そこから「次」のCNodeを辿ることができる
- 例:
-
seL4_CNode_Copy( // dest seL4_CapInitThreadCNode, boot_info->empty.start, seL4_WordBits, // src seL4_CapInitThreadCNode, seL4_CapInitThreadTCB, seL4_WordBits, seL4_AllRights);- (CapabilityのTutorialより一部改変)
seL4_CapInitThreadCNodeはenum seL4_RootCNodeCapSlotsのメンバで、ルートタスクの「CSpaceというアドレス空間」におけるポインタのようなもの??となっており、これでCNodeを指定したうえでCPtrでスロットの位置を指定している- そうするとsrc指定で第4・第5引数の双方に
enum seL4_RootCNodeCapSlotsを使っていて変だが、ルートタスクにおいてはこれらは同じ意味であるらしい(要出典)- つまり、例えば
seL4_CapInitThreadCNodeは、第1引数などでCNodeを指定する時にも使えるし、CNodeを操作するCapabilityが格納されているスロットを指定するのにも使えるseL4_CNode_Copy(a,b,c, seL4_CapInitThreadCNode, seL4_CapInitThreadCNode, seL4_WordBits, d);というわけ
- つまり、例えば
-
- CSpace内の(CNodeに格納されている)Capabilityにアクセスする(direct addressing)際は、操作対象のCNode・CNodeにおけるCSlotのindex・CNodeをトラバースする際のdepthを指定する
Untyped Memory
https://docs.sel4.systems/Tutorials/untyped.html
- 全ての利用可能な物理メモリへのCapabilityは、Untyped memoryへのCapabilityとしてルートタスクに渡される
- Untyped Memory は、特定のサイズを持つ連続した物理メモリのブロック
- Untyped Capability は、カーネルオブジェクトとそれらへのCapabilityにRetypeできる
- Untyped Capability は、そこにカーネルが書き込みできるかどうかを示すboolean
deviceを持つ- true である(カーネルが書き込めない)メモリ領域って何?BIOSとかに予約されてる領域なのか?(だとしたらそこを Untyped Memory としてルートタスクに渡す意味がわからない)
- これに対して行える処理は
seL4_Untyped_Retype()のみ- Untyped Capability のタイプを変更?
- カーネルオブジェクトが付随するタイプのCapabilityであれば、カーネルオブジェクトを作成し、それを操作するためのCapabilityと共に返却
- Untyped Capability を retype して作った Capability は、もとの Capability の子 (children) となる
- 親が参照するメモリ領域の一部にアクセスできるようになる
-
seL4_Untyped_Retype( seL4_Untyped _service, seL4_Word type, seL4_Word size_bits, seL4_CNode root, seL4_Word node_index, seL4_Word node_depth, seL4_Word node_offset, seL4_Word num_objects)_service: retype したい Untyped Memorytype:seL4_TCBObjectなどの retype 先size_bits: オブジェクトのサイズが可変である場合に使う- Untyped Object がそう
- チュートリアルでは以下のようなことをやっている
- ルートタスクに与えられた Untyped Memory から適切なものを選ぶ
- サイズが十分(?)で、device がfalseであるようなもの
- サイズは
seL4_TCBBits + 1となっているが、TCB以外にも作成してそうなのにそれらを考慮していないのか? - 2^n でサイズを指定するから、最も大きいサイズである
seL4_TCBBitsに1足すだけで良い、ということらしい- TCBBits は 11 で、 EndpointBits + NotificationBits = 5 + 4 = 9 だから正しそう
- サイズは
- サイズが十分(?)で、device がfalseであるようなもの
- それを Untyped に Retype することで Untyped Object を得る
- それを親として、
(Untyped Object内での?)CNode 内のオフセットをずらしつつカーネルオブジェクト(TCB、Endpoint、Notification)を作っていくoffset は 1 ずつズレていくが、これはどういうことなのだろうか カーネルオブジェクトのサイズぶんズラすというわけではないのか?- この offset はおそらく CNode における offset で、Retype して得られた Capability を格納する先を指定するためのものだから、カーネルオブジェクトは関係ない
- ルートタスクに与えられた Untyped Memory から適切なものを選ぶ
TCB
https://docs.sel4.systems/Tutorials/threads.html
Thread Control Object
- カーネルオブジェクトの一種
- CSpace root と呼ばれるCNode capabilityを持つ
- 空である場合がある
ブート
- 最初に起動するタスクは root task と呼ばれる
- 全てのcapabilityを制御できる
struct BootInfoをもらえるuntypedListという、Untyped Memory オブジェクトの情報をもつseL4_UntypedDescの配列がある
seL4_Untyped_Retype()によってBootInfo.untypedListの要素を任意のCapability(あるいはobject)に変換していく- sel4-tutorialsのlibsel4tutorials/src/alloc.c にそういう感じの処理が書かれている
libsel4/include/sel4/bootinfo_types.hを並べて読むと良いだろう
- seL4_x86-64でのブート処理