自作OS(1): ブートローダ


OSつくってみたいので、進捗を日記的にまとめていきたい

大まかな流れは30日でできる! OS自作入門に沿っていく
ただ、同じようにFAT12フロッピーだとつまらないと思ったので、いろいろ変えていきたい

ブートローダ

NASMの説明や, セグメント方式等の説明に関しては省略
(NASMに関しては、個人的にNASM Tutorialがとてもよかった)

まず、FAT32 USBを目標にしたいと思ったのでFAT32のBPBを調査することから始めた(Linux系では不要?なのだが, WIndowsとかでは必要らしい)
これについては、FAT32の仕様書「Microsoft Extensible Firmware Initiative FAT32 File System Specification」を参照した(FATファイル システムのしくみと操作法では日本語に訳してくれている)

以下は、それらを簡単にまとめた表と手元のUSBメモリをWindowsでFAT32フォーマットして確認したバイナリ




























名前 オフセット(byte) サイズ(bytes) 説明 実際確認した値(Little Endian)
BS_jmpBoot 0 3 ブートコードへのジャンプ命令 EB 58 90
BS_OEMName 3 8 任意の名前 4D 53 44 4F 53 35 2E 30 (MSDOS5.0)
BPB_BytsPerSec 11 2 bytes/sector, 普通512か 00 02 (512)
BPB_SecPerClus 13 1 sectors/cluster, 2冪でないといけない, 現在よく使われるのは8のようだ 08
BPB_RsvdSecCnt 14 2 予約セクタ数, FAT32の場合32が代表的 80 07
BPB_NumFATs 16 1 ボリューム中のFATデータ構造の数, FATの場合常に2とある 02
BPB_RootEntCnt 17 2 FAT12/16においてルートディレクトリ中の32byteエントリの数を示す, FAT32の場合0とある 00 00
BPB_TotSec16 19 2 ボリューム中の総セクタ数(16bit), FAT32の場合0 00 00
BPB_Media 21 1 パーティションありのノンリムーバルメディアの場合は0xF8, パーティションなしのリムーバルメディアの場合は0xF0が標準的な値 F8
BPB_FATSz16 22 2 FAT12/FAT16における1つのFATによって占有されるセクタ数(16bit), FAT32の場合0 00 00
BPB_SecPerTrk 24 2 secters/track 3F 00(63)
BPB_NumHeads 26 2 ヘッド数 FF 00 (255)
BPB_HiddSec 28 4 FATボリュームより前にある隠れセクタの数, パーティションがないメディアでは0 3F 00 00 00 (63)
BPB_TotSec32 32 4 ボリューム中の総セクタ数(32bit) C1 7F F1 00 (15826881)
BPB_FATSz32 36 4 1つのFATに占有されるセクタ数(32bit) 40 3C 00 00 (15424)
BPB_ExtFlags 40 2 0-3: アクティイブなFAT数, 4-6: 予約, 7: 0ならミラーリング, 1なら1FAT, 8-15: 予約 00 00
BPB_FSVer 42 2 FAT32ボリュームのバージョン, Highがメジャー, Lowがマイナー 00 00
BPB_RootClus 44 4 ルートディレクトリの最初のクラスタの番号, 基本2とある 02 00 00 00
BPB_FSInfo 48 2 FAT32ボリュームの予約領域中のFATINFO構造のセクタ番号, 基本1 01 00
BPB_BkBootSec 50 2 0の場合ブートレコードのコピーボリューム中の予約領域のセクタ番号, 基本6 06 00
BPB_Reserverd 52 12 拡張のための予約領域, FAT32の場合0で埋める 0埋め
BS_DrvNum 64 1 ドライブナンバー 80
BS_Reserved1 65 1 WindowsNT用の予約領域, 0でいい 00
BS_BootSig 66 1 0x29, 以下の3フィールドがあるということを示す 29
BS_VolID 67 4 ボリュームのシリアルナンバー, BS_VolLabと共に、リムーバルメディアでのトラッキングに利用される, これによりFATファイルシステムのドライバが不正なディスクが挿入されたことを検出できる 5C A1 A6 A0
BS_VolLab 71 11 ボリュームラベル, ルートディレクトリに記録されている11byteのボリュームラベルに一致する 4E 4F 20 4E 41 4D 45 20 20 20 20 (NO NAME )
BS_FileSysType 82 8 "FAT32"という文字列を入れる 46 41 54 33 20 20 20 (FAT32 )

