第三章第二節 mkimage工具
製作Linux核心的壓縮映象檔案,需要使用到mkimage工具。mkimage這個工具位於u-boot-2013. 04中的tools目錄下,它可以用來製作不壓縮或者壓縮的多種可啟動映象檔案。mkimage在製作映象檔案的時候,是在原來的可執行映象檔案的前面加上一個16個byte(0x40)的頭,用來記錄引數所指定的資訊,這樣u-boot才能識別出製作出來的這個映象是針對哪一個CPU體系結構、哪一種OS、哪種型別、載入到記憶體中的哪個位置、入口點在記憶體的哪個位置以及映象名是什麼等資訊。在/u-boot-2013.04/tools目錄下執行./mkimage,輸出資訊如下所示:
zhuzhaoqi@zhuzhaoqi-desktop:~/u-boot/u-boot-2013.04/u-boot-2013.04/tools$./mkimage
Usage: ./mkimage -l image
-l==> list image header information
./mkimage[-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A==> set architecture to 'arch'
-O==> set operating system to 'os'
-T==> set image type to 'type'
-C==> set compression type 'comp'
-a==> set load address to 'addr' (hex)
-e==> set entry point to 'ep' (hex)
-n==> set image name to 'name'
-d==> use image data from 'datafile'
-x==> set XIP (execute in place)
./mkimage[-D dtc_options] -f fit-image.its fit-image
./mkimage-V ==> print version information and exit
表3. 1 CPU體系結構
取值
表示的體系結構
取值
表示的體系結構
alpha
Alpha
arm
ARM
x86
Intel x86
ia64
IA64
mips
MIPS
mips64
MIPS 64 Bit
ppc
PowerPC
s390
IBM S390
sh
SuperH
sparc
SPARC
sparc64
SPARC 64 Bit
m68k
MC68000
針對上面的輸出資訊,-A 指定CPU的體系結構,也就是說,arch的取值可以是如表3. 1所示。
-O 指定作業系統型別,os可以取:openbsd、netbsd、freebsd、4_4bsd、linux、svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos。
-T 指定映象型別,type可以是:standalone、kernel、ramdisk、multi、firmware、script、filesystem。
-C 指定映象壓縮方式,comp可以是:none(不壓縮)、gzip( 用gzip的壓縮方式)、bzip2 (用bzip2的壓縮方式)。
-a 指定映象在記憶體中的載入地址,映象下載到記憶體中時,要按照用mkimage製作映象時,這個引數所指定的地址值來下載。
-e 指定映象執行的入口點地址,這個地址就是-a引數指定的值加上0x40(因為前面有個mkimage新增的0x40個位元組的頭)。
-n 指定映象名。
-d 指定製作映象的原始檔。
將u-boot-2013.04下的tools這個資料夾下中的mkimage工具複製到ubuntu系統的/user/bin下,這樣可以直接當作操作命令使用。
第三章第三節 載入地址和入口地址
在上一節中,無法啟動核心,導致的原因可能是載入地址、入口地址等導致的。執行./mkimage之後如下:
zhuzhaoqi@zhuzhaoqi-desktop:~/u-boot/u-boot-2013.04/u-boot-2013.04/tools$ ./mkimage
./mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
其中-a addr指的就是映象在記憶體中的載入地址,映象下載到記憶體中時,要按照用mkimage製作映象時,這個引數所指定的地址值來下載。
而-e ep 是指定映象執行的入口點地址。
還有兩個概念需要明白,即是bootm address和kernel執行地址。bootm address:通過uboot的bootm命令,從address啟動kernel。kernel執行地址:在具體mach目錄中的Makefile.boot中指定,是kernel啟動後實際執行的實體地址。
如果bootm address和Load Address相等,在這種情況下,bootm不會對uImage header後的zImage進行memory move的動作,而會直接go到Entry Point開始執行。因此此時的Entry Point必須設定為Load Address+ 0x40。如果kernel boot過程沒有到uncompressing the kernel,就可能是這裡設定不對。它們之間的關係為:boom address == Load Address == Entry Point - 0x40。
如果bootm address和Load Address不相等(但需要避免出現memory move時出現覆蓋導致zImage被破壞的情況)。此種情況下,bootm會把uImage header後的zImage檔案move到Load Address,然後go到entry point開始執行。這段程式碼在common/cmd_bootm.c中bootm_load_os函式中,如下程式所示。由此知道此時的Load Address必須等於Entry Point。它們之間的關係則為:boom address != Load Address == Entry Point。
case IH_COMP_NONE:
if (load == blob_start || load == image_start)
{
printf(" XIP %s ... ", type_name);
no_overlap = 1;
}
else
{
printf(" Loading %s ... ", type_name);
memmove_wd((void *)load, (void *)image_start,
image_len, CHUNKSZ);
}
*load_end = load + image_len;
puts("OK\n");
break;
zImage的頭部有地址無關的自解壓程式,因此剛開始執行的時候,zImage所在的記憶體地址(Entry Point)不需要同編譯kernel的地址相同。自解壓程式會把kernel解壓到編譯時指定的實體地址,然後開始地址相關程式碼的執行。在開啟MMU之前,kernel都是直接使用實體地址(可參看核心符號對映表System.map)。
通過上面的分析,大概找出了問題的根源,由於bootm address和Load Address都為50008000,屬於相等情況,也就是說Entry Point: 50008000,這個地址需要修改,替換成50008040。
找到Load Address和Entry Point這兩個地址的定義,存在於scripts/makefile.lib中,
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/scripts$ gedit Makefile.lib
開啟之後可以找到如下:
318 UIMAGE_LOADADDR ?= arch_must_set_this
319 UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR)
這裡就是說Entry Point等於Load Address,那麼應該修改成為Entry Point=Load Address+0x40,在GNU make中,有sed –e替換操作,如sed -e "s/..$$/40/",就是把輸出的字串的最後兩個字元刪掉,並且用40來補充,也就是說把字串最後兩個字元用40來替換。
那麼作如下修改:
318 UIMAGE_LOADADDR ?= arch_must_set_this
319 #UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR)
320 UIMAGE_ENTRYADDR ?=$(shell echo $(UIMAGE_LOADADDR) |
sed -e "s/..$$/40/")
修改完成之後,回到linux-3.8.3根目錄下進行編譯,如下操作:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ make uImage
如果有如下報錯:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ make uImage
scripts/kconfig/conf --silentoldconfig Kconfig
*** Error during update of the configuration.
make[2]: *** [silentoldconfig] 錯誤 1
make[1]: *** [silentoldconfig] 錯誤 2
make: *** 沒有規則可以建立“include/config/kernel.release”需要的目標“include/config/auto.conf”。停止。
那就是許可權的問題,要麼修改檔案許可權,要麼在root下編譯。這樣即可:
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ sudo make uImage
編譯成功之後輸出如下資訊:
······
Image Name: Linux-3.8.3
Created: Sat Mar 16 10:38:47 2013
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1664080 Bytes = 1625.08 kB = 1.59 MB
Load Address: 50008000
Entry Point: 50008040
Image arch/arm/boot/uImage is ready
從串列埠輸出可知,Entry Point=Load Address+0x40,依舊按照SD燒寫方式進行測試,如果bootdelay延時過長,可以修改bootdelay時間,如下操作:
Hit any key to stop autoboot: 0
zzq6410 >>> set bootdelay 3
zzq6410 >>> sav
Saving Environment to NAND...
Erasing Nand...
Erasing at 0x80000 -- 100% complete.
Writing to Nand... done
zzq6410 >>>
重啟OK6410開發平臺,測試結果如下:
NAND read: device 0 offset 0x100000, size 0x500000
5242880 bytes read: OK
## Booting kernel from Legacy Image at 50008000 ...
Image Name: Linux-3.8.3
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1664080 Bytes = 1.6 MiB
Load Address: 50008000
Entry Point: 50008040
Verifying Checksum ... OK
XIP Kernel Image ... OK
OK
Starting kernel ...
Starting kernel ...
Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0x0
Linux version 3.8.3 (zhuzhaoqi@zhuzhaoqi-desktop) (gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67) ) #1 Fri Mar 15 12:56:52 CST 2013
CPU: ARMv6-compatible processor [410fb766] revision 6 (ARMv7), cr=00c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
Machine: OK6410
Memory policy: ECC disabled, Data cache writeback
CPU S3C6410 (id 0x36410101)
S3C24XX Clocks, Copyright 2004 Simtec Electronics
S3C64XX: PLL settings, A=533000000, M=533000000, E=24000000
S3C64XX: HCLK2=266500000, HCLK=133250000, PCLK=66625000
mout_apll: source is fout_apll (1), rate is 533000000
mout_epll: source is epll (1), rate is 24000000
mout_mpll: source is mpll (1), rate is 533000000
usb-bus-host: source is clk_48m (0), rate is 48000000
irda-bus: source is mout_epll (0), rate is 24000000
CPU: found DTCM0 8k @ 00000000, not enabled
CPU: moved DTCM0 8k to fffe8000, enabled
CPU: found DTCM1 8k @ 00000000, not enabled
CPU: moved DTCM1 8k to fffea000, enabled
CPU: found ITCM0 8k @ 00000000, not enabled
CPU: moved ITCM0 8k to fffe0000, enabled
CPU: found ITCM1 8k @ 00000000, not enabled
CPU: moved ITCM1 8k to fffe2000, enabled
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 65024
Kernel command line: root=/dev/mtdblock2 rootfstype=cramfs console=ttySAC0,115200
PID hash table entries: 1024 (order: 0, 4096 bytes)
Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
__ex_table already sorted, skipping sort
Memory: 256MB = 256MB total
Memory: 256532k/256532k available, 5612k reserved, 0K highmem
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
DTCM : 0xfffe8000 - 0xfffec000 ( 16 kB)
ITCM : 0xfffe0000 - 0xfffe4000 ( 16 kB)
fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
vmalloc : 0xd0800000 - 0xff000000 ( 744 MB)
lowmem : 0xc0000000 - 0xd0000000 ( 256 MB)
modules : 0xbf000000 - 0xc0000000 ( 16 MB)
.text : 0xc0008000 - 0xc02bed88 (2780 kB)
.init : 0xc02bf000 - 0xc02da7a4 ( 110 kB)
.data : 0xc02dc000 - 0xc03076a0 ( 174 kB)
.bss : 0xc0308000 - 0xc0338ef8 ( 196 kB)
SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS:246
VIC @f6000000: id 0x00041192, vendor 0x41
VIC @f6010000: id 0x00041192, vendor 0x41
sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps every 4294967286ms
Console: colour dummy device 80x30
Calibrating delay loop... 353.89 BogoMIPS (lpj=1769472)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
Setting up static identity map for 0x502149a8 - 0x50214a04
DMA: preallocated 256 KiB pool for atomic coherent allocations
OK6410: Option string ok6410=0
OK6410: selected LCD display is 480x272
s3c64xx_dma_init: Registering DMA channels
PL080: IRQ 73, at d0846000, channels 0..8
PL080: IRQ 74, at d0848000, channels 8..16
S3C6410: Initialising architecture
bio: create slab <bio-0> at 0
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
ROMFS MTD (C) 2007 Red Hat, Inc.
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
s3c-fb s3c-fb: window 0: fb
Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
s3c6400-uart.0: ttySAC0 at MMIO 0x7f005000 (irq = 69) is a S3C6400/10
console [ttySAC0] enabled
s3c6400-uart.1: ttySAC1 at MMIO 0x7f005400 (irq = 70) is a S3C6400/10
s3c6400-uart.2: ttySAC2 at MMIO 0x7f005800 (irq = 71) is a S3C6400/10
s3c6400-uart.3: ttySAC3 at MMIO 0x7f005c00 (irq = 72) is a S3C6400/10
brd: module loaded
loop: module loaded
s3c24xx-nand s3c6400-nand: Tacls=4, 30ns Twrph0=8 60ns, Twrph1=6 45ns
s3c24xx-nand s3c6400-nand: System booted from NAND
s3c24xx-nand s3c6400-nand: NAND soft ECC
NAND device: Manufacturer ID: 0xec, Chip ID: 0xd5 (Samsung NAND 2GiB 3,3V 8-bit), 2048MiB, page size: 4096, OOB size: 218
No oob scheme defined for oobsize 218
……
Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
中間作者省去了很多資訊,因為這些資訊暫時對我們是沒有太大關係,但是也給出了很多資訊,因為可以很好地和接下來的每一步移植作對比。從串列埠的輸出,可以得知核心是啟動了。也就是說,此時u-boot已經成功將相關引數傳遞給linux3.8.3核心,完成了u-boot到核心的交接。並且核心已經識別了是OK6410開發平臺,控制CPU是s3c6410等資訊。
當然,讀者不僅僅可以通過修改Entry Point使得核心啟動,還可以修改啟動核心的地址使得bootm address和Load Address不相等,也就是修改U-Boot原始碼中include/configs/目錄下的s3c6410.h檔案中:
#ifdef CONFIG_ENABLE_MMU
#define CONFIG_SYS_MAPPED_RAM_BASE 0xc0000000
#define CONFIG_BOOTCOMMAND"nand read 0xc0018000 0x600000x1c0000;\"bootm 0xc0018000"
#else
#define CONFIG_SYS_MAPPED_RAM_BASE CONFIG_SYS_SDRAM_BASE
#define CONFIG_BOOTCOMMAND"nand read 0x50018000 0x100000 0x500000;"\"bootm 0x50018000"
#endif
第三章第四節 核心啟動分析
對於ARM處理器,核心啟動大體上可以分為兩個階段:與處理器相關的彙編啟動階段和與處理器無關的C程式碼啟動階段。彙編啟動階段從head.S(arch/arm/kernel/head.S)檔案開始,C程式碼啟動階段從start_kernel函式(init/main.c)開始。當然,經過壓縮的核心映象檔案zImage,在進入彙編啟動階段前還要執行一段自解壓程式碼(arch/arm/boot/compressed/head.S)。
省略一些無關緊要的過程和編譯後不執行的程式碼,該過程的啟動流程如圖3. 7所示。相對早期linux-2.6.38的版本,linux-3.8.3在彙編啟動階段並沒有出現__lookup_machine_type,但這並不意味著核心不再檢查bootloader傳入的machine_arch_type引數(R1),只是將檢查機制推遲到了C程式碼階段。
1) __lookup_processor_type
__lookup_processor_type函式的具體實現如程式清單3. 1。
程式清單3. 1查詢處理器型別函式
__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: mov pc, lr
ENDPROC(__lookup_processor_type)
.align 2
.type __lookup_processor_type_data, %object
__lookup_processor_type_data:
.long .
.long __proc_info_begin
.long __proc_info_end
.size __lookup_processor_type_data, . - __lookup_processor_type_data
__lookup_processor_type函式的主要功能是將核心支援的所有CPU型別與通過程式實際讀取的cpu id進行查表匹配。如果匹配成功,將匹配到的proc_info_list的基地址存到r5,否則,r5為0,程式將會進入一個死迴圈。函式傳入引數r9為程式實際讀取的cpu id,傳出引數r5為匹配到的proc_info_list指標的地址。同時為了使C語言能夠呼叫這個函式,根據APCS(ARM 過程呼叫標準)規則,簡單使用以下程式碼就能包裝成一個C語言版本__lookup_processor_type的API函式,函式的原型為struct proc_info_list *lookup_processor_type(unsigned int)。
ENTRY(lookup_processor_type)
stmfd sp!, {r4 - r6, r9, lr}
mov r9, r0
bl __lookup_processor_type
mov r0, r5
ldmfd sp!, {r4 - r6, r9, pc}
ENDPROC(lookup_processor_type)
ENTRY和ENDPROC巨集的定義如下:
#define ENTRY(name) \
.globl name; \
name:
#define ENDPROC(name)
核心利用一個結構體proc_info_list來記錄處理器相關的資訊,在檔案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;
};
事實上,在arch/arm/mm/proc-*.S這類檔案中,程式才真正給核心所支援的arm處理器的proc_info_list分配了記憶體空間,例如linux/arch/arm/mm/proc-v6.S檔案用匯編語言定義的__v6_proc_info結構體。.section指示符來指定這些結構體編譯到.proc.info段。.proc.info的起始地址為 __proc_info_begin,終止位置為__proc_info_end,把它們作為全域性變數儲存在記憶體中,連結指令碼arch/arm/kernel/vmlinux.lds部分內容參考如下:
.init.proc.info : {
. = ALIGN(4);
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
}
2) __vet_atags
在啟動核心時, bootloader會向核心傳遞一些引數。通常,bootloader 有兩種方法傳遞引數給核心:一種是舊的引數結構方式(parameter_struct)——主要是2.6 之前的核心使用的方式;另外一種是現在的核心在用的引數列表(tagged list) 的方式。這些引數主要包括,系統的根裝置標誌、頁面大小、記憶體的起始地址和大小、當前核心命令引數等。而這些引數是通過struct tag結構體組織,利用指標連結成一個按順序排放的引數列表。bootloader引導核心啟動時,就會把這個列表的首地址放入R2中,傳給核心,核心通過這個地址就分析出傳入的所有引數。
核心要求引數列表必須存放在RAM實體地址的頭16k位置,並且ATAG_CORE型別的引數需要放置在引數的列表的首位。__vet_atags的功能就是初步分析傳入的引數列表,判斷的方法也很簡單。如果這個列表起始引數是ATAG_CORE型別,則表示這是一個有效的引數列表。如果起始引數不是ATAG_CORE,就認為bootloader沒有傳遞引數給核心或傳入的引數不正確。
1) __create_page_tables
圖3. 8實際記憶體分佈圖
linux核心使用頁式記憶體管理,應用程式給出的記憶體地址是虛擬地址,它需要經過若干級頁表一級一級的變換,才變成真正的實體地址。32位CPU的虛擬地址大小從0x0000_0000到0xFFFF_FFFF共4G。以段(1 MB)的方式建立一級頁表,可以將虛擬地址空間分割成4096個段條目(section entry)。條目也稱為“描述符”(Descriptor),每一個段描述符32位,因此一級頁表佔用16K(0x4000)記憶體空間。
s3c6410處理器DRAM的地址空間從0x5000_0000開始,上文提到bootloader傳遞給核心的引數列表存放在RAM實體地址的頭16K位置,頁表放置在核心的前16K,因此核心的偏移地址為32K(0x8000),由此構成了如圖3. 8所示的實際記憶體分佈圖。
圖3. 9初步頁表建立流程
__create_page_tables函式初始化了一個非常簡單頁表,僅對映了使核心能夠正常啟動的程式碼空間,更加細緻的工作將會在後續階段完善。流程如所示,獲取頁表實體地址、清空頁表區和建立啟動引數頁表通過閱讀原始碼很容易理解,不加分析。
__enable_mmu函式使能mmu後,CPU發出的地址是虛擬地址,程式正常執行需要對映得到實體地址,為了保障正常地配置mmu,需要對這段程式碼1:1的絕對對映,對映範圍__turn_mmu_on至__turn_mmu_on_end。正常使能mmu後,不需要這段特定的對映了,在後續C程式碼啟動階段時被paging_init()函式刪除。建立__enable_mmu函式區域的頁表程式碼如程式清單3. 2所示。
程式清單3. 2 __enable_mmu頁表的建立
//r4 =頁表實體地址
//獲取段描述符的預設配置flags
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS]
adr r0, __turn_mmu_on_loc //得到__turn_mmu_on_loc的實體地址
ldmia r0, {r3, r5, r6}
sub r0, r0, r3 //計算得到實體地址與虛擬地址的偏差
add r5, r5, r0 //修正得到__turn_mmu_on的實體地址
add r6, r6, r0 //修正得到__turn_mmu_on_end的實體地址
mov r5, r5, lsr #SECTION_SHIFT //1M對齊
mov r6, r6, lsr #SECTION_SHIFT //1M對齊
1: orr r3, r7, r5, lsl #SECTION_SHIFT //生成段描述符:flags + 段基址
str r3, [r4, r5, lsl #PMD_ORDER] //設定段描述絕對對映,實體地址等於虛擬地址。每個段描述符佔4位元組,PMD_ORDER = 2
cmp r5, r6
addlo r5, r5, #1 //下一段,實際上__turn_mmu_on_end - __turn_mmu_on< 1M
blo 1b
............................
__turn_mmu_on_loc:
.long . //__turn_mmu_on_loc當前位置的虛擬地址
.long __turn_mmu_on //__turn_mmu_on的虛擬地址
.long __turn_mmu_on_end //__turn_mmu_on_end的虛擬地址
建立核心的對映區頁表,分析見程式清單3. 3。
程式清單3. 3核心的對映區頁表的建立
//r4 =頁表實體地址
mov r3, pc //r3 = 當前實體地址
mov r3, r3, lsr #SECTION_SHIFT //實體地址轉化段基址
orr r3, r7, r3, lsl #SECTION_SHIFT //段基址 + flags = 段描述符
//KERNEL_START = 0xC000_8000 SECTION_SHIFT = 20 PMD_ORDER = 2
//由於arm 的立即數只能是8位表示,所有用兩條指令實現了將r3儲存到對應的頁表項中
add r0, r4, #(KERNEL_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)
str r3, [r0, #((KERNEL_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!
ldr r6, =(KERNEL_END - 1)
add r0, r0, #1 << PMD_ORDER
add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER) //核心對映頁表結束的段基址
1: cmp r0, r6
add r3, r3, #1 << SECTION_SHIFT //得到段描述符
strls r3, [r0], #1 << PMD_ORDER //設定段描述符
bls 1b
1) __v6_setup
__v6_setup 函式在 proc-v6.S 檔案中,在頁表建立起來之後,此函式進行一些使能 MMU 之前的初始化操作。
2) __enable_mmu
__v6_setup已經為使能 MMU做好了必要的準備,為了保證MMU啟動後程式順利返回,在進入__enable_mmu函式之前,已經將__mmap_switched的虛擬地址(連結地址)儲存在R13中。
3) __mmap_switched
程式執行到這裡,MMU已經啟動,__mmap_switched函式為核心進入C程式碼階段做了一些準備工作:複製資料段,清楚BSS段,設定堆疊指標,儲存processor ID、machine type(bootloader中傳入的)、atags pointer等。最後,終於跳轉到start_kernel函式,進入C程式碼啟動階段。
from:http://bbs.eeworld.com.cn/thread-419923-1-1.html