##前回
「ENTRY(stext)でproc_info_list構造体へのポインタを取得するまで」を説明しました。
##Kernel内のENTRY(stext)の続き(PHYS_OFFSET取得まで)
#ifdef CONFIG_ARM_LPAE
mrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0
and r3, r3, #0xf @ extract VMSA support
cmp r3, #5 @ long-descriptor translation table format?
THUMB( it lo ) @ force fixup-able long branch encoding
blo __error_lpae @ only classic page table format
#endif
まず、上記の処理を通るには、
コンフィグでLPAE(Large Physical Address Extension)を有効にする必要があります。
LPAE(仮想化拡張機能)は、ページテーブルを2つ用意する手法で、
ハイパーバイザを使用した際のスループットを維持するための仕組みのようです。
ちなみに、前回の説明でハイパーバイザのインストール処理を飛ばしたので、
上記の説明も割愛いたします(後日、ハイパーバイザと共に別記事を書き、リンクを貼ります)。
次の処理に進みます。
#ifndef CONFIG_XIP_KERNEL
adr r3, 2f
ldmia r3, {r4, r8}
sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
add r8, r8, r4 @ PHYS_OFFSET
#else
ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case
#endif
上記の処理は、 Kernel Execute-In-Place(XIP)が有効/無効で分岐します。
ここでXIPとは、RAMにコード(.textセクションの内容、関数)を展開する事なく、
不揮発性フラッシュメモリ上のコードに直接アクセスして実行する形式です。
私はXIPを使用した事がないので、アドレス空間の問題とか気になります。
ここでの処理の目的は、RAMに展開したデータが物理アドレス0x0000_0000番地から始まらない場合(開始アドレスが0x0000_0000 + PHYS_OFFSETの場合)に備えて、
r8にPHYS_OFFSETを入れる事です。
XIPが有効の場合は、r8にPLAT_PHYS_OFFSETを格納します。
PLAT_PHYS_OFFSETは、
linux-rpi-4.1.y/arch/arm/mach-bcm2709/include/mach/memory.h
内で、
#define PLAT_PHYS_OFFSET UL(CONFIG_PHYS_OFFSET)
と定義されています。
CONFIG_PHYS_OFFSETは、
arch/arm/configs/bcm2709_defconfig
内で、CONFIG_PHYS_OFFSET=0
と定義されています。
「値が0なら、この処理は不要では?」と思いますが、
このオフセット値は__virt_to_phys()
マクロで使用されるため、定義する必要があります。
XIPが無効の場合、r3にPAGE_OFFSETへのラベル(アドレス)を格納します。
PAGE_OFFSETへのラベル(2:)は、head.S内にあります。
2: .long .
.long PAGE_OFFSET
ここからがアセンブリ言語が読めなくて、
ldmia命令によって、r3(ラベルへのアドレス)から、r4、r8に値をロードされる筈。
どのレジスタに、どの値を?
最終的には、r8にPHYS_OFFSETが格納されますが、それまでの加算減算も理解できてません。
後日、追記します。
この時点での汎用レジスタの状態は、以下の通りです。
r1 = machine number
r2 = dtb
r8 = phys_offset
r9 = cpu_id
r10 = proc_info
##Kernel内のENTRY(stext)の続き(ページテーブルの作成まで)
bl __vet_atags
#ifdef CONFIG_SMP_ON_UP
bl __fixup_smp
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
bl __fixup_pv_table
#endif
bl __create_page_tables
__vet_atagsの定義は、linux-rpi-4.1.y/arch/arm/head-common.S
内にあります。
この関数では、r2に格納されているポインタ(ATAGS or DTB)の妥当性を確認します。
r2の内容と妥当性を確認するための定数とを比較し、一致しない場合はr2を0にします。
一致している場合は、何も行いません。
__fixup_smp(linux-rpi-4.1.y/arch/arm/head.S
)の処理は、
CONFIG_SMP_ON_UP(SMP=Symmetric Multi-Processors)が有効の場合に通る。
Raspberry Pi2のCPUは複数のコアが搭載されているので、SMPに対応しています。
肝心の処理ですが、CPUがSMPに対応しているかどうかの確認以外、
読み取ることができませんでした(後日、追記します)。
__fixup_pv_table(linux-rpi-4.1.y/arch/arm/head.S
)の処理も、
CONFIG_ARM_PATCH_PHYS_VIRTが有効の場合のみ通ります。
この関数では、arch/arm/include/asm/memory.h
内の「仮想アドレス-物理アドレス」を
変換する関数やマクロが使用する変数を決定します。
__create_page_tables(linux-rpi-4.1.y/arch/arm/head.S
)では、
r4にページテーブルのアドレスを格納します。その後の処理はページテーブルの作成です。
##Kernel内のENTRY(stext)の続き(MMUの有効化まで)
ldr r13, =__mmap_switched @ address to jump to after
@ mmu has been enabled
adr lr, BSYM(1f) @ return (PIC) address
mov r8, r4 @ set TTBR1 to swapper_pg_dir
ldr r12, [r10, #PROCINFO_INITFUNC]
add r12, r12, r10
ret r12
1: b __enable_mmu
ENDPROC(stext)
__mmap_switched(linux-rpi-4.1.y/arch/arm/head-common.S
)は、
MMUをONにした後に実行するstart_kernel(linux-rpi-4.1.y/init/main.c
)を
r13(SP)に格納する。
ここでの処理は、こちらのサイトが参考になります。
最後に、__enable_mmuでMMUがONになった後、
start_kernel(アーキテクチャに依存しない処理、C言語)に処理が移ります。
##疑問点
・XIPが有効の場合は、何故PLAT_PHYS_OFFSETが0なのか。
・__fixup_smpの処理内容
・アセンブラをステップ実行する方法
##次回
start_kernel()のカナリアコードをスタックに埋め込むまでを説明しています。