|
0から作るOS開発 カーネルローダその4 カーネルをロードする
|
前回までの内容
これまでで、
- CR0制御レジスタのPEビットを1にすることで32ビットのプロテクティッドモードへ移行できる
- プロテクティッドモードへの移行と同時にJMP命令でコードセグメントのセグメントセレクタを設定する
- プロテクティッドモード用の32ビットコードを作成する
- IN命令、OUT命令でフロントサイドバスに接続されているI/Oデバイスにアクセスできる
- キーボードコントローラを制御することでA20以上のアドレスラインを有効にし、4GBのメモリ空間を利用できるようにする
ということがわかりました。それではカーネルローダの開発を進めていきましょう!
今回はブートローダとカーネルローダの総まとめです
カーネルローダ メインを作っていく
カーネルローダその1
で紹介しましたカーネルローダメインにモジュールを追加していく形で進めていきます
最初のカーネルローダは”
Starting.asm”で作成しました
最初に作成したStarting.asmファイル
[BITS 16]
ORG 0x500
JMP MAIN2
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Preprocessor directives
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
%include "Print.inc"
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Data Section
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
msgphello DB 0x0D, 0x0A, "Hello", 0x0D, 0x0A, 0x00
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Kernel Procedure
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
MAIN2:
; 汎用レジスタ初期化
XOR AX, AX
XOR BX, BX
XOR CX, CX
XOR DX, DX
; データセグメント初期化
MOV DS, AX
MOV ES, AX
; メッセージ表示
MOV SI, msgphello
CALL DisplayMessage
HLT
でした。ここではPrint.incは割愛させていただきます
スタックの初期化とGDTの設定を追加する
スタックの初期化とGDTの設定を追加します。スタックはリアルモードで使用可能な
最大メモリ(0x0009FFFCあたり)をスタックとして使用します
使用メモリについては
カーネルローダその1を参照してください
スタックについては
ブートローダその8を参照してください
また、GDTについては
カーネルローダその2を参照してください
Starting.asmファイル
処理を追加していきます
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Kernel Procedure
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
MAIN2:
; MAIN2の続き
MOV AX, 0x9000 ; スタックポインタを0x0009FFFCに設定する
MOV SS, AX
MOV SP, 0xFFFC
CALL _setup_gdt ; GDT設定処理関数をコール
; MAIN2が続いていく
[BITS 16]
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Set up IDT
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
_setup_idt: ; GDT設定処理
CLI
PUSHA
LIDT [idt_toc] ; GDTを読み込む
STI
POPA
RET
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Global Descriptor Table
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
gdt_toc:
DW 4*8
DD _gdt
_gdt:
DW 0x0000 ; Null descriptor
DW 0x0000
DW 0x0000
DW 0x0000
DB 0xFF ; Code descriptor
DB 0xFF
DW 0x0000
DB 0x00
DB 10011010b
DB 11001111b
DB 0
DB 0xFF ; Data descriptor
DB 0xFF
DW 0x0000
DB 0x00
DB 10010010b
DB 11001111b
DB 0
DW 0x0000 ; TEMPORARY - don't use
DW 0x0000
DW 0x0000
DW 0x0000
次にカーネルを読み込んでいきます
カーネルのロード
カーネルのロードには
- FAT12ファイルシステムのルートディレクトリ領域でカーネルイメージファイルを探す
- ファイルが見つかればカーネルイメージの最初のクラスタ番号を取得する
- FAT領域のクラスタ番号を取得し、読み込みたいLBAを計算する
- LBAから読み込みたいフロッピーディスクのヘッド番号、シリンダ番号、セクタ番号を計算してINT 0x13で読み込む
を行います。ほとんど
ブートローダその11ででやった内容ですが、少しパワーアップしています
ルートディレクトリ領域でカーネルイメージファイルを探す
カーネルを読み込む前に時間がかかるのでローディングメッセージを表示させています
Starting.asmファイル
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Preprocessor directives
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
%include "Print.inc"
%include "common.inc"
%include "Fat12.inc"
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Data Section
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
LoadingMsg DB 0x0D, 0x0A, "Searching for System...", 0x00
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Kernel Procedure
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
MAIN2:
; MAIN2の続き
MOV SI, LoadingMsg ; ローディングメッセージ表示
CALL DisplayMessage
CALL Find_File ; Find_File関数をコール
CMP AX, 0 ; カーネルイメージが見つかったかどうかチェック
; MAIN2が続いていく
カーネルイメージを探すFind_File関数は”
Fat12.inc”で定義しています
ブートローダその11での復習のつもりで”
Fat12.inc”を見て行きましょう
Fat12.incファイル
Fat12.incではFAT12ファイルシステムのドライバを記述しています
;**********************************************************************************
; FAT12 filesystem read only function
;
;**********************************************************************************
%ifndef __FAT12_INC_INCLUDED__
%define __FAT12_INC_INCLUDED__
[BITS 16]
%include "BPB.inc"
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Variable
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
KernelImageCluster DW 0x0000 ; 見つかったカーネルイメージの最初のクラスタ番号を保存します
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Define
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
%define ES_BASE_ADDR 0x07C0 ; ブートローダのセグメント開始位置
%define RMODE_BASE_SEG 0x0800 ; カーネルを読み込むセグメント開始位置(0x00008000)
%define RMODE_BASE_ADDR 0x0000
%define BX_FAT_ADDR 0x0200 ; 0x000007C00をベースアドレスとしたBXに設定するアドレス
; ブートローダが読み込んだFAT領域のアドレス
%define BX_RTDIR_ADDR 0x2600 ; 0x000007C00をベースアドレスとしたBXに設定するアドレス
; ブートローダが読み込んだルートディレクトリ領域のアドレス
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Find Kernel File
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
Find_File: ; カーネルイメージを探索する
PUSHA
MOV BX, ES_BASE_ADDR
MOV ES, BX
MOV BX, BX_RTDIR_ADDR
MOV CX, WORD [BPB_RootEntCnt] ; BPB.incに定義
MOV SI, KernelImageName ; common.incに定義
Finding_File:
MOV DI, BX
PUSH CX
MOV CX, 0x000B
PUSH DI
PUSH SI
REPE CMPSB
POP SI
POP DI
JCXZ Found_File
ADD BX, 0x0020
POP CX
; 次のエントリへ
LOOP Finding_File
JMP FAILURE
FAILURE:
POPA
MOV AX, -1 ; 見つからなかったときは返り値としてAXに-1を返す
RET
Found_File:
POP CX
MOV WORD [KernelImageCluster], BX ; 見つかったファイルの最初のクラスタ番号を保存
POPA
MOV AX, 0 ; 見つかった場合は返り値としてAXに0を返す
RET
%endif
ブートローダその11からの変更点は
- BPB_RootEntCntは”BPB.inc”に定義
- 読み込むカーネルイメージファイル名は”common.inc”に定義(MAIN2のインクルードを参照してください)
- ルートディレクトリ領域自体の読み込みはブートローダが行っているので、それを再利用している
- ファイルが見つからなかった場合は-1をAXレジスタに返り値として返す
- ファイルが見つかった場合は、見つかったファイルの最初のクラスタ番号を”KernelImageCluster”に保存
- ファイルが見つかった場合は0をAXレジスタに返り値として返す
としています。ここで
ブートセクタのBPBを再定義している”
BPB.inc”を見ていきます
(ブートローダが置いてあるアドレスを見に行くようにしてみるのも面白いと思います。ちょっとめんどくさいですが。。。)
BPB.incファイル
BPB.incでは
FAT12で使用するブートセクタのBPBを定義しています
;**********************************************************************************
; BIOS parameter block
;
;**********************************************************************************
%ifndef __BPB_INC_INCLUDED__
%define __BPB_INC_INCLUDED__
[BITS 16]
BS_OEMName DB "MyOS "
BPB_BytsPerSec DW 0x0200 ;BytesPerSector
BPB_SecPerClus DB 0x01 ;SectorPerCluster
BPB_RsvdSecCnt DW 0x0001 ;ReservedSectors
BPB_NumFATs DB 0x02 ;TotalFATs
BPB_RootEntCnt DW 0x00E0 ;MaxRootEntries
BPB_TotSec16 DW 0x0B40 ;TotalSectors
BPB_Media DB 0xF0 ;MediaDescriptor
BPB_FATSz16 DW 0x0009 ;SectorsPerFAT
BPB_SecPerTrk DW 0x0012 ;SectorsPerTrack
BPB_NumHeads DW 0x0002 ;NumHeads
BPB_HiddSec DD 0x00000000 ;HiddenSector
BPB_TotSec32 DD 0x00000000 ;TotalSectors
BS_DrvNum DB 0x00 ;DriveNumber
BS_Reserved1 DB 0x00 ;Reserved
BS_BootSig DB 0x29 ;BootSignature
BS_VolID DD 0x20090321 ;VolumeSerialNumber
BS_VolLab DB "MyOS " ;VolumeLabel
BS_FilSysType DB "FAT12 " ;FileSystemType
%endif
このように再定義しておけばプログラムから直ぐに利用できて便利です
(%defineでもよいかと思います)
次に”
common.inc”を見ていきます
common.incファイル
common.incではカーネルローダで使用する共通変数などを定義しています
%ifndef _COMMON_INC_INCLUDED
%define _COMMON_INC_INCLUDED
; プロテクティッドモードでカーネルイメージを置くアドレス
%define IMAGE_PMODE_BASE 0x100000
; リアルモードでカーネルイメージを置くアドレス
%define IMAGE_RMODE_BASE 0x8000
; カーネルイメージのファイル名(FAT12では11文字にしなければならない)
KernelImageName DB "KIMAGE "
; カーネルイメージのファイルサイズを格納する変数
ImageSize DD 0x00000000
ImageSizeBX DW 0x0000
ImageSizeES DW 0x0000
%endif
各定義は次のように使用しています
- IMAGE_PMODE_BASEは、プロテクティッドモード時にカーネルイメージを置くアドレスを定義しています
- IMAGE_RMODE_BASEは、リアルモード時にカーネルイメージを置くアドレスを定義しています
- KernelImageNameは、読み込みたいカーネルイメージのファイル名を定義しています。
FAT12の仕様から11バイトである必要があります
- ImageSizeは、読み込むカーネルイメージのファイルサイズをバイト単位で格納します
- ImageSizeBXは、読み込み済みのカーネルイメージのセグメントオフセット値を格納します
- ImageSizeESは、読み込み済みのカーネルイメージのセグメント開始位置を格納します
カーネルローダではリアルモードでBIOSを利用してカーネルイメージを読み込みます
ですので、このカーネルローダでは読み込めるカーネルイメージは600Kバイト程度となります
カーネルローダその1で説明したように、4GBのメモリ空間を利用できるようにして、
プロテクティッドモードでカーネルイメージを読み込みたいところなのですが、プロテクティッドモードではBIOSが利用できません
(INT命令で利用するBIOSの割り込み処理は16ビット命令ですので、プロテクティッドモードではもはや利用できません)
BIOSが利用できませんので、自前でフロッピーディスクドライバを作成する必要があります
自前で作成してもよいのですが、後でカーネルにフロッピーディスクドライバを組み込んでいきたいのと
世の中には賢い方がいっぱいいらっしゃって、
GRUBというブートローダがあります
このサイトでも後々GRUBを利用していきますので、とりあえず今のところ、リアルモードでカーネルを読み込んでいきます
(とりあえずは600Kバイトほどのカーネルが読み込みできますので、相当困ることはありません。困る方はGRUBを導入していきましょう)
上記の理由から、まずリアルモードでカーネルイメージを一時領域(このサイトでは0x00008000)に読み込んでおき、
さしあたっては、プロテクティッドモードに移行してから一時領域にあるカーネルを0x00010000にコピーする方針をとります
カーネルイメージを読み込む
カーネルイメージが見つかりましたのでカーネルイメージを読み込んでいきます
まずはメイン処理の”
Starting.asmです
Starting.asmファイルの続き
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Data Section
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
msgFoundFile DB 0x0D, 0x0A, "System is found!!!", 0x00
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Kernel Procedure
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
MAIN2:
; MAIN2の続き
Load_Kernel: ; カーネルをロードする処理
MOV SI, msgFoundFile ; カーネルが見つかったメッセージを表示
CALL DisplayMessage
CALL Load_File ; カーネルをロードする関数をコール
; MAIN2が続く
カーネルをロードする処理を見ていきます。これも
ブートローダその11
の内容から修正を加えて、
”
Fat12.inc”ファイルに追加しています
Fat12.incファイル
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Load Kernel File
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
Load_File:
PUSHA
MOV WORD [datasector], 0x0021
MOV BX, ES_BASE_ADDR
MOV ES, BX
MOV BX, WORD [KernelImageCluster] ; カーネルイメージの開始クラスタ番号
ADD BX, 0x001A
MOV AX, WORD [ES:BX]
MOV BX, RMODE_BASE_SEG ; カーネルを読み込むセグメント
MOV ES, BX
MOV BX, RMODE_BASE_ADDR ; カーネルを読み込むアドレス
PUSH BX
MOV WORD [cluster], AX
Load_Image:
MOV AX, WORD [cluster]
POP BX
XOR BX, BX
CALL ClusterLBA
XOR CX, CX
MOV CL, BYTE [BPB_SecPerClus]
CALL ReadSector
MOV BX, ES
ADD BX, 0x0020 ; ESを0x200だけずらす
MOV ES, BX ; ESを更新する(ESは読み込んだバイト数になる)
ES_ADDED:
PUSH BX
; 次のクラスタ番号取得
MOV AX, WORD [cluster]
MOV CX, AX
MOV DX, AX
SHR DX, 0x0001
ADD CX, DX ; 次のクラスタ番号オフセット取得
PUSH ES ; ESをスタックへ
MOV BX, ES_BASE_ADDR
MOV ES, BX
MOV BX, BX_FAT_ADDR ; ブートローダが読み込んだFATを利用
ADD BX, CX ; 次のクラスタ番号のオフセットを足す
MOV DX, WORD [ES:BX]
POP ES ; ESを復帰(読み込んだバイト数)
TEST AX, 0x0001
JNZ ODD_CLUSTER
EVEN_CLUSTER:
AND DX, 0x0FFF
JMP LOCAL_DONE
ODD_CLUSTER:
SHR DX, 0x0004
LOCAL_DONE:
MOV WORD [cluster], DX
CMP DX, 0x0FF0
JB Load_Image
ALL_DONE:
POP BX
XOR BX, BX
MOV WORD [ImageSizeBX], BX ; 0クリアしています(無駄な処理です)
MOV BX, ES
SUB BX, RMODE_BASE_SEG ; リードしたバイト数を計算
MOV WORD [ImageSizeES], BX ; 保存する
POPA
RET
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; ReadSector
; Read 1 Sector
; Input: BX:address
; : AX:sector number
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
ReadSector:
MOV DI, 0x0005 ; エラーがあった場合5回まで繰り返す
SECTORLOOP:
PUSH AX
PUSH BX
PUSH CX
CALL LBACHS
MOV AH, 0x02 ; BIOSのリードセクタ
MOV AL, 0x01 ; 1セクタ読み込み
MOV CH, BYTE [absoluteTrack] ; シリンダ
MOV CL, BYTE [absoluteSector] ; セクタ
MOV DH, BYTE [absoluteHead] ; ヘッド
MOV DL, BYTE [BS_DrvNum] ; ドライブ
INT 0x13 ; BIOS処理を呼出す
JNC SUCCESS ; エラーチェック
MOV AH, 0x01
INT 0x13
XOR AX, AX ; フロッピーディスクをリセット
INT 0x13 ; BIOS処理を呼出す
DEC DI ; エラーカウンタを減らす
POP CX
POP BX
POP AX
JNZ SECTORLOOP ; 処理を繰り返す
INT 0x18
SUCCESS:
POP CX
POP BX
POP AX
RET
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
; PROCEDURE ClusterLBA
; convert FAT cluster into LBA adressing scheme
; LBA = (cluster - 2 ) * sectors per cluster
; INPUT : AX : cluster number
; OUTPUT : AX : base image sector
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
ClusterLBA:
SUB AX, 0x0002
XOR CX, CX
MOV CL, BYTE [BPB_SecPerClus]
MUL CX
ADD AX, WORD [datasector]
RET
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
; PROCEDURE LBACHS
; convert LBA addressing scheme to CHS addressing scheme
; absolutesector = (logical sector / setors per track) + 1
; absolute head = (logical sector / sectors per track) MOD number of heads
; absolute track = (logical sector / ( sectors per track * number of heads ) )
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
LBACHS:
XOR DX, DX
DIV WORD [BPB_SecPerTrk]
INC DL
MOV BYTE [absoluteSector], DL
XOR DX, DX
DIV WORD [BPB_NumHeads]
MOV BYTE [absoluteHead], DL
MOV BYTE [absoluteTrack], AL
RET
ブートローダその11からの変更点は
- RMODE_BASE_SEGが示すセグメントにカーネルイメージを読み込む
- RMODE_BASE_ADDRが示すセグメントのオフセットにカーネルイメージを読み込む
- 読み込む先のセグメントの開始位置(ES)を0x200(512バイト)ずつ更新する
- 読み込んだカーネルイメージのバイト数をImageSizeESに格納する
となります。ESが読み込んだバイト数をあらわしていますがセグメントレジスタですので、ESの値×0x10が
読み込んだバイト数となることに注意してください(やけにややこしいプログラムしてます。。。)
これでカーネルイメージの読み込みは完了しました。次にプロテクティッドモードへ移行します
プロテクティッドモードへの移行
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Preprocessor directives
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
%include "Print.inc"
%include "common.inc"
%include "Fat12.inc"
%include "A20.inc"
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Define Section
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
%define NULL_DESC 0
%define CODE_DESC 0x8
%define DATA_DESC 0x10
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Kernel Procedure
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
MAIN2:
; MAIN2の続き
EnableA20:
CALL Enable_A20
Enter_pmode:
CLI
MOV EAX, CR0
;without paging
OR EAX, 0x00000001
MOV SI, msgpmode
CALL DisplayMessage
MOV CR0, EAX
JMP CODE_DESC:Pmode_start
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Protected Mode
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
[BITS 32]
Pmode_start:
MOV AX, DATA_DESC
MOV SS, AX
MOV ES, AX
MOV FS, AX
MOV GS, AX
MOV DS, AX
MOV ESP, 90000h
; MAIN2が続く
ここではA20を有効にしてプロテクティッドモードへ移行しています
- Enable_A20関数で4GBのメモリ空間を利用できるようにする
- CR0制御レジスタにPEビットを1にしてプロテクティッドモードへ移行する
- JMP命令でコードセグメントセレクタを変更しつつ32ビット命令へジャンプ
- セグメントセレクタを初期化し、スタックポインタを初期化する
A20を有効にするソースは
カーネルローダその3と同様となりますので割愛させていただきます
カーネルイメージのコピーとカーネル起動
カーネルの読み込みが終わったので、カーネルを0x00010000へコピーし、カーネルを起動します
Starting.asmファイル
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
;
; Starting Protected Mode
;
;/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
[BITS 32]
Pmode_start: ; プロテクティッドモード開始
MOV AX, DATA_DESC ; セグメントセレクタを初期化
MOV SS, AX
MOV ES, AX
MOV FS, AX
MOV GS, AX
MOV DS, AX
MOV ESP, 90000h
CopyKernelImage: ; カーネルイメージを0x00010000へコピー
; get kernel image size
XOR EAX, EAX
MOVZX EAX, WORD [ImageSizeES] ; 読み込んだバイト数を取得
SHL EAX, 0x4 ; 読み込んだバイト数(セグメントレジスタの値)
; から実バイト数を計算(0x10をかける)
MOV DWORD [ImageSize], EAX ; カーネルイメージのサイズを保存
; copy
CLD ; ディレクションフラグをクリア
MOV ESI, IMAGE_RMODE_BASE ; 読み込んだカーネルイメージの先頭アドレス
MOV EDI, IMAGE_PMODE_BASE ; コピー先のアドレス(0x00010000)
MOV ECX, EAX
REP MOVSD ; ESIからEDIのアドレスに1バイトずつコピー
; (ECXのバイト数分繰り返す)
JMP EXECUTE ; カーネル実行処理へジャンプ
Failure2: ; 失敗時の処理。無限ループ
HLT
JMP Failure2
EXECUTE:
;-------------------------------------------
; Execute Kernel
;-------------------------------------------
MOV EBX, IMAGE_PMODE_BASE ; カーネルの先頭アドレスをEBXに入れる
MOV EBP, EBX ; そしてEBPに入れる
XOR EBX, EBX
CLI ; 割り込み禁止
CALL EBP ; カーネルのエントリを関数コールします
ADD ESP, 4
JMP Failure2
プロテクティッドモードに入ってからの処理を行っています
ここの処理では
- セグメントセレクタとスタックポインタの初期化
- 読み込んだカーネルイメージのサイズを計算。ちょっと複雑な処理をしてしまっています
カーネルを読み込んだときにESにサイズを憶えていたので、ESの値×0x10を計算しなければ
いけないようになってしまいました。。。
- リアルモードで読み込んだカーネルイメージを0x00010000へコピー
- カーネルが置かれている0x00010000を関数コールすることでカーネルを実行している
となります。少しあほな処理となっていますが、もっときちんとした処理を実装していただければとおもいます
また、アセンブラ言語かC言語かでフロッピーディスクドライバを実装して、プロテクティッドモードでカーネルを
読み込めるようにして、大きなカーネルでもロードできるようにしても面白いとおもいます
また、今後はこの方法とは別にGRUBでのカーネル起動も紹介していきたいと思います
(ちょっと後になりますが。。。)
以上でブートローダからカーネルローダを読み込み、カーネルローダからカーネルを読み込んで実行することが
できました。次回はいよいよカーネルの実装へいきたいと思います