ARM系Linuxの移植の際に読んだkernelソースについての備忘録(間違いだらけかもしれませんがご了承をw)。
電源投入からkernelが呼び出されるまでを大雑把に順を追って見ていくと:
・ターゲットの電源が投入されファームウェアによってマシンの初期化が実行される
・ファームは何かしらの方法でkernelのイメージ(zImageやuImage)をメモリに読み込む
(HDDから読み込んだり、ROMからRAMへ転送したり(ROMから直接起動する場合はこの限りではない))
・kernelのイメージに制御を移す(jumpする)
・圧縮されている場合は指定のアドレスに展開しkernel本体に制御を移す
・展開されたkernelが起動する(ここからが本格的なkernelの起動となる)
これで一通りの初期化が行われ、最初のプロセス"init"が動き通常モードになる。
予めターゲットに搭載されているファーム(LS-GLではu-boot)に依存しているので細かい動作は異なる。但し、arm-linuxではr0=0、r1=マシンアーキテクチャID(MACH_TYPE_xxx)、r2=TAG引数へのポインタを渡してkernelが呼び出されることを期待しているので(きっと)これに従っているのだと思う。
kernel本体が圧縮されている場合、arch/arm/boot/compressed/head.Sのstartから開始する
.align
start:
.type start,#function
.rept 8
mov r0, r0
.endr
b 1f
.word 0x016f2818 @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
1: mov r7, r1 @ save architecture ID ←マシンアーキテクチャID退避
mov r8, r2 @ save atags pointer ←TAG引数ポインタ退避
コンパイル/リンク時にldが意図した配置と実際に読み込まれたイメージ配置とをチェックし異なる場合はGlobal Offset Tableを書き換える。ldの配置情報はLC0に埋め込まれている。
.type LC0, #object
LC0: .word LC0 @ r1 ←ldが意図したLC0自身のアドレス
.word __bss_start @ r2
.word _end @ r3
.word zreladdr @ r4 ←zImage展開後のkernel配置アドレス
.word _start @ r5
.word _got_start @ r6
.word _got_end @ ip
.word user_stack+4096 @ sp
LC1: .word reloc_end - reloc_start
.size LC0, . - LC0
これをr1-r6、ip、spに読み込み、実際のLC0のアドレスとldが意図したLC0アドレスに違いがないかをチェックする。違いがあった場合は、その差をオフセット値としてGOT等を変更し是正する。
同じARM系でも各実装によってメモリマッピングが異なるので、zreladdrの値等はarch/arm/mach-xxxの各ディレクトリにあるMakefile.bootに記載されている。
adr r0, LC0 ←実際のLC0のアドレスを取得する
ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}
subs r0, r0, r1 @ calculate the delta offset
call_kernel: bl cache_clean_flush
bl cache_off
mov r0, #0 @ must be zero
mov r1, r7 @ restore architecture number
mov r2, r8 @ restore atags pointer
mov pc, r4 @ call kernel
kernel本体はarch/arm/kernel/head.Sのstextから開始する
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags pointer.
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
*
* We're trying to keep crap to a minimum; DO NOT add any machine specific
* crap here - that's what the boot loader (or in extreme, well justified
* circumstances, zImage) is for.
*/
.section ".text.head", "ax"
.type stext, %function
ENTRY(stext)
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
bl __lookup_machine_type @ r5=machinfo
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
bl __vet_atags
bl __create_page_tables
/*
* The following calls CPU specific code in a position independent
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
* xxx_proc_info structure selected by __lookup_machine_type
* above. On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.
*/
ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
adr lr, __enable_mmu @ return (PIC) address
add pc, r10, #PROCINFO_INITFUNC
r13に次に飛ぶアドレスを入れ