「手探りでCUI OS作成に挑む」連載
この記事は「手探りでCUI OS作成に挑む」連載の一部です。
全体の目次は「手探りでCUI OS作成に挑む」連載目次を御覧下さい。
仮想HDDを作成しFAT16で初期化
BPBを見たいだけなのでパーティションは作らない。
# 1. 16MBの空ファイルを作成
dd if=/dev/zero of=disk.img bs=1M count=16
# 2. ループデバイスに割り当て
sudo losetup -f --show disk.img
# 例えば /dev/loop0 と表示される
# 3. ループデバイスに直接FAT16を作る(パーティションテーブルなしでOK)
sudo mkfs.fat -F 16 /dev/loop0
# 4. マウント用ディレクトリを作る
mkdir mnt
# 5. マウントして中身を見る
sudo mount /dev/loop0 mnt
ls mnt # 空のはず
# 6. 書き込みなどしてみる
echo "hello" | sudo tee mnt/hello.txt
# 7. 中身確認
ls mnt
cat mnt/hello.txt
# 8. 終わったらアンマウント&ループデバイス解除
sudo umount mnt
sudo losetup -d /dev/loop0
FAT16 ブートセクタ(BPB)解析
ダンプを取る
xxd -l 512 disk.img
PBP
test@test-ThinkPad-X280:~/kaihatsu/disk$ xxd -l 512 disk.img
00000000: eb3c 906d 6b66 732e 6661 7400 0204 0400 .<.mkfs.fat.....
00000010: 0200 0200 80f8 2000 2000 0200 0000 0000 ...... . .......
00000020: 0000 0000 8000 294d 8f33 6a4e 4f20 4e41 ......)M.3jNO NA
00000030: 4d45 2020 2020 4641 5431 3620 2020 0e1f ME FAT16 ..
00000040: be5b 7cac 22c0 740b 56b4 0ebb 0700 cd10 .[|.".t.V.......
00000050: 5eeb f032 e4cd 16cd 19eb fe54 6869 7320 ^..2.......This
00000060: 6973 206e 6f74 2061 2062 6f6f 7461 626c is not a bootabl
00000070: 6520 6469 736b 2e20 2050 6c65 6173 6520 e disk. Please
00000080: 696e 7365 7274 2061 2062 6f6f 7461 626c insert a bootabl
00000090: 6520 666c 6f70 7079 2061 6e64 0d0a 7072 e floppy and..pr
000000a0: 6573 7320 616e 7920 6b65 7920 746f 2074 ess any key to t
000000b0: 7279 2061 6761 696e 202e 2e2e 200d 0a00 ry again ... ...
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000100: 0000 0000 0000 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 0000 0000 0000 0000 ................
00000190: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa ..............U.
オフセット | バイト数 | 項目 | 16進値 | 説明 |
---|---|---|---|---|
0x00 | 3 | ジャンプ命令 | EB 3C 90 | ブートセクタ先頭へのジャンプ |
0x03 | 8 | OEM名 | 6D 6B 66 73 2E 66 61 74 | "mkfs.fat"作成ツール名 |
0x0B | 2 | バイト/セクタ | 00 02 | 512 バイト/セクタ |
0x0D | 1 | セクタ/クラスタ | 04 | 1クラスタ = 4セクタ |
0x0E | 2 | 予約セクタ数 | 00 04 | 予約領域 4セクタ |
0x10 | 1 | FATコピー数 | 02 | FATコピー数 2 |
0x11 | 2 | ルートディレクトリエントリ数 | 00 02 | 512エントリ(リトルエンディアン) |
0x13 | 2 | 総セクタ数(小) | 80 F8 | 0xF880 = 63680 セクタ |
0x15 | 1 | メディアタイプ | F8 | メディアタイプ |
0x16 | 2 | FATサイズ(セクタ数) | 20 00 | FATサイズ 32 セクタ |
0x18 | 2 | セクタ/トラック | 20 00 | 32 セクタ/トラック |
0x1A | 2 | ヘッド数 | 00 00 | 0(未設定) |
0x1C | 4 | 隠しセクタ数 | 00 00 00 00 | 0(未設定) |
0x20 | 4 | 総セクタ数(大) | 00 00 00 00 | 0の場合、小さい方を使う |
0x24 | 1 | ドライブ番号 | 00 | 0x80 = 固定ディスク |
0x25 | 1 | 予約 | 00 | 予約 |
0x26 | 1 | 目印 | 00 | 0x29 固定 |
0x27 | 4 | ボリュームシリアル番号 | 00 00 00 00 | シリアル番号 |
0x2B | 11 | ボリュームラベル | 00 00 00 00 00 00 00 00 00 00 00 | "NO NAME "(スペース) |
0x36 | 8 | ファイルシステムタイプ | 00 00 00 00 00 00 00 00 | "FAT16 " |
0x1FE | 2 | ブートセクタ目印 | 55 AA | MBRの最後の目印 |
各領域の開始セクタ計算
領域名 | 計算式 | 結果(セクタ番号) | 説明 |
---|---|---|---|
予約領域開始 | 0 | 0 | ブートセクタ開始 |
予約領域終了 | 予約セクタ数 - 1 | 3 | 予約領域は4セクタ分 |
FAT1開始 | 予約セクタ数 | 4 | 予約領域の直後から |
FAT1終了 | FAT1開始 + FATサイズ - 1 | 35 | 32セクタ分 |
FAT2開始 | FAT1終了 + 1 | 36 | FATの2つ目コピー |
FAT2終了 | FAT2開始 + FATサイズ - 1 | 67 | 32セクタ分 |
ルートディレクトリ開始 | FAT2終了 + 1 | 68 | FATコピーの直後 |
ルートディレクトリサイズ | ルートディレクトリエントリ数 × 32 / バイト/セクタ | 512 × 32 / 512 = 32 | 512エントリ × 32バイト / 512バイト/セクタ |
データ領域開始 | ルートディレクトリ開始 + ルートディレクトリサイズ | 68 + 32 = 100 | ここからクラスタ領域(ファイルデータ) |
FAT1のダンプ
開始セクタ = 予約セクタ数 = 4
開始バイト = 4 × 512 = 2048 バイト
test@test-ThinkPad-X280:~/kaihatsu/disk$ xxd -s 2048 -l 16384 disk.img
00000800: f8ff ffff 0000 ffff 0000 0000 0000 0000 ................
00000810: 0000 0000 0000 0000 0000 0000 0000 0000 ................
オフセット | クラスタ番号 | 内容 (リトルエンディアン) | 状態 |
---|---|---|---|
0x0000 | 0 | F8FF → 0xFFF8 | メディア識別+予約領域 |
0x0002 | 1 | FFFF → 0xFFFF | 使用中(終端マーク) |
0x0004 | 2 | 0000 → 0x0000 | 空きクラスタ |
0x0006 | 3 | FFFF → 0xFFFF | 使用中(終端マーク) |
0x0008〜 | 4〜 | 0000 → 空きクラスタ | 空きクラスタ |
1クラスタ未満のファイルしか保存していない為、FAT1は終端の情報しかない。
ルートディレクトリのダンプ
開始バイト = 68 × 512 = 34816 バイト
test@test-ThinkPad-X280:~/kaihatsu/disk$ xxd -s 34816 -l 16384 disk.img
00008800: 4168 0065 006c 006c 006f 000f 00f1 2e00 Ah.e.l.l.o......
00008810: 7400 7800 7400 0000 ffff 0000 ffff ffff t.x.t...........
00008820: 4845 4c4c 4f20 2020 5458 5420 0099 9665 HELLO TXT ...e
00008830: de5a de5a 0000 9665 de5a 0300 0600 0000 .Z.Z...e.Z......
00008840: 0000 0000 0000 0000 0000 0000 0000 0000 ................
ファイルは1つなのに2つのエントリが見えるが、上の方は長いファイル名(LFN)用の特殊なエントリなので、自作OSを作る上では無視してよい。
項目 | 値 | 内容 |
---|---|---|
オフセット 0x00 | 41 | 順番(ビット7=1で最後のLFN)0x41=最後のエントリ |
0x01〜0x0A | 68 00 65 00 6C 00 6C 00 6F 00 | UTF-16文字列 "hello"
|
0x0B | 0F | 属性(LFNの固定値) |
0x0C | 00 | 型(常に0、予約) |
0x0D | F1 | チェックサム(8.3エントリ名との対応用) |
0x0E〜0x19 | 2E 00 74 00 78 00 74 00 | UTF-16文字列 "txt" (拡張子部分) |
0x1A〜0x1B | FF FF | 未使用領域 |
0x1C〜0x1D | FF FF | 未使用領域 |
0x1E〜0x1F | FF FF | 未使用領域 |
オフセット | 内容 | 説明 |
---|---|---|
0x00〜0x07 | 48 45 4C 4C 4F 20 20 20 → "HELLO " | ファイル名部分(8文字固定、パディングあり) |
0x08〜0x0A | 54 58 54 → "TXT" | 拡張子部分(3文字固定) |
0x0B | 00 | 属性(通常ファイル、属性なし) |
0x0C | 99 | 予約(システム用、通常無視) |
0x0D〜0x0E | 96 65 | 作成時間(詳細は別途解析必要) |
0x0F〜0x10 | DE 5A | 作成日(詳細は別途解析必要) |
0x11〜0x12 | DE 5A | 最終アクセス日 |
0x13〜0x14 | 00 00 | 高位クラスタ番号(FAT16では未使用) |
0x15〜0x16 | 96 65 | 最終更新時間 |
0x17〜0x18 | DE 5A | 最終更新日 |
0x1A〜0x1B | 03 00 → 0x0003 | 先頭クラスタ番号 |
0x1C〜0x1F | 06 00 00 00 → 0x00000006 | ファイルサイズ(バイト単位) |
ファイルのダンプ
ルートディレクトリによると先頭クラスタ番号 = 0x0003
データ領域開始セクタ = 予約セクタ数 + (FAT数 × FATセクタ数) + ルートディレクトリセクタ数
ルートディレクトリエントリ数 = 0x0200 = 512エントリ
1エントリ = 32バイト → 512エントリ × 32バイト = 16384バイト = 32セクタ
予約セクタ数 = 0x0004 = 4セクタ
FAT数 = 2
FATサイズ = 0x0020 = 32セクタ
データ領域開始 = 4 + (2 × 32) + 32 = 100セクタ目
先頭クラスタ3の位置 = データ領域開始 + (クラスタ番号 - 2) × セクタ/クラスタ
クラスタ番号 = 3、セクタ/クラスタ = 4
なので:
先頭クラスタ3の開始セクタ = 100 + (3 - 2) × 4 = 104セクタ目
test@test-ThinkPad-X280:~/kaihatsu/disk$ xxd -s 53248 -l 512 disk.img
0000d000: 6865 6c6c 6f0a 0000 0000 0000 0000 0000 hello...........
0000d010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
ダンプを取るとテキストファイルの中身が確認出来る。