# arch輪講 mikan本 \#1 10/27 ## はじめに - 今日カバーする範囲: sec1-3 - この範囲でやること: ブートローダの作成 - 省略すること: - sec0全体 - sec1-3において - 自明な知識(と思われるもの) e.g. C言語における文法,概念解説 ポインタ,アロー演算子とか - OS自作にあたる心構えなどの記載(重要な気もするが,おそらく皆さんにとっては自明なことしか書いていない) 基本的に最初の方はみなさんにとってはさっと読めばすぐわかることしか書いてないと思うので,(現段階では)輪講の時間で一緒にさっと読みつつ,さっと読んで得られない部分を解説することで付加価値を産み出せればいいのかなと考えています. 本当に1-3章はさっと読めてしまう内容しかないです.輪講の時間のうち大半が僕の個人的な見解による「さっと読んではわからない部分」に消えます. ## 環境構築 Ubuntu18,20前提.仮想環境でもOKだが,QEMU on UbuntuでNested VM構成になるのと,x86_64 OSを作ろうというはなしなのでそこは注意 (Ubuntu on ARMとかだと著者が用意してくれたansibleでは必要なパッケージがUbuntu上にインストールされないと思われる) とりあえず用意したUbuntuで以下を事項してください ``` cd $HOME mkdir -p osbook/devenv cd $your_project_dir git clone https://github.com/uchan-nos/mikanos-build.git git checkout 8d4882122ec548ef680b6b5a2ae841a0fd4d07a1 cd devenv ansible-playbook -K -i ansible_inventory ansible_provision.yml ``` ## Sec 1 PCの仕組みとハローワールド p29- Sec 1の内容についてはざっくりと紹介するだけでいいと判断したので... ### この章の内容 要約: Hello, World!のUEFIアプリケーションをバイナリ手書きで作成,実行したあと,Cで実装して実機かQEMUで実行してみようぜ! - 1.1 ハローワールド - ハローワールドするUEFIアプリケーションのバイナリを手書きして実機で実行してみよう - 本輪講では手書きは行わない(手書きする体験によって得られるものは皆さん既に得ていると思うため)が代わりにバイナリを読解してみることにした(こっちはやったことないヒトもいると思うため) - 1.2 USBメモリのデバイス名の探し方 - 本輪講では省略 - 一応内容 - dmesgからディスク名を確認するだけ - みんなdmesgもfdiskも使えるよね - 1.3 WSLでのやりかた - 1.1節の内容のうち開発(バイナリの手書きの部分)をWSLで実行する方法について - 本輪講では省略 - 誰もWindowsを使っていないため - 1.4 エミュレータでのやり方 - 1.1節の内容のうち(バイナリの)実行をQEMUで実行する方法について - 本輪講では省略 - QEMUのオプションを知ることに意義はないため - 著者がシェルスクリプト化してくれているので,それを使いましょう - 1.5 結局,何をやったのか? - 本輪講では省略 - 本当に「何をやった」かは書いてない - ので,それを説明するのが僕的な「付加価値」 - 1.6 とにかく手を動かそう - その通りです,手が動かない人間ですみません - 本輪講では省略 - 一応内容 - これまでのはなしよくわからん?でもきっちり理解しようとするより,とりあえず手を動かして先に進むほうがええで? - 本書はOSを作る過程を細かく刻んで紹介するだけで,OSの理論を体系的に紹介したりしないよ(だからきっちり理解しようとせずに,とりあえず手を動かしてね) - 独自の改造とかやってみてね,結果的にきっちり理解することになるから - 写経はいい勉強になるよ - 1.7 UEFI BIOSによる起動 - この節のほうが皆にとっては「結局,何をやったのか」だと思う - 本輪講では省略 - 一応内容 - EFIファイルをusbメモリに保存し、ブートするとHello, Worldが表示されるのはなぜ? - ブート後、BIOSが実行され実行可能ファイルをメモリへロードし、そのファイルの実行が開始される - bios: basic input output system 基本的な入出力機能、OS起動前にコンピュータ内部を初期化、OSをストレージから読み出す機能を提供 - UEFI BIOS: UEFIという仕様に準拠したBIOSのこと UEFI BIOS上で実行されるプログラムをUEFIアプリケーションという。今回扱ったhwバイナリ、扱うhw cコードはuefiアプリケーションとなる - メモリは揮発するため、電源を入れた後にbiosが実行されるようにしておかないと作成したプログラムを実行させることができない(実行可能ファイルをメモリにロードしてくれるプログラムが必要)。だからcpuは自動でbiosを実行する - biosはpc本体。周辺機器を初期化する。例: cpuの動作モード設定、pciデバイス検出、設定、実行可能ファイルをメモリにロードし、実行を開始 - 補足: 2つ上の文章ではbiosの必要性はIPLとしての必要性の部分しか説明されていない。3つ上、1つ上でbiosの機能、挙動として説明された入出力の話も重要、だってbasic input output systemだよ - 入出力の提供って? - biosはi/oの抽象化レイヤとして機能する - けどあんまり使われないからみんな意識してないかも? - そもそも入出力はちゃんと動くのか?hw的に壊れていないのか? - 一応biosがテストしてるよ - biosメニューからram testとかできるはず - 1.8 OSを作る道具 - 本輪講では省略 - 一応内容 - バイナリエディタでだって作れんことはないが,適材適所,プログラミング言語とテキストエディタでしょ - 言語はC/C++だよ - ソースコードをコンパイルしてリンクしたら実行可能ファイルになるんやで - コンパイルするやつはコンパイラっていうんやで - 1.9 C言語でハローワールド ### 本講におけるこの章の進め方 飛ばす章が多いので、結構構成を変えて話します 1. 作者の提示するHello, World! UEFI Application (バイナリ手書き)の解説 2. QEMU上でHello, World! UEFI Application (バイナリ手書き)を実行 3. 作者の提示するHello, World! UEFI Application (C)の解説 4. QEMU上でHello, World! UEFI Application (C)を実行 必要な知識 - UEFIアプリケーションとは? - PE形式実行可能ファイルのフォーマット - x86アーキテクチャのアセンブリ ### Hello, World! バイナリの解読 #### MS-DOS Header (0x00000000-0x0000003f) - MS-DOSとWindows間でバイナリの互換性を取るためだけに存在するフィールドである - 2つのフィールド以外使用されない ``` 00000000: 4d5a 0000 0000 0000 0000 0000 0000 0000 MZ.............. 00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000030: 0000 0000 0000 0000 0000 0000 8000 0000 ................ ``` - -0x00000000-0x00000001- (0x4d5a): e_magic (Magic number) - 0x4d5a→"MZ" - このファイルがMZ DOS実行可能ファイルであることを示す - このマジックナンバーがあればCOM以外のMS-DOS実行可能ファイル - MZはMS-DOS開発者,Mark Zbikowskiの頭文字 - この間にも様々なフィールドがあるが,実行可能ファイルがPE形式である場合には使用されないので割愛 - -0x0000003c-0x0000003f- (0x80000000): e_lfanew (File address of new exe header ) - (**l**ong **f**ile **a**ddress of **new** exe header) - PEヘッダ(or PE Signature)のファイルアドレス(どちらも同じ意味だけど...MS公式の文書ではPE Signatureってよく言う) - 0x8000_0000→0x0000_0080 (big-endian to little-endian of 4B grouping) #### MS-DOS Real-Mode Stub (0x00000040-0x0000007f) - MS-DOSでPE形式実行可能ファイルを起動した時に実行されるプログラムを格納する - "This program cannot be run in DOS mode."をデフォルトでリンカがリンクしてくれる - 自分で作成した実行可能ファイルをリンクすることも可能 - 要は今あんまり関係ない ``` 00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ ``` #### PE Header (0x00000080-0x0000----) - winnt.hにIMAGE_NT_HEADERS32と定義されていたりするのでNT Headerと呼ぶ人も - COFFから派生しているのでPE/COFF Headerみたいな表記をされることも - 3つの部分からなる - Signature - File header - Optional header ##### Signature - -0x00000080-0x00000083- (0x50450000): Signature (PE signature) - 0x50450000→"PE\0\0" - この実行可能ファイルがPE形式であることを示す ``` 00000080: 5045 0000 6486 0200 0000 0000 0000 0000 PE..d........... ``` ##### File header - -0x00000084-0x00000097-: FileHeader (File header) - Machine: 0x6486→0x8664→x64 - NumberOfSections: 0x0200→0x0002→2(10) - TimeDateStamp: 0x00000000 - PointerToSymbolTable: 0x00000000 - PEにはシンボルテーブルはない - NumberOfSymbols: 0x00000000 - PEにはシンボルテーブルはない - SizeOfOptionalHeader: 0xf000→0x00f0→16(10) - Characteristics: 0x2202→0x0222 - 0x0200: Debug stripped - 0x0020: Large address - 0x0002: Executable image (must be set) ``` 00000080: 5045 0000 6486 0200 0000 0000 0000 0000 PE..d........... 00000090: 0000 0000 f000 2202 0b02 0000 0002 0000 ......"......... ``` ##### Optional header - -0x00000098-0x000000xx-: OptionalHeader (Optional header) - Magic: 0x0b02→0x020b→PE+ (0x0b01z;0x010b→PE32) <!-- - MajorLinkerVersion: - MinorLinkerVersion: --> - SizeOfCode: (0x0002_0000→0x0000_0200) - コード領域 - SizeOfInitializedData: (0x0002_0000→0x0000_0200) - 値が確定している領域 <!-- - SizeOfUninitializedData: 0x00000000 - 大域変数などの値が確定していない領域 --> - AddressOfEntryPoint: (0x0010_0000→0x0000_1000) - エントリポイントのイメージベースからのRVA - (\*PEはだいたいRVA) - BaseOfCode: (0x0010_0000→0x0000_1000) - コードの先頭セクションがメモリに読み込まれるときのRVA <!-- - BaseOfData --> - ImageBase: (0x0000_0040_0100_0000→0x0000_0001_4000_0000) - イメージファイルがロードされるアドレス - Default: - EXE: 0x - DLL: - SectionAlignment: (0x0010_0000→0x0000_1000) - FileAlignment: (0x0002_0000→0x0000_0200) - default: 512 - 512-64Kの2の累乗である必要がある <!-- - MajorOperatingSystemVersion: (0x0000) - MinorOperatingSystemVersion: (0x0000) - MajorImageVersion: (0x0000) - MinorImageVersion: (0x0000) - MajorSubsystemVersion: (0x0600→0x0006) - MinorSubsystemVersion:: (0x0000) - Win32VersionValue/Reserved1: (0x0000_0000) --> - SizeOfImage: (0x0030_0000→0x0000_3000) - イメージファイルをロードするために必要なサイズ,セクションが必要とする倍数を求めた後,ページアライメント,セクションアライメントを取り算出 - section alignmentの倍数 - SizeOfHeaders: (0x) - ms-dos stub, pe header, section table全てのサイズ - file alignmentの倍数に切り上げ <!-- - CheckSum: - checksum of an image file - Subsystem: - image fileの実行に必要なwindowsサブシステム - DllCharacteristics - SizeOfStackReverse - SizeOfStackCommit - SizeOfHeapCommit - LoaderFlags - NumberOfRvaAndSizes --> - DataDirectory - ... - Reserverd (-0x00000180-0x00000187) ``` 00000090: 0000 0000 f000 2202 0b02 0000 0002 0000 ......"......... 000000a0: 0002 0000 0000 0000 0010 0000 0010 0000 ................ 000000b0: 0000 0040 0100 0000 0010 0000 0002 0000 ...@............ 000000c0: 0000 0000 0000 0000 0600 0000 0000 0000 ................ 000000d0: 0030 0000 0002 0000 0000 0000 0a00 6081 .0............`. 000000e0: 0000 1000 0000 0000 0010 0000 0000 0000 ................ 000000f0: 0000 1000 0000 0000 0010 0000 0000 0000 ................ 00000100: 0000 0000 1000 0000 0000 0000 0000 0000 ................ 00000110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000130: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000140: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000150: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000160: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000170: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000180: 0000 0000 0000 0000 .... .... .... .... ................ ``` ##### Section Table (Section Header) - Section Header (Section Table Entry) - 40B each - Fields - Name: 8B - VirtualSize: 4B - VirtualAddress: 4B - SizeOfRawData: 4B - PointerToRawData: 4B - .text - Physical/VirtualSize: 0x1400_0000→0x0000_0014 - セクションの実際のファイル上でのサイズ - VirtualAddress: 0x0010_0000→0x0000_1000 - SizeOfRawData: 0x0002_0000→0x0000_0200 - メモリにロードしたときのサイズ - 次の境界までpaddingされるので、だいたいphysical/virtual size <= sizeofrawdata - filealignmentの倍数まで丸められる - PointerToRawData: 0x0002_0000→0x0000_0200 ``` cyan@cyan-vivado:~/project/mikanos-build/day01/bin$ objdump -d hello.efi hello.efi: file format pei-x86-64 Disassembly of section .text: 0000000140001000 <.text>: 140001000: 48 83 ec 28 sub $0x28,%rsp 140001004: 48 8b 4a 40 mov 0x40(%rdx),%rcx 140001008: 48 8d 15 f1 0f 00 00 lea 0xff1(%rip),%rdx # 0x140002000 14000100f: ff 51 08 callq *0x8(%rcx) 140001012: eb fe jmp 0x140001012 ``` やっていることはrsp -= 0x28 スタックポインタを-40(10)する→40Bのスタック確保 ``` 140001000: 48 83 ec 28 sub $0x28,%rsp ``` rdx内のアドレス+40に存在する値をrcxに ここを理解するためにはuefi calling conventionとCコード見たほうがいい ``` 140001004: 48 8b 4a 40 mov 0x40(%rdx),%rcx ``` rip+0xff1って? lea命令実行時点でのripは0x0000_0014_0001_000fなので 0x140001000f+0xff1=0x140002000 .rdataセクションの先頭アドレス(メモリ上の) ``` 140001008: 48 8d 15 f1 0f 00 00 lea 0xff1(%rip),%rdx # 0x140002000 ``` 0x8でoverloadしたrcxへのindirect jump ここを理解するためにはuefi calling conventionとCコード見たほうがいい ``` 14000100f: ff 51 08 callq *0x8(%rcx) ``` ``` 00000180: .... .... .... .... 2e74 6578 7400 0000 .........text... 00000190: 1400 0000 0010 0000 0002 0000 0002 0000 ................ 000001a0: 0000 0000 0000 0000 0000 0000 2000 5060 ............ .P` 000001b0: 2e72 6461 7461 0000 1c00 0000 0020 0000 .rdata....... .. 000001c0: 0002 0000 0004 0000 0000 0000 0000 0000 ................ 000001d0: 0000 0000 4000 5040 0000 0000 0000 0000 ....@.P@........ 000001e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000001f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000200: 4883 ec28 488b 4a40 488d 15f1 0f00 00ff H..(H.J@H....... 00000210: 5108 ebfe 0000 0000 0000 0000 0000 0000 Q............... 00000220: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000230: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000240: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000250: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000260: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000270: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000280: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000290: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000002a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000002b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000002c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000002d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000002e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000002f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000300: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000310: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000320: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000330: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000340: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000350: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000360: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000370: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000380: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000390: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000003a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000003b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000003c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000003d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000003e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000003f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000400: 4800 6500 6c00 6c00 6f00 2c00 2000 7700 H.e.l.l.o.,. .w. 00000410: 6f00 7200 6c00 6400 2100 0000 0000 0000 o.r.l.d.!....... 00000420: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000430: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000440: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000450: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000460: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000470: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000480: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000490: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000004a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000004b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000004c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000004d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000004e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000004f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000500: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000510: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000520: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000530: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000540: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000550: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000560: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000570: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000580: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000590: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000005a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000005b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000005c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000005d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000005e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000005f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ ``` ### HW in C hello.c ``` typedef unsigned short CHAR16; typedef unsigned long long EFI_STATUS; typedef void *EFI_HANDLE; struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL; typedef EFI_STATUS (*EFI_TEXT_STRING)( struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, CHAR16 *String); typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { void *dummy; EFI_TEXT_STRING OutputString; } EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL; typedef struct { char dummy[52]; EFI_HANDLE ConsoleOutHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; } EFI_SYSTEM_TABLE; EFI_STATUS EfiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello, world!\n"); while (1); return 0; } ``` #### `-mno-red-zone`? ![](https://i.imgur.com/9V2UTqY.png) - ここを予約しておくとleaf funcとかがtmp dataぶっこめて(2つ命令減らして)便利だよねってはなし - red zoneはx86 64 system v abiのはなしなので,peはwindows abiだけど,x86 windows abiにはred zoneないから関係ない気がするぞ? - ちなみにwindows abiにおいてもaarch64 ia64むけには16B,aarch32では8Bのred zoneがある - (実際なくても動く ### UEFI? UEFI APP? - UEFI - specification https://uefi.org/specifications ## Sec 2 EDK Ⅱ 入門とメモリマップ ### この章の内容 EDK Ⅱ を使って... - 2.1 EDK Ⅱ 入門 - EDK Ⅱって何? - EDK Ⅱのディレクトリ構造 - 2.2 EDK Ⅱ でハローワールド (osbook day02a) - EDK ⅡのUEFIアプリケーションを作るための使い方 - 1.9のCプログラムをEDK Ⅱを用いて再実装するよ - 2.3 メインメモリ - 本輪講では省略 - 一応内容 - ハードウェア的には複数のメモリチップから構成されているメモリはソフトウェア的には多数のバイトが直線的に整列しているように見える - なんで?→メモリコントローラ - 補足: メモリコントローラは昔はnorth bridgeの機能であったが,トランジスタの集積度の増加によるSoC化により,現在はCPUの中にあることが一般的 - 各バイトに振られた連番→アドレス - 2.4 メモリマップ - 2.5 メモリマップの取得 (osbook day02b) - 2.6 メモリマップのファイルへの保存 - 2.7 メモリマップの確認 - 2.8 ポインタ入門(1):アドレスとポインタ - 本輪講では省略 - 2.9 ポインタとアロー演算子 - 本輪講では省略 ## Sec 3 画面表示の練習とブートローダ ### この章の内容 - 3.1 QEMUモニタ - 3.2 レジスタ - 3.3 初めてのカーネル (osbook day03a) - ブートローダはUEFIアプリケーション,カーネルはELFバイナリとして別々に開発していくよ - とりあえず無限ループするプログラムのELFバイナリ