zulinx86; home - uchan-nos/os-from-zero GitHub Wiki

zulinx86 の個人ページ

低レイヤーの技術に興味があり、個人的に勉強を進めています! まだまだ初学者ですが、頑張ります!

過去の勉強の成果物

SNS

「ゼロからのOS自作入門」読書記録

First Try (2021/03/25 - 2021/04/11)

環境

  • 開発環境
    • ローカル PC: macOS Catalina 10.15.7
    • ビルド環境: Ubuntu 18.04 on EC2 (AMI: ami-0ef85cf6e604e5650)
  • 実機環境
    • ASUS ZenBook UX310UQ
      • CPU: Intel Core i5-7200U
      • GPU: NVIDIA GeForce 940MX
      • Memory: 8 GB (DDR4-2133)
      • Storage: SSD 256 GB

0 章 (2021/03/25)

1 章 (2021/03/25)

  • バイナリエディタ Hex Friend をインストール
  • 「Disk Utility」から USB を「MS-DOS (FAT)」にフォーマットし、BOOTX64.EFI をコピー
  • 1.1 ハローワールド
    • 実機で動作確認
  • 1.4 エミュレータでのやり方
    • QEMU で動作確認
$ git clone https://github.com/uchan-nos/mikanos-build.git
$ MIKANOS_BUILD=<mikanos-build へのパス>

$ qemu-img create -f raw disk.img 200M
$ hdiutil attach -nomount disk.img
/dev/disk3     
$ newfs_msdos /dev/disk3
$ mkdir mount
$ mount -t msdos /dev/disk3 mnt/
$ mkdir -p mnt/EFI/BOOT
$ cp BOOTX64.EFI mnt/EFI/BOOT
$ hdiutil deatch disk3
$ qemu-system-x86_64 -drive if=pflash,file=$MIKANOS_BUILD/devenv/OVMF_CODE.fd -drive if=pflash,file=$MIKANOS_BUILD/devenv/OVMF_VARS.fd -hda disk.img

2 章 (2021/03/26)

  • EDK II による build 時に No module named 'distutils.util' エラーが出たら、python3-distutils をインストール
$ sudo apt install python3-distutils

3 章 (2021/03/29)

4 章 (2021/03/31)

5 章 (2021/03/31)

6 章 (2021/04/01 ~)

7 章 (2021/04/11)

Second Try (2021/12/14 - 2021/12/18)

0 章 (2021/12/14)

  • 色々あって途中で挫折してしまったが、2021 年のやり残したこと No.1 なので、再開することを決意。
  • とりあえず写経は諦めて読破を目標とし、一度読破した後に、写経したり、気になったところを深掘りしていく。

1 章 (2021/12/14)

  • 気になった点・深掘りしたい点
    • コンパイラやリンカのオプションがそれぞれどういう意味なのか。

2 章 (2021/12/14 & 2021/12/15)

  • 気になった点・深掘りしたい点
    • AppPkg 配下のアプリケーションの例を通して、UEFI に慣れたい。
    • OVMF の実装を読みたい。

3 章 (2021/12/15)

  • 気になった点・深堀りしたい点
    • 名前修飾 (name mangling) の規則を調べる。
    • -fno-rtti の動的型情報とは何か。なぜ必要か。
    • -z norelro でリロケーション情報を read only にしてはいけない理由は何か。
    • reinterpret_cast を含むキャスト周りとしっかりと勉強しておきたい。https://en.cppreference.com/w/cpp/language/reinterpret_cast
    • alignasalignof を使ってみる。

4 章 (2021/12/16)

  • 気になった点・深堀りしたい点
    • C++ の純粋仮想関数や継承、メソッドオーバーライド周りを復習しておきたい。
    • 配置 new を定義するときのシンタックスを確認しておきたい。https://en.cppreference.com/w/cpp/language/new
    • 「リンカ・ローダ実践開発テクニック」も積読にしてしまってるので、読んでおきたい。

5 章 (2021/12/16)

  • 気になった点・深掘りしたい点
    • makefont.py の中身を読んでおきたい。
    • Newlib の中身が気になる。
    • Newlib を使わずに標準ライブラリの関数を自前実装してみたい。

6 章 (2021/12/17)

  • 気になった点・深堀りしたい点
    • USB ドライバーの実装を読む。
    • PCI / PCIe について理解を深める。

7 章 (2021/12/18)

  • 気になった点・深堀りしたい点
    • 「30 日でできる!OS 自作入門」でやった割り込みと違って PIC の初期化とかがなかったが、UEFI がやってくれてるのだろうか。
    • 「30 日でできる!OS 自作入門」でやった割り込みでは、IRETD 命令を使って割り込み処理から抜けていて、EOI への通知は明示的に実行してなかったが、この辺がどうなってるのか。

