Edited at

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

More than 3 years have passed since last update.

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での動作確認はできた


参考