こちらも基本的に詳細はコメントに書かれているのでわかりやすい!
!*===========================================================================* !* trampoline_pm * !*===========================================================================* ! This is the protected mode section of the trampoline ! Load apropiate values into registers and jump to ! ap_main c function to start execution _trampoline_pm: ! We are in protected mode. Load AP registers as in BSP mov ax, DS_SELECTOR mov ds, ax ! load DS mov ss, ax ! load SS mov es, ax ! load ES mov fs, ax ! load FS mov gs, ax ! load GS mov eax, ( _esi_data ) mov esi, eax ! load ESI mov eax, ( _edi_data ) mov edi, eax ! load EDI mov eax, ( _ebp_data ) mov ebp, eax ! load EBP
ここら辺まではプロテクトモード用にレジスタの設定をしている。
! Load TSS of this ap THIS_CPU(eax) dec eax ! AP# less 1 shl eax, 3 ! mult DESC_SIZE add eax, TSS_FIRST_AP_SELECTOR ltr ax ! load TSS
ここはコメントで説明しているようにTSSを設定している部分。
TSSを「はじめて読む486]で調べると「Task State Segment」の略で、タスクの状態の保存・復元に利用するデータ領域のことと言う風に説明している。
OSDev.orgにも説明あります。Linuxはどうしているのかって説明が英語版のウィキペディアにありました。
ともかく、CPU自体がタスク切り替えの仕組みを提供しているので、それをminixで使うためには設定が必要。
TSSもgdt、idtのようにTSSディスクリプタの先頭アドレス、リミット値、属性を設定するテーブルの作成とTSSディスクリプタを作る必要がある。
THIS_CPUはxmp.hで定義しているマクロ。
/* This secuence provides in <reg> current cpu number from 0 to n-1 Registers <reg>, EDX, DS are afected SHORT version does not restores DS_SELECTOR in DS <reg> can be EAX, EBX, ECX */ #define THIS_CPU_SHORT(reg) ;\ mov edx, (_local_apic_base) ;\ add edx, 0x20 ;\ mov reg, FLAT_DS_SELECTOR ;\ mov ds, reg ;\ mov reg, (edx) ;\ and reg, 0x0F000000 ;\ shr reg, 6*4 #define THIS_CPU(reg) ;\ THIS_CPU_SHORT(reg) ;\ mov edx, DS_SELECTOR ;\ mov ds, dx
TSS_FIRST_AP_SELECTORはprotect.hで定義しているマクロで、以下のように定義。
#define TSS_FIRST_AP_SELECTOR 0x70 /* TSS_AP_INDEX(1) * DESC_SIZE) */
コメント中に出てくる掛け算の内容はどういうことかと言うと、
protect.hにいる2個のマクロを使った掛け算で、
#define TSS_AP_INDEX(n) (13+n) /* TSS for AP 1,2,3... */ #define DESC_SIZE 8 /* sizeof (struct segdesc_s) */
(13 + 1) * 8 = 112 = 0x70
って感じで、TSS_FIRST_AP_SELECTORが0x70として定義される。
ltr命令はTSSをTRレジスタに読み込むための命令。
TRレジスタは現在のTSSを保持するためのレジスタで、常にカレントTSSを保持する。
! Now we are ready to address as usual and execute normal ! kernel code, so, lets go ! Each CPU needs its own stack space inside kernel stack ! Make esp point to cpus stack top THIS_CPU(eax) SET_CPU_STK(eax) ! Enable AP cache call _enable_cache ! Continue in C code call _ap_main hlt
TSSをTRレジスタに設定して、ついにcで書かれた関数をコールする準備ができてきた。
THIS_CPUは先ほども出てきたマクロなので飛ばして、次のSET_CPU_SKTを調べると・・
こんな感じのマクロがxmp.hに定義されている。
これもコメントのままだけど、カーネルで使うスタック領域の設定をしている部分。
/****************************************************************/ /* Macro definitions for kernel stack management */ /****************************************************************/ #define K_STACK_BYTES_SH 10 /* log2 K_STACK_BYTES */ /* K_STACK_BYTES MUST BE POWER OF 2 */ #if (K_STACK_BYTES != (1<<K_STACK_BYTES_SH)) #error "ERROR: K_STACK_BYTES_SH is not log2(K_STACK_BYTES)" #endif /* This secuence sets kernel stack to the top of kernel stack depending of current cpu number stored in <reg> Registers <reg>, EDX, ESP are afected <reg> can be EAX, EBX, ECX */ #define SET_CPU_STK(reg) ;\ shl reg, K_STACK_BYTES_SH ;\ mov edx, k_stktop ;\ sub edx, reg ;\ mov esp, edx
enable_caheはCR0レジスタの29,30ビット目のNWとCDビットを触ってると思う・・・
CDビットはCache Disableの略でCDとNWビットが両方0だとキャッシュが有効になるらしい。
NWはNow Write throughの略。
!*===========================================================================* !* enable_cahe * !*===========================================================================* ! Enable cache on current cpu _enable_cache: mov ax, cr0 and ax, 0x9FFFFFFF ! 100111111111...111 mov cr0, ax wbinv ret
TSS、キャッシュの設定が終わって最後にc言語で書かれたap_main()をコールする。(mp.c内に本体がいる)
"_"がついているのはお約束的なもの。
! Continue in C code call _ap_main
続きはまた、