Raspberry Pi2(Linux Kernel)のブートシーケンスを読む(その2) アーキテクチャ依存部

##前回
[「電源投入からKernelに制御が移行するまで」](http://qiita.com/Mogura1215/items/b8f866b4ede757cdaa73)を説明しました。
目的や使用機材についても記載があります。

##Kernelのソースコードを読む前に(事前準備)
私のような初心者は、途中でソースコードを追えなくなります。
また、正しいコードを読んでいるのかを確認するために、ブート時のログを取得します。

ログは、Raspberry Piの起動後に以下のコマンドを入力する事で取得できます。
piはユーザ名です。boot_log.txtは好きなファイル名で問題ありません。
`$ dmesg > /home/pi/Desktop>boot_log.txt`

コマンド入力後、以下の内容が記載されたテキストファイルが出力されます。
>例
>[    0.000000] Booting Linux on physical CPU 0xf00
>[    0.000000] Initializing cgroup subsys cpuset
>[    0.000000] Initializing cgroup subsys cpu
>[    0.000000] Initializing cgroup subsys cpuacct
>(以下省略)

このログメッセージを以下のように検索する事で、該当する処理が記載されたファイルが分かります。
この際、慣れないと正しいファイルが判断できない事があります。慣れるしかないです。
`$ cd /home/pi/Desktop/linux-rpi-4.1.y`
`$ grep -nr "Booting Linux on physical CPU" *`


##Kernel内のENTRY(stext)へ移動
一番最初の起動ログを検索すると、
`$ grep -nr "Booting Linux on physical CPU" *`
linux-rpi-4.1.y/arch/arm/kernel/setup.c内の"void notrace cpu_init(void)"が
該当の処理と分かります。

しかし、実際はこの関数に辿り着く前に、細かな処理を行っています。
Raspberry Pi2はARM CPU(Cortex-A7)なので、
linux-rpi-4.1.y/arch/arm/kernel/head.SのENTRY(stext)から処理が開始されます。

ENTRY(stext)は、kernel.imgの先頭からオフセット(0x8000)を足した位置にあります。
オフセットの値を知る方法は、以下の2通りです。
　・start.elfを逆アセンブル：`$ arm-none-eabi-objdump -m arm -D start.elf`
　・linux-rpi-4.1.y/arch/arm/kernel/vmlinux.ldsの中身を読む

ENTRY(stext)を実行した直後のCPUの状態は、以下の通りです。
>MMU     = OFF
>D-cache = OFF
>I-cache = Don't care
>r0      = 0
>r1      = machine number
>r2      = atargs or DTB pointer

[MMU(メモリ管理ユニット)](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E7%AE%A1%E7%90%86%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88)はOFF、
D-Cache(データキャッシュ)もOFF、
I-cache(命令キャッシュ)はON/OFFどちらでも問題がありません。
r0は0(ARM CPUのr0は引数渡しではなく、返り値を渡す時に使用されるため)、
r1はlinux-rpi-4.1.y/arch/arm/tools/mach-typesに定義されているボード固有を格納し、
r2はatags構造体かDTB(Device Tree Blob)へのポインタが格納されています。

r0~r2に値を入れたのは、Bootloaderです。
atags構造体、DTB(Device Tree)は、ボード固有のハードウェア情報を記述したものです。
前者はKernel内(arch/arm/mach-*/)にハードコーディングするのに対して、
後者はKernel外(Device Tree)に記述しています。
一般的に、ARM環境(Raspberry Pi)ではDTBが使用されます。

##ENTRY(stext)での処理(CPU IDの取得まで)
以下に概略を記載します。

```linux-rpi-4.1.y/arch/arm/kernel/head.S
ENTRY(stext)
 ARM_BE8(setend	be )			@ ensure we are in BE8 mode

 THUMB(	adr	r9, BSYM(1f)	)	@ Kernel is always entered in ARM.
 THUMB(	bx	r9		)	@ If this is a Thumb-2 kernel,
 THUMB(	.thumb			)	@ switch to Thumb now.
 THUMB(1:			)

#ifdef CONFIG_ARM_VIRT_EXT
	bl	__hyp_stub_install
#endif
	@ ensure svc mode and all interrupts masked
	safe_svcmode_maskall r9

	mrc	p15, 0, r9, c0, c0		@ get processor id
	bl	__lookup_processor_type		@ r5=procinfo r9=cpuid
	movs	r10, r5				@ invalid processor (r5=0)?
 THUMB( it	eq )		@ force fixup-able long branch encoding
	beq	__error_p			@ yes, error 'p'
```

　1）CPUを[ビッグエンディアン形式](https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%87%E3%82%A3%E3%82%A2%E3%83%B3)に変更
　2）CPUの動作モードをARM(32bit命令モード)に変更
　3）CPUを[スーパバイザモード](http://yougo.ascii.jp/caltar/%E3%82%B9%E3%83%BC%E3%83%91%E3%83%BC%E3%83%90%E3%82%A4%E3%82%B6%E3%83%BC%E3%83%A2%E3%83%BC%E3%83%89)に変更
　4）全ての割り込みをマスク
　5）[c0(プロセッサに関する情報を含むレジスタ)](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0363fj/Bgbceeed.html)からCPU IDをr9に格納
　6）__lookup_processor_typeにより、r5にproc_info_list構造体へのポインタを格納

```linux-rpi-4.1.y/arch/arm/kernel/head-common.S
__lookup_processor_type:
	adr	r3, __lookup_processor_type_data
	ldmia	r3, {r4 - r6}
	sub	r3, r3, r4			@ get offset between virt&phys
	add	r5, r5, r3			@ convert virt addresses to
	add	r6, r6, r3			@ physical address space
1:	ldmia	r5, {r3, r4}			@ value, mask
	and	r4, r4, r9			@ mask wanted bits
	teq	r3, r4
	beq	2f
	add	r5, r5, #PROC_INFO_SZ		@ sizeof(proc_info_list)
	cmp	r5, r6
	blo	1b
	mov	r5, #0				@ unknown processor
2:	ret	lr
ENDPROC(__lookup_processor_type)
```

まず、proc_info_listへのポインタをCPU ID(r9)を用いて入手します。
r4には__proc_info_begin(仮想アドレス)、r6には__proc_info_end(仮想アドレス)が格納され、
これらのポインタを物理アドレスに変換します。
(このタイミングでは、MMUが有効化されていないため)

次に、ldmia命令でr3にCPU IDを格納し、r4にCPU MASKを格納します。
r4(__proc_info_begin)とr9(CPU ID)の論理積をとります。

最後に、r3(CPU ID)とr4(__proc_info_beginとCPU IDの論理積)を比較します。
一致していれば、ラベル2:に移動し、ret命令により、呼び出し元にリターンします。
値が異なる場合、ポインタをオフセット分(PROC_INFO_SZ)移動させ、
次のproc_info_list構造体の中身を調べます(ラベル1：に飛びます)。
このオフセットは、linux-rpi-4.1.y/arch/arm/kernel/asm-offsets.c内で、
`DEFINE(PROC_INFO_SZ, sizeof(struct proc_info_list));`と定義されています。

##proc_info_list構造体について
以下の構造体はアセンブリ言語で定義されている(C言語ではない)。

```linux-rpi-4.1.y/arch/arm/include/asm/procinfo.h
struct proc_info_list {
	unsigned int		cpu_val;
	unsigned int		cpu_mask;
	unsigned long		__cpu_mm_mmu_flags;	/* used by head.S */
	unsigned long		__cpu_io_mmu_flags;	/* used by head.S */
	unsigned long		__cpu_flush;		/* used by head.S */
	const char		*arch_name;
	const char		*elf_name;
	unsigned int		elf_hwcap;
	const char		*cpu_name;
	struct processor	*proc;
	struct cpu_tlb_fns	*tlb;
	struct cpu_user_fns	*user;
	struct cpu_cache_fns	*cache;
};
```

定義は以下の通りです(後日追記します)

```linux-rpi-4.1.y/arch/arm/mm/proc-v7.S
	/*
	 * Standard v7 proc info content
	 */
.macro __v7_proc name, initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions
	ALT_SMP(.long	PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
			PMD_SECT_AF | PMD_FLAGS_SMP | \mm_mmuflags)
	ALT_UP(.long	PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
			PMD_SECT_AF | PMD_FLAGS_UP | \mm_mmuflags)
	.long	PMD_TYPE_SECT | PMD_SECT_AP_WRITE | \
		PMD_SECT_AP_READ | PMD_SECT_AF | \io_mmuflags
	initfn	\initfunc, \name
	.long	cpu_arch_name
	.long	cpu_elf_name
	.long	HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | \
		HWCAP_EDSP | HWCAP_TLS | \hwcaps
	.long	cpu_v7_name
	.long	\proc_fns
	.long	v7wbi_tlb_fns
	.long	v6_user_fns
	.long	v7_cache_fns
.endm
```

##次回
[「Raspberry Pi2(Linux Kernel)のブートシーケンスを読む(その3)」](http://qiita.com/Mogura1215/items/0f29cf4361dce85bfe0f)
start_kernelに移行するまで(アーキテクチャ依存部の最後まで)の説明を記述しています。

##参考サイト
[Device Tree 入門](http://masahir0y.blogspot.jp/2014/05/device-tree.html)
[Device Tree についてのまとめ](http://qiita.com/koara-local/items/ed99a7b96a0ca252fc4e)