8 章 (2021/12/18)

  • 気になった点・深掘りしたい点
    • 64-bit mode におけるメモリ管理の知識を深めておきたい。
    • UEFI がデフォルトで設定している GDT や Paging をダンプできるようにしたい。

Third Try (2023/01/24 -)

  • 転職など色々あり、Second Try も挫折しました。
  • 友人が読み進めるというので、便乗して再び完走を目指します。
  • 今回は開発環境は M1 Mac で開発しようと思います。

0 章 (2023/01/24)

1 章 (2023/01/24)

  • 1.1 ハローワールド
    • バイナリエディタは Visual Studio Code の Hex Editor を使用
      $ dd if=/dev/zero of=BOOTX64.EFI bs=1 count=0x600
      1536+0 records in
      1536+0 records out
      1536 bytes transferred in 0.010300 secs (149126 bytes/sec)
      
      $ sum BOOTX64.EFI 
      12430 2 BOOTX64.EFI
      
  • 1.4 エミュレータでのやり方
    • 必要なパッケージのインストール
      • qemu: エミュレータ本体
      • dosfstools: mkfs.fat コマンドで必要
      $ brew install qemu dosfstools
      
    • ループバックデバイスのマウントができない模様
      $ qemu-img create -f raw disk.img 200M
      Formatting 'disk.img', fmt=raw size=209715200
      
      $ mkfs.fat -n 'MIKAN OS' -s 2 -f 2 -R 32 -F 32 disk.img
      mkfs.fat 4.2 (2021-01-31)
      
      $ hdiutil attach -nomount disk.img
      /dev/disk4
      
      $ diskutil list /dev/disk4
      /dev/disk4 (disk image):
         #:                       TYPE NAME                    SIZE       IDENTIFIER
         0:                            MIKAN OS               +209.7 MB   disk4
      
      $ sudo mount -t msdos /dev/disk4 mnt
      Executing: /usr/bin/kmutil load -p /System/Library/Extensions/msdosfs.kext
      
      $ sudo mkdir -p mnt/EFI/BOOT
      
      $ sudo cp BOOTX64.EFI mnt/EFI/BOOT/BOOTX64.EFI
      
      $ sudo umount mnt
      
      $ hdiutil detach disk4
      "disk4" ejected.
      
    • mikanos-build リポジトリを submodule に追加して、QEMU を実行。
      $ git submodule add https://github.com/uchan-nos/mikanos-buil
      
      $  qemu-system-x86_64 -drive if=pflash,file=mikanos-build/devenv/OVMF_CODE.fd -drive if=pflash,file=mikanos-build/devenv/OVMF_VARS.fd -hda disk.img
      
      $ ./mikanos-build/devenv/run_image.sh disk.img
      
  • 1.9 C 言語でハローワールド
    • lld-link コマンドを使うため、llvm パッケージをインストールして、パスを通す。(今後のために .bashrc や .zshrc などに登録しておくとよい)
      $ brew install llvm
      $ export PATH=/opt/homebrew/opt/llvm/bin:$PATH
      
    • mikanos-buildrun_qemu.sh を使うために以下の変更を加える
      diff --git a/devenv/make_image.sh b/devenv/make_image.sh
      index 1f99933..0330974 100755
      --- a/devenv/make_image.sh
      +++ b/devenv/make_image.sh
      @@ -30,4 +30,4 @@ then
           sudo cp $ANOTHER_FILE $MOUNT_POINT/
       fi
       sleep 0.5
      -sudo umount $MOUNT_POINT
      +hdiutil detach $MOUNT_POINT
      diff --git a/devenv/mount_image.sh b/devenv/mount_image.sh
      index ba8233e..fda1d59 100755
      --- a/devenv/mount_image.sh
      +++ b/devenv/mount_image.sh
      @@ -17,4 +17,4 @@ then
       fi
       
       mkdir -p $MOUNT_POINT
      -sudo mount -o loop $DISK_IMG $MOUNT_POINT
      +hdiutil attach -mountpoint $MOUNT_POINT $DISK_IMG
      
    • 本の通り実行
      $ clang -target x86_64-pc-win32-coff -o hello.o -c hello.c
      $ lld-link /subsystem:efi_application /entry:EfiMain /out:hello.efi hello.o
      $ ../mikanos-build/devenv/run_qemu.sh hello.efi
      

