在分析start_kernel函數的時候,其中有構架相關的初始化函數setup_arch。此函數根據構架而異,對于ARM構架的詳細分析如下:
void __init setup_arch(char **cmdline_p)
{
struct machine_desc *mdesc;
點選(此處)折疊或打開
此為裝置描述結構體,對于任何闆子都定義了這樣的一個結構體,我以前的文章有介紹:
unwind_init();
初始化基於ARM EABI的Backtrace Unwind機制(棧回退),此函數主要用于位址轉換(arch/arm/kernel/unwind.c)
setup_processor();
再次檢測處理器類型,并初始化處理器相關的底層變量。核心啟動時的處理器資訊(包括cache)就是通過這個函數列印的,例如:
CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), cr=10c53c7f
CPU: VIPT nonaliasing data cache, VIPT aliasing instruction cache
mdesc = setup_machine_fdt(__atags_pointer);
if (!mdesc)
mdesc = setup_machine_tags(machine_arch_type);
<b>在此處通過bootloader傳遞過來的裝置ID來比對一個 struct machine_desc 結構體</b>
<b>(這個結構體就是在arch/arm/mach-*/mach-*.c中定義的結構體:MACHINE_START~MACHINE_END )</b>
如果沒有比對上就死循環。
如果比對上了就列印機器名 ,并處理bootloader傳遞過來的tagged_list,将所有的tag資訊儲存到相應的全局變量或結構體中。
核心啟動時的機器資訊就是這裡列印的,例如:
Machine: ti8168evm
最後傳回結構體指針。
machine_desc = mdesc;
machine_name = mdesc->name;
通過比對的struct machine_desc 結構體資料,初始化一些全局變量
if (mdesc->soft_reboot)
reboot_setup("s");
通過struct machine_desc 中的soft_reboot資料來設定重新開機類型:
如果存在就為“s”:softreset;如果不存在就為“h”:hardreset
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;
這裡通過連接配接腳本中得到的Linux代碼位置資料來初始化一個mm_struct結構體init_mm中的部分資料
ps:每一個任務都有一個mm_struct結構以管理記憶體空間,init_mm是核心自身的mm_struct
/* 同時填充cmd_line以備後用, 保護boot_command_line資料 */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
将boot_command_line複制到cmd_line中。<b>這裡關鍵是要知道系統啟動的時候的cmdline是如何傳遞的。</b>
parse_early_param();
處理在 struct obs_kernel_param 中定義為early的啟動參數(主要是記憶體配置部分的參數)
其中就分析了mem=size@start參數初始化了struct meminfo meminfo;
同時如果有vmalloc=size參數也會初始化 vmalloc_min
這裡需要注意的是核心的cmdline中的參數按照其被需要的先後,分為early和非early的。
include/linux/init.h:
struct obs_kernel_param {
const char *str; //在cmdline中相應參數名。
int (*setup_func)(char *); //對于此參數的專用處理函數
int early; <b> //是否為早期需要處理的參數</b>
};
兩種不同的參數在核心中用了不同的宏來定義:
early: #define early_param(str, fn) \
__setup_param(str, fn, fn, <b>1</b>)
非early: #define __setup(str, fn) \
__setup_param(str, fn, fn, <b>0</b>)
使用這兩個宏定義的參數和構架相關,一些構架或者闆子可以定義自己特定的參數和處理函數。對于比較重要的“men”參數就是early參數。
sanity_check_meminfo();
在此處設定struct meminfo meminfo中每個bank中的highmem變量,
通過vmalloc_min确定每個bank中的記憶體是否屬于高端記憶體
arm_memblock_init(&meminfo, mdesc);
在此處按位址資料從小到大排序meminfo中的資料,并初始化全局的memblock資料。
<b> paging_init(mdesc);</b>
設定核心的參考頁表。
此頁表不僅用于實體記憶體映射,還用于管理vmalloc區。
此函數中非常重要的一點就是初始化了bootmem配置設定器!
request_standard_resources(mdesc);
通過擷取裝置描述結構體(struct machine_desc)中的資料和編譯時産生的位址資料,<b>初始化記憶體相關的全局結構體變量</b>。
unflatten_device_tree();
通過啟動參數中的“非平坦裝置樹”資訊(如果有),擷取記憶體相關資訊
#ifdef CONFIG_SMP
if (is_smp())
smp_init_cpus();
#endif
針對SMP處理器,初始化可能存在的CPU映射 - 這描述了可能存在的CPU
reserve_crashkernel();
用于核心崩潰時的保留核心
此功能通過核心command line參數中的"crashkernel="保留下記憶體用于主核心崩潰時擷取核心資訊的導出。
cpu_init();
初始化一個CPU,并設定一個per-CPU棧
tcm_init();
初始化ARM内部的TCM(緊耦合記憶體)。
ARM官網也有介紹文檔
#ifdef CONFIG_MULTI_IRQ_HANDLER
handle_arch_irq = mdesc->handle_irq;
調用裝置描述結構體中的mdesc->handle_irq函數,目的未知。
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
early_trap_init();
對中斷向量表進行早期初始化
<b> if (mdesc->init_early)</b>
<b> mdesc->init_early();</b>
如果裝置描述結構體定義了init_early函數(應該是早期初始化之意),則在這裡調用。
}
這個函數主要是檢查處理器的類型是否比對,并擷取處理器資訊來設定處理器的相關底層參數。
static void __init setup_processor(void)
struct proc_info_list *list;
/*
* 在支援處理器清單中定位處理器
* 連接配接器為我們建立這個清單,從 * <b>arch/arm/mm/proc-*.S</b>中的入口
*/
list = lookup_processor_type(read_cpuid_id());
if (!list) {
printk("CPU configuration botched (ID %08x), unable "
"to continue.\n", read_cpuid_id());
while (1);
}
這裡再次核對處理器類型,雖然這個已經在彙編代碼中執行過一遍了
cpu_name = list->cpu_name;
#ifdef MULTI_CPU
processor = *list->proc;
#ifdef MULTI_TLB
cpu_tlb = *list->tlb;
#ifdef MULTI_USER
cpu_user = *list->user;
#ifdef MULTI_CACHE
cpu_cache = *list->cache;
通過從struct proc_info_list擷取的資料初始化CPU相關的全局變量
printk("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n",
cpu_name, read_cpuid_id(), read_cpuid_id() & 15,
proc_arch[cpu_architecture()], cr_alignment);
列印核心啟動時的處理器資訊
sprintf(init_utsname()->machine, "%s%c", list->arch_name, ENDIANNESS);
sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS);
elf_hwcap = list->elf_hwcap;
#ifndef CONFIG_ARM_THUMB
elf_hwcap &= ~HWCAP_THUMB;
feat_v6_fixup();
針對特定的ARM核軟體屏蔽一些功能
cacheid_init();
初始化ARM核中的緩存
cpu_proc_init();
宏:
#define cpu_proc_init __glue(CPU_NAME,_proc_init)
意在調用處理器特定的初始化函數。