「手探りでCUI OS作成に挑む」連載
この記事は「手探りでCUI OS作成に挑む」連載の一部です。
全体の目次は「手探りでCUI OS作成に挑む」連載目次を御覧下さい。
HDD
# 128MBの仮想HDD生成
dd if=/dev/zero of=virtual_disk.img bs=1M count=128
FAT16初期化
FAT16形式、1クラスタ=4kb(8セクタ)でHDDを初期化する。
qemu-system-i386 \
-m 1024 \
-hda xp.img \
-hdb virtual_disk.img \
-boot c \
-cpu host \
-smp 2 \
-net nic -net user \
-vga std \
-rtc base=localtime \
-enable-kvm \
-usbdevice tablet
BPBの情報
VBRのダンプ
test@test-ThinkPad-X280:~$ xxd -s $((512*63)) -l 512 virtual_disk.img
00007e00: eb3c 904d 5344 4f53 352e 3000 0208 0200 .<.MSDOS5.0.....
00007e10: 0200 0200 00f8 7f00 3f00 1000 3f00 0000 ........?...?...
00007e20: b1f3 0300 8000 2926 74bf 5c4e 4f20 4e41 ......)&t.\NO NA
00007e30: 4d45 2020 2020 4641 5431 3620 2020 33c9 ME FAT16 3.
00007e40: 8ed1 bcf0 7b8e d9b8 0020 8ec0 fcbd 007c ....{.... .....|
00007e50: 384e 247d 248b c199 e83c 0172 1c83 eb3a 8N$}$....<.r...:
00007e60: 66a1 1c7c 2666 3b07 268a 57fc 7506 80ca f..|&f;.&.W.u...
00007e70: 0288 5602 80c3 1073 eb33 c98a 4610 98f7 ..V....s.3..F...
00007e80: 6616 0346 1c13 561e 0346 0e13 d18b 7611 f..F..V..F....v.
00007e90: 6089 46fc 8956 feb8 2000 f7e6 8b5e 0b03 `.F..V.. ....^..
00007ea0: c348 f7f3 0146 fc11 4efe 61bf 0000 e8e6 .H...F..N.a.....
00007eb0: 0072 3926 382d 7417 60b1 0bbe a17d f3a6 .r9&8-t.`....}..
00007ec0: 6174 324e 7409 83c7 203b fb72 e6eb dca0 at2Nt... ;.r....
00007ed0: fb7d b47d 8bf0 ac98 4074 0c48 7413 b40e .}.}....@t.Ht...
00007ee0: bb07 00cd 10eb efa0 fd7d ebe6 a0fc 7deb .........}....}.
00007ef0: e1cd 16cd 1926 8b55 1a52 b001 bb00 00e8 .....&.U.R......
00007f00: 3b00 72e8 5b8a 5624 be0b 7c8b fcc7 46f0 ;.r.[.V$..|...F.
00007f10: 3d7d c746 f429 7d8c d989 4ef2 894e f6c6 =}.F.)}...N..N..
00007f20: 0696 7dcb ea03 0000 200f b6c8 668b 46f8 ..}..... ...f.F.
00007f30: 6603 461c 668b d066 c1ea 10eb 5e0f b6c8 f.F.f..f....^...
00007f40: 4a4a 8a46 0d32 e4f7 e203 46fc 1356 feeb JJ.F.2....F..V..
00007f50: 4a52 5006 536a 016a 1091 8b46 1896 9233 JRP.Sj.j...F...3
00007f60: d2f7 f691 f7f6 4287 caf7 761a 8af2 8ae8 ......B...v.....
00007f70: c0cc 020a ccb8 0102 807e 020e 7504 b442 .........~..u..B
00007f80: 8bf4 8a56 24cd 1361 6172 0b40 7501 4203 ...V$..aar.@u.B.
00007f90: 5e0b 4975 06f8 c341 bb00 0060 666a 00eb ^.Iu...A...`fj..
00007fa0: b04e 544c 4452 2020 2020 2020 0d0a 5265 .NTLDR ..Re
00007fb0: 6d6f 7665 2064 6973 6b73 206f 7220 6f74 move disks or ot
00007fc0: 6865 7220 6d65 6469 612e ff0d 0a44 6973 her media....Dis
00007fd0: 6b20 6572 726f 72ff 0d0a 5072 6573 7320 k error...Press
00007fe0: 616e 7920 6b65 7920 746f 2072 6573 7461 any key to resta
00007ff0: 7274 0d0a 0000 0000 0000 00ac cbd8 55aa rt............U.
BPBから取り出した今欲しい情報
1クラスタ:8セクタ
FAT表:127セクタ
予約セクタ:2セクタ
FAT表1
開始位置:63(パーティション開始位置) + 2(予約セクタ)=65
test@test-ThinkPad-X280:~$ xxd -s $((512*65)) -l 512 virtual_disk.img
00008200: f8ff ffff 0300 0400 0500 0600 0700 0800 ................
00008210: 0900 0a00 0b00 0c00 0d00 0e00 0f00 1000 ................
00008220: 1100 ffff 0000 0000 0000 0000 0000 0000 ................
00008230: 0000 0000 0000 0000 0000 0000 0000 0000 ................
断片化せず綺麗に連続して並んでいる。
ルートディレクトリ
開始位置:63(パーティション開始位置) + 2(予約セクタ) + 127×2(FAT1+FAT2)=319
test@test-ThinkPad-X280:~$ xxd -s $((512*319)) -l 512 virtual_disk.img
00027e00: d0c2 bcd3 beed 2020 2020 2008 0000 0000 ...... .....
00027e10: 0000 0000 0000 0399 e35a 0000 0000 0000 .........Z......
00027e20: 5445 5354 2020 2020 5458 5420 186a 7699 TEST TXT .jv.
00027e30: e35a e35a 0000 6a99 e35a 0200 92f4 0000 .Z.Z..j..Z......
00027e40: 0000 0000 0000 0000 0000 0000 0000 0000 ................
ファイル
ルートディレクトリの大きさ=32(1ファイルあたり)×512 / 512=32クラスタ
開始位置:63(パーティション開始位置) + 2(予約セクタ) + 127×2(FAT1+FAT2) + 32
test@test-ThinkPad-X280:~$ xxd -s $((512*351)) -l 512 virtual_disk.img
0002be00: 3031 3233 3435 3637 3839 3031 3233 3435 0123456789012345
0002be10: 3637 3839 3031 3233 3435 3637 3839 3031 6789012345678901
0002be20: 3233 3435 3637 3839 3031 3233 3435 3637 2345678901234567
0002be30: 3839 3031 3233 3435 3637 3839 3031 3233 8901234567890123
0002be40: 3435 3637 3839 3031 3233 3435 3637 3839 4567890123456789
0002be50: 3031 3233 3435 3637 3839 3031 3233 3435 0123456789012345
自前MBR中のBPBをXPに合わせる
先ほどダンプを取ったMBRから解析したBPBは以下の通り
オフセット | 大きさ | 16進値 (リトルエンディアン) | 10進値 | 説明 |
---|---|---|---|---|
0x00 | 3 | EB 3C 90 | - | ジャンプ命令 (JMP 3C NOP) |
0x03 | 8 | "MSDOS5.0" | - | OEM名 |
0x0B | 2 | 0x0200 | 512 | バイト/セクタ |
0x0D | 1 | 0x08 | 8 | セクタ/クラスタ |
0x0E | 2 | 0x0002 | 2 | 予約セクタ数 |
0x10 | 1 | 0x02 | 2 | FAT数 |
0x11 | 2 | 0x0000 | 0 | ルートエントリ数 (FAT16では0) |
0x13 | 2 | 0x0000 | 0 | 総セクタ数16 (0 = FAT32または64KB以上) |
0x15 | 1 | 0xF8 | 248 | メディアタイプ (固定ディスク) |
0x16 | 2 | 0x007F | 127 | セクタ/FAT |
0x18 | 2 | 0x003F | 63 | セクタ/トラック |
0x1A | 2 | 0x0010 | 16 | ヘッド数 |
0x1C | 4 | 0x000003F3 | 1011 | 非表示セクタ数 |
0x20 | 4 | 0x00008000 | 32768 | 総セクタ数32 |
0x24 | 1 | 0x00 | 0 | 物理ドライブ番号 |
0x25 | 1 | 0x00 | 0 | 予約 |
0x26 | 1 | 0x29 | 41 | 拡張ブート署名 |
0x27 | 4 | 0x2674BF5C | 644,829,020 | ボリュームシリアル番号 |
0x2B | 11 | "NO NAME " | - | ボリュームラベル |
0x36 | 8 | "FAT16 " | - | ファイルシステムタイプ |
このMBRをこのまま使うと自作OSが起動できないため、BPBの部分をまるまる自分のMBRへ入れる
書き直したMBR
[BITS 16]
[ORG 0x7C00] ; VBRはアドレス0x7C00に読み込まれる
; FAT16のBPB
jmp start
nop
db "MSDOS5.0" ; OEM名(8bitに満たない場合は空白埋め)
dw 512 ; バイト/セクタ(0x0200)
db 8 ; セクタ/クラスタ(4KBクラスタ)
dw 2 ; 予約セクタ数
db 2 ; FAT数
dw 0 ; ルートディレクトリエントリ数
dw 0 ; 総セクタ数(16bit領域、0で代わりにTotSec32を使用)
db 0xF8 ; メディアタイプ(固定ディスク)
dw 127 ; FATサイズ(セクタ数、後述計算)
dw 63 ; セクタ/トラック(LBA互換)
dw 16 ; ヘッド数(LBA互換)
dd 1011 ; 隠しセクタ数
dd 32768 ; 総セクタ数
; 拡張BPB (FAT16用)
db 0 ; BIOSドライブ番号(0x80=HDD)
db 0 ; 予約
db 0x29 ; 拡張ブート署名
dd 0x2674BF5C ; ボリュームID(任意)
db 'NO NAME ' ; ボリュームラベル
db 'FAT16 ' ; ファイルシステム名
start:
mov si, msg_loaded
call print_string
; kernelは128セクタ分(65536バイト=64KB),LBA=351
; KERNEL.BIN は LBA 351 セクタ目から始まる
mov ah, 0x02
mov al, 128 ; 128セクタ読み込む
mov ch, 0 ; Cylinder 0
mov cl, 36 ; Sector 36
mov dh, 5 ; Head 5
mov dl, 0x80 ; HDD
mov bx, 0x8000 ; 読み込み先アドレス
int 0x13
jc load_error
jmp 0x0000:0x8000 ; kernelの開始アドレスへ跳ぶ
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
ret
load_error:
mov si, error_msg
call print_string
msg_loaded db "[VBR] Execution started at 0x0000:0x7C00", 0x0D, 0x0A, 0
error_msg db "Failed to load KERNEL.BIN", 0x0D, 0x0A, 0
; 残りの領域を512バイトまで埋める
times 510-($-$$) db 0
dw 0xAA55
FAT表及びルートディレクトリを自前で作る
fat16_init.asm
; FAT表及びルートディレクトリを初期化する。
; DDで65セクタ目からの位置に書き込む。(63+予約セクタ2)
; 固定でKERNEL.BINを一つ登録しておく。
; 前提:
; - MBRは0セクタ目に設置
; - VBRは63セクタ目に設置(パーティションはMBR中で63セクタ目から始まるように設定してある)
; - FATの情報は65から始まる
bits 16
; ------------------------------
; FAT1表LBA 64〜始まる (DDでこの場所に置く)
; ------------------------------
fat1:
dw 0xFFF8 ; クラスタ0:予約
dw 0xFFFF ; クラスタ1:予約
; KERNEL.BIN
; 64kb / 4kb = 16クラスタ
dw 0x0300
dw 0x0400
dw 0x0500
dw 0x0600
dw 0x0700
dw 0x0800
dw 0x0900
dw 0x0a00
dw 0x0b00
dw 0x0c00
dw 0x0d00
dw 0x0e00
dw 0x0f00
dw 0x1000
dw 0x1100
dw 0xFFFF
;KERNEL.BIN終端
; FAT表の剰余分を0で埋める (127セクタ×512バイト-既に書き込んだ分のバイト数)
times 65024 - ($ - fat1) db 0
; ------------------------------
; FAT2表(FAT表1の予備 FAT1の直後)簡略化の為に0埋め
; ------------------------------
fat2:
dw 0xFFF8 ; クラスタ0:予約
dw 0xFFFF ; クラスタ1:予約
; KERNEL.BIN
; 64kb / 4kb = 16クラスタ
dw 0x0300
dw 0x0400
dw 0x0500
dw 0x0600
dw 0x0700
dw 0x0800
dw 0x0900
dw 0x0a00
dw 0x0b00
dw 0x0c00
dw 0x0d00
dw 0x0e00
dw 0x0f00
dw 0x1000
dw 0x1100
dw 0xFFFF
;KERNEL.BIN終端
times 65024 - ($ - fat2) db 0
; ------------------------------
; ルートディレクトリ(FAT2の直後,LBA 319)
; ------------------------------
root_dir:
; KERNEL.BINの登録(32字节)
db 'KERNEL BIN' ; ファイル名(8.3形式)
db 0x20 ; 属性(0X20は通常のファイルを意味する)
db 0 ; 保留
db 0 ; 作成時間(ミリ秒)
dw 0x0000 ; 作成時間(16:00:00)
dw 0x2100 ; 作成日時(2023-01-01)
dw 0x2100 ; 最終変更日時
dw 0 ; EA索引
dw 0x0000 ; 最終変更時間
dw 0x2100 ; 最終変更日時
dw 2 ; 開始クラスタ
dd 65536 ; ファイルの大きさ(バイト)
; ルートディレクトリが32セクタとなるように0で埋める。
times 512 * 32 - ($ - root_dir) db 0
以下の手順で書き込む
cd boot # nasm -f bin boot/fat16_init.asm -o fat16_init.binがなぜか実行できない為苦肉の策
nasm -f bin mbr.asm -o mbr.bin
nasm -f bin vbr.asm -o vbr.bin
nasm -f bin fat16_init.asm -o fat16_init.bin
cd ..
nasm -f bin kernel.asm -o kernel.bin
# 128MBの仮想HDD生成
dd if=/dev/zero of=virtual_disk.img bs=1M count=128
# MBRを先頭512バイトへ書き込み
dd if=boot/mbr.bin of=virtual_disk.img bs=512 count=1 conv=notrunc
# VBRを63セクタ目へ書き込み「第1パーティションの先頭512バイト」
dd if=boot/vbr.bin of=virtual_disk.img bs=512 seek=63 conv=notrunc
# ルートディレクトリ及びFAT表を65バイト目〜に書き込み
dd if=boot/fat16_init.bin of=virtual_disk.img bs=512 seek=65 conv=notrunc
# カーネル部分
dd if=kernel.bin of=virtual_disk.img bs=512 seek=351 conv=notrunc
XPで読み込むと初期化されていないと表示される。
自分で作った時はHファイル名が表示され、ファイルが開けないだけだった。
XPで初期化したHDDに合わせるとHDDそのものが開けなくなった。
全く原因が分からない。
既に10時間以上ファイルシステムに費やしていて気が狂いそうなので今日はやめる。