2 章 (2023/01/25)

  • 2.2 EDK II でハローワールド
    • EDK II のリポジトリのクローン
      $ cd ~
      $ git clone https://github.com/tianocore/edk2.git
      $ cd edk2
      $ git submodule update --init
      
    • EDK II のビルドに当たって以下のパッチを適用
      --- a/BaseTools/Source/C/Makefiles/header.makefile
      +++ b/BaseTools/Source/C/Makefiles/header.makefile
      @@ -89,7 +89,7 @@ BUILD_OPTFLAGS = -O2 $(EXTRA_OPTFLAGS)
       ifeq ($(DARWIN),Darwin)
       # assume clang or clang compatible flags on OS X
       BUILD_CFLAGS = -MD -fshort-wchar -fno-strict-aliasing -Wall -Werror \
      --Wno-deprecated-declarations -Wno-self-assign -Wno-unused-result -nostdlib -g
      +-Wno-deprecated-declarations -Wno-self-assign -Wno-unused-result -Wno-stringop-truncation -nostdlib -g
       else
       ifeq ($(CXX), llvm)
       BUILD_CFLAGS = -MD -fshort-wchar -fno-strict-aliasing -fwrapv \
      
    • EDK II のビルド
      $ cd BaseTools/Source/C
      $ make
      
    • MikanLoaderPkg.dsc[LibraryClasses] に以下を追加
      RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf
      
    • ビルド前の準備
      $ ln -s $WORKPLACE/mikanos/src/MikanLoaderPkg ./
      $ source edksetup.sh
      
    • Conf/target.txt の変更
      ACTIVE_PLATFORM       = MikanLoaderPkg/MikanLoaderPkg.dsc
      # snipped
      TARGET                = DEBUG
      # snipped
      TARGET_ARCH           = X64
      # snipped
      TOOL_CHAIN_TAG        = CLANGPDB
      

3 章 (2023/01/26 - 2023/01/29)

  • 3.3 初めてのカーネル
    • readelf のインストール 
      $ brew install binutils
      $ echo 'export PATH="/opt/homebrew/opt/binutils/bin:$PATH"' >> ~/.zshrc
      $ source ~/.zshrc
      
      • readelf の代わりに greadelf が使えるようになる
    • kernel のビルド
      • 本の通りにビルドすると、以下のようになる。
        $ greadelf -l kernel.elf
        
        Elf file type is EXEC (Executable file)
        Entry point 0x101120
        There are 4 program headers, starting at offset 64
        
        Program Headers:
          Type           Offset             VirtAddr           PhysAddr
                         FileSiz            MemSiz              Flags  Align
          PHDR           0x0000000000000040 0x0000000000100040 0x0000000000100040
                         0x00000000000000e0 0x00000000000000e0  R      0x8
          LOAD           0x0000000000000000 0x0000000000100000 0x0000000000100000
                         0x0000000000000120 0x0000000000000120  R      0x1000
          LOAD           0x0000000000000120 0x0000000000101120 0x0000000000101120
                         0x0000000000000013 0x0000000000000013  R E    0x1000
          GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                         0x0000000000000000 0x0000000000000000  RW     0x0
        
        • エントリーポイントは 0x101120 だが、0x100000 からファイルをそのままメモリ上に展開するので、メモリ上の KernelMain() のアドレスは 0x100000 + 0x120 (ファイル上のオフセット) = 0x100120 となる。
      • 上記の問題を回避するためには、リンカに -z separate-code オプションを渡す。
        $ greadelf -l kernel.elf
        
        Elf file type is EXEC (Executable file)
        Entry point 0x101000
        There are 4 program headers, starting at offset 64
        
        Program Headers:
          Type           Offset             VirtAddr           PhysAddr
                         FileSiz            MemSiz              Flags  Align
          PHDR           0x0000000000000040 0x0000000000100040 0x0000000000100040
                         0x00000000000000e0 0x00000000000000e0  R      0x8
          LOAD           0x0000000000000000 0x0000000000100000 0x0000000000100000
                         0x0000000000000120 0x0000000000000120  R      0x1000
          LOAD           0x0000000000001000 0x0000000000101000 0x0000000000101000
                         0x0000000000001000 0x0000000000001000  R E    0x1000
          GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                         0x0000000000000000 0x0000000000000000  RW     0x0
        
        • エントリーポイント 0x101000 と、メモリに展開した時の KernelMain() のアドレス 0x100000 + 0x1000 (ファイル上でのオフセット) が一致することになる。
  • 3.5 カーネルからピクセルを描く
    • TOOL_CHAIN_TAGCLANG38 ではなく CLANGPDB を指定している場合、Microsoft x64 ABI が使われるとのこと。
    • したがって、SystemV AMD64 ABI で呼び出されることが想定されている KernelMain に、正しく引数を渡すことができない模様。
    • これを避けるためには、以下のように宣言部分を変える必要があるとのこと。
      typedef void __attribute__((sysv_abi)) EntryPointType(UINT64, UINT64);
      

4 章 ピクセル描画と make 入門 (2023/01/29)

  • 4.3 C++ の機能を使って書き直す
    • カーネルをそのままコンパイルすると、以下のエラーが出ました。
      error: unknown type name 'size_t'
      • 調べたところ、size_tstddef.h に定義されているようなのでインクルード
        #include <stddef.h>

5 章 文字表示とコンソールクラス (2023/01/29 - 2023/02/01)

6 章 マウス入力と PCI (2023/02/02)

⚠️ **GitHub.com Fallback** ⚠️