arch/arm/kernel/head-armv.S
該文件是內核最先執行的一個文件,包括內核入口ENTRY(stext)到start_kernel間的初始化代碼,
主要作用是檢查CPU ID, Architecture Type,初始化BSS等操作,並跳到start_kernel函數。在執行前,處理器應滿足以下狀態:
r0 - should be 0
r1 - unique architecture number
MMU - off
I-cache - on or off
D-cache – off
1 /* 部分源代碼分析 */ 2 /* 內核入口點 */ 3 ENTRY(stext) 4 /* 程序狀態,禁止FIQ、IRQ,設定SVC模式 */ 5 mov r0, #F_BIT | I_BIT | MODE_SVC@ make sure svc mode 6 /* 置當前程序狀態寄存器 */ 7 msr cpsr_c, r0 @ and all irqs disabled 8 /* 判斷CPU類型,查找運行的CPU ID值與Linux編譯支持的ID值是否支持 */ 9 bl __lookup_processor_type 10 /* 跳到__error */ 11 teq r10, #0 @ invalid processor? 12 moveq r0, #'p' @ yes, error 'p' 13 beq __error 14 /* 判斷體系類型,查看R1寄存器的Architecture Type值是否支持 */ 15 bl __lookup_architecture_type 16 /* 不支持,跳到出錯 */ 17 teq r7, #0 @ invalid architecture? 18 moveq r0, #'a' @ yes, error 'a' 19 beq __error 20 /* 創建核心頁表 */ 21 bl __create_page_tables 22 adr lr, __ret @ return address 23 add pc, r10, #12 @ initialise processor 24 /* 跳轉到start_kernel函數 */ 25 b start_kernel
下面對start_kernel()函數及其相關函數進行分析。
1 /* Getting the big kernel lock. 2 * This cannot happen asynchronously, 3 * so we only need to worry about other 4 * CPU's. 5 */ 6 extern __inline__ void lock_kernel(void) 7 { 8 if (!++current->lock_depth) 9 spin_lock(&kernel_flag); 10 }
kernel_flag 是一個內核大自旋鎖,所有進程都通過這個大鎖來實現向內核態的遷移。
只有獲得這個大自旋鎖的處理器可以進入內核,如中斷處理程序等。在任何一對 lock_kernel/unlock_kernel函數裡至多可以有一個程序占用CPU。
進程的lock_depth成員初始化為-1,在 kerenl/fork.c文件中設置。在它小於0時 (恆為 -1),進程不擁有內核鎖;當大於或等於0時,進程得到內核鎖。
setup_arch()函數做體系相關的初始化工作,函數的定義在arch/arm/kernel/setup.c文件中,主要涉及下列主要函數及代碼。
setup_processor()
該函數主要通過
for (list = &__proc_info_begin; list < &__proc_info_end ; list++) if ((processor_id & list->cpu_mask) == list->cpu_val) break;
這樣一個循環來在.proc.info段中尋找匹配的processor_id,processor_id在head_armv.S文件中設置。
該函數獲得體系結構的信息,返回mach-xxx/arch.c 文件中定義的machine結構體的指針,包含以下內容
MACHINE_START (xxx, “xxx”) MAINTAINER ("xxx" BOOT_MEM (xxx, xxx, xxx) FIXUP (xxx) MAPIO (xxx) INITIRQ (xxx) MACHINE_END
if (meminfo.nr_banks == 0) { meminfo.nr_banks = 1; meminfo.bank[0].start = PHYS_OFFSET; meminfo.bank[0].size = MEM_SIZE; }meminfo結構表明內存情況,是對物理內存結構meminfo的默認初始化。
nr_banks指定內存塊的數量,bank指定每塊內存的范圍,PHYS _OFFSET指定某塊內存塊的開始地址,MEM_SIZE指定某塊內存塊長度。 PHYS _OFFSET和MEM_SIZE都定義在include/asm-armnommu/arch-XXX/memory.h文件中,其中 PHYS _OFFSET是內存的開始地址,MEM_SIZE就是內存的結束地址。
這個結構在接下來內存的初始化代碼中起重要作用。
init_mm.start_code = (unsigned long) &_text; 內核代碼段開始 init_mm.end_code = (unsigned long) &_etext; 內核代碼段結束 init_mm.end_data = (unsigned long) &_edata; 內核數據段開始 init_mm.brk = (unsigned long) &_end; 內核數據段結束
每一個任務都有一個mm_struct結構管理其內存空間,init_mm 是內核的mm_struct。
其中設置成員變量* mmap指向自己, 意味著內核只有一個內存管理結構,設置 pgd=swapper_pg_dir,swapper_pg_dir是內核的頁目錄,ARM體系結構的內核頁目錄大小定義為16k。init_mm定義了整個內核的內存空間,內核線程屬於內核代碼,同樣使用內核空間,其訪問內存空間的權限與內核一樣。
bootmem_init (&meminfo)函數根據meminfo進行內存結構初始化。
bootmem_init(&meminfo)函數中調用reserve_node_zero(bootmap_pfn, bootmap_pages) 函數,這個函數的作用是保留一部分內存使之不能被動態分配。
這些內存塊包括:
reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); /*內核所占用地址空間*/ reserve_bootmem_node(pgdat, bootmap_pfn<<PAGE_SHIFT, bootmap_pages<<PAGE_SHIFT) /*bootmem結構所占用地址空間*/
創建內核頁表,映射所有物理內存和IO空間,對於不同的處理器,該函數差別比較大。
下面簡單描述一下ARM體系結構的存儲系統及MMU相關的概念。
在ARM存儲系統中,使用內存管理單元(MMU)實現虛擬地址到實際物理地址的映射。
利用MMU,可把SDRAM的地址完全映射到0x0起始的一片連續地址空間,而把原來占據這片空間的FLASH或者ROM映射到其他不相沖突的存儲空間位置。
例如,FLASH的地址從0x0000 0000~0x00FFFFFF,而SDRAM的地址范圍是0x3000 0000~0x3lFFFFFF,則可把SDRAM地址映射為0x0000 0000~0xlFFFFFF,而FLASH的地址可以映射到0x9000 0000~0x90FFFFFF(此處地址空間為空閒,未被占用)。映射完成後,如果處理器發生異常,假設依然為IRQ中斷,PC指針指向0xl8處的地址,而這個時候PC實際上是從位於物理地址的0x3000 0018處讀取指令。
通過MMU的映射,則可實現程序完全運行在SDRAM之中。在實際的應用中.可能會把兩片不連續的物理地址空間分配給SDRAM。而在操作系統中,習慣於把SDRAM的空間連續起來,方便內存管理,且應用程序申請大塊的內存時,操作系統內核也可方便地分配。通過MMU可實現不連續的物理地址空間映射為連續的虛擬地址空間。操作系統內核或者一些比較關鍵的代碼,一般是不希望被用戶應用程序訪問。通過MMU可以控制地址空間的訪問權限,從而保護這些代碼不被破壞。
MMU的實現過程,實際上就是一個查表映射的過程。建立頁表是實現MMU功能不可缺少的一步。頁表位於系統的內存中,頁表的每一項對應於一個虛擬地址到物理地址的映射。每一項的長度即是一個字的長度(在ARM中,一個字的長度被定義為4Bytes)。頁表項除完成虛擬地址到物理地址的映射功能之外,還定義了訪問權限和緩沖特性等。
MMU的映射分為兩種,一級頁表的變換和二級頁表變換。兩者的不同之處就是實現的變換地址空間大小不同。
一級頁表變換支持1 M大小的存儲空間的映射,而二級可以支持64 kB,4 kB和1 kB大小地址空間的映射。
動態表(頁表)的大小=表項數*每個表項所需的位數,即為整個內存空間建立索引表時,需要多大空間存放索引表本身。
表項數=虛擬地址空間/每頁大小
每個表項所需的位數=Log(實際頁表數)+適當控制位數
實際頁表數 =物理地址空間/每頁大小
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-10/108034p2.htm