ブートコードを書く前の事前知識

BIOSがMBRをロードするアドレス

BIOSはMBRを0000:7c00にロードする
もしくは07c0:0000と書かれる場合もあるが、
これは、
0x0000 * 16 + 0x7c00 = 0x07c0 * 16 + 0x0000 = 0x7c00
なので同じ場所

したがって、初期化処理として2通りのやり方がある

  • org 0x7c00 で0x7c00から始めて、セグメントレジスタを0x0000に初期化する
  • 0x0000から始めて、セグメントレジスタを0x07c0に初期化する

int 0x10

0x10はVideo Servicesに関連する割り込み
1. AHレジスタにファンクションコードを指定
2. ALレジスタにビデオモードを指定
3. int 0x10実行
4. AHレジスタに0x0Eを指定しint 0x10を実行することでALレジスタの文字を書き込む(終端コードまでループ)
したがって、コードでは以下のような流れになる

        mov     al, 0x00
        mov     ah, 0x03
        int     0x10

        mov     si, msg
        mov     ah, 0x0e

println:
        lodsb
        or      al, al          ; if \0                                                                                                      
        jz      hang            ; jump to hang                                                                                               

        int     0x10
        jmp     println

ASCIIコード

とりあえず知っておくべきもの
* キャリッジリターン 13
* ラインフィード 10
* 終端 0

MBRの最後

MBRの511バイト目と512バイト目は0xaa 0x55という決まり

HelloWorld

前述のことをまとめて、最初のステップとしてHelloWorldを表示するだけのブートローダを作成した。

boot.asm
[bits 16]
[org 0x7c00]
        ;; BPB Structure                                                                                                                     
        jmp     start           ;BS_jmpBoot                                                                                                  
BS_OEMName      db "MYTESTOS"
BPB_BytsPerSec  dw 0x0200
BPB_SecPerClus  db 0x08
BPB_RsvdSecCnt  dw 0x0020
BPB_NumFATs     db 0x02
BPB_RootEntCnt  dw 0x0000
BPB_TotSec16    dw 0x0000
BPB_Media       db 0xf8
BPB_FATSz16     dw 0x0000
BPB_SecPerTrk   dw 0x003f
BPB_NumHeads    dw 0x00ff
BPB_HiddSec     dd 0x0000003f
BPB_TotSec32    dd 0x00f17fc1
BPB_FATSz32     dd 0x00003c40
BPB_ExtFlags    dw 0x0000
BPB_FSVer       dw 0x0000
BPB_RootClus    dd 0x00000002
BPB_FSInfo      dw 0x0001
BPB_BkBootSec   dw 0x0006
        times   12      db 0    ;BPB_Reserverd                                                                                               
BS_DrvNum       db 0x80
BS_Reserved1    db 0x00
BS_BootSig      db 0x29
BS_VolID        dd 0xa0a615c
BS_VolLab       db "TESTOS BOOT"
BS_FileSysType  db "FAT32   "

        ;; boot code                                                                                                                         
start:
        ;; initialize segment registers                                                                                                      
        xor     ax, ax
        mov     ds, ax          ; Data Segment                                                                                               
        mov     es, ax
        mov     fs, ax
        mov     gs, ax
        mov     ss, ax          ; Stack Segment                                                                                              
        mov     sp, 0x7c00      ; Stack Pointer                                                                                              


        mov     al, 0x00
        mov     ah, 0x03
        int     0x10

        mov     si, msg
        mov     ah, 0x0e

println:
        lodsb
        or      al, al          ; if \0                                                                                                      
        jz      hang            ; jump to hang                                                                                               

        int     0x10
        jmp     println

hang:
        hlt
        jmp     hang
msg:
        db      "Hello, World", 13, 10, 0

        times   510-($-$$) db 0
        dw      0xaa55

とりあえずQEMUでの動作確認はできた

参考