學習筆記 --- LINUX核心啟動第二階段分析(不考慮自解壓過程)
上篇文章中分析了Linux核心從head.s啟動:
.section ".text.head", "ax" ENTRY(stext) setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ 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
1 開始設定ARM模式為管理模式,禁止中斷,然後獲取處理器ID
2 呼叫__lookup_processor_type 判斷核心是否支援該處理器,這裡略,不講!
3 處理器ID驗證完後呼叫 __lookup_machine_type 判斷核心是否支援該機器型別:
第二行4b的位置是:__lookup_machine_type: adr r3, 4b ldmia r3, {r4, r5, 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: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type teq r3, r1 @ matches loader number? beq 2f @ found add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc cmp r5, r6 blo 1b mov r5, #0 @ unknown machine 2: mov pc, lr ENDPROC(__lookup_machine_type)
4: .long .
.long __arch_info_begin
.long __arch_info_end
所以2,3行執行之後:
r4 = . r5=__arch_info_begin r6=__arch_info_end
4,5,6行都有解釋,目的就是為了計算出__arch_info_begin 和__arch_info_end在SDRAM裡面實際存放地址,這兩個東西是連結檔案裡面定義的arch.info.init段的起始和結束標誌:
而arch.info.init段存放的是一個結構體,從程式碼可以看出:__arch_info_begin = .; *(.arch.info.init) __arch_info_end = .;
/*
* Set of macros to define architecture features. This is built into
* a table by the linker.
*/
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
該結構體格式為:
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head.S, head-common.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
定義的實體:
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <[email protected]> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100, //就是0x30000100
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
還有很多很多不同的單板都定義了這個,可以知道,這個arch.info.init段裡面放的就是linux核心支援的所有單板的引數資訊,再回到彙編,7-10行就是判斷每個單板的機器型別(讀到暫存器r3)是否和uboot傳入的單板引數(存在暫存器r1)匹配,如果匹配則核心支援該單板。
4 接著執行__vet_atags :
__vet_atags:
tst r2, #0x3 @ aligned?
bne 1f
ldr r5, [r2, #0] @ is first tag ATAG_CORE?
cmp r5, #ATAG_CORE_SIZE
cmpne r5, #ATAG_CORE_SIZE_EMPTY
bne 1f
ldr r5, [r2, #4]
ldr r6, =ATAG_CORE
cmp r5, r6
bne 1f
mov pc, lr @ atag pointer is ok
1: mov r2, #0
mov pc, lr
ENDPROC(__vet_atags)
這個函式就是去驗證uboot傳入的TAG引數區域的ATAG_CORE_SIZE與ATAG_CORE是否和核心的匹配,這段TAG引數起始地址存在暫存器r2,讀出來再比較這兩項是否一致,如果一致則通過。
5 接下來建立頁表
6 使能MMU
最後跳轉到 start_kernel 進入C語言程式碼段,開始列印版本資訊,然後執行下面這段程式碼,主要作用就是解析uboot傳入的引數:
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line;
unwind_init();
setup_processor();
mdesc = setup_machine(machine_arch_type); //設定機器型別,返回machine_desc結構體,這個結構體通過MACHINE_START設定好了
machine_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup("s");
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
else if (mdesc->boot_params) //就是結構體裡面的S3C2410_SDRAM_PA + 0x100 = 0x30000100,這個地址存放uboot設定好的TAG引數
tags = phys_to_virt(mdesc->boot_params); //轉為虛擬地址,返回tags,tags指向uboot存放的TAG引數起始地址
/*
* If we have the old style parameters, convert them to
* a tag list.
*/
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
if (tags->hdr.tag != ATAG_CORE) //如果TAG引數不匹配,則使用核心預設的引數
tags = (struct tag *)&init_tags;
if (mdesc->fixup)
mdesc->fixup(mdesc, tags, &from, &meminfo);
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags);
}
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;
memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(cmdline_p, from); //解析命令列,就是解析uboot傳入的bootargs環境變數,它為TAG引數的一部分
paging_init(mdesc);
request_standard_resources(&meminfo, mdesc);
#ifdef CONFIG_SMP
smp_init_cpus();
#endif
cpu_init();
tcm_init();
/*
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq; //把結構體裡的引數都放在獨自的變數裡單獨表示
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
early_trap_init();
}
上面執行完後,返回bootargs引數到cmdline_p,然後再執行下面函式,將命令列地址存放在全域性指標saved_command_line裡面:
static void __init setup_command_line(char *command_line)
{
saved_command_line = alloc_bootmem(strlen (boot_command_line)+1);
static_command_line = alloc_bootmem(strlen (command_line)+1);
strcpy (saved_command_line, boot_command_line);
strcpy (static_command_line, command_line);
}
然後下面會用unknown_bootoption函式來解析這個命令列,比如檔案系統掛載到哪個地方,使用串列埠幾作為監控端等;
接下來是一大堆初始化,執行到最後rest_init:
kernel_init ---- prepare_namespace ---- mount_root (掛接根檔案系統)
掛接好之後 init_post:
static noinline int init_post(void)
__releases(kernel_lock)
{
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
free_initmem();
unlock_kernel();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) //開啟終端檔案 ----用於標準printf列印
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0); //複製終端檔案 用於 標準scanf輸入
(void) sys_dup(0); //複製終端檔案 用於 標準error列印
current->signal->flags |= SIGNAL_UNKILLABLE;
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) { //
run_init_process(execute_command); //執行uboot傳入的“init=xxx”指定的應用指令碼
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init"); //如果上面uboot沒有指定,那麼往下執行預設路徑的指令碼
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
}
上面掛接檔案系統mount_root ,掛接到哪裡是由環境變數bootargs裡面的“root=” 類引數決定的,是通過unknown_bootoption函式解析出來的。
假設uboot傳進來引數為 “root=/dev/mtdblock3” 則表示掛載到mtd裝置的第4個分割槽,至於這個分割槽對應哪裡是在nandflash的驅動裡面指定的:
static struct mtd_partition friendly_arm_default_nand_part[] = {
[0] = {
.name = "uboot",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "param",
.offset = 0x00040000,
.size = 0x00020000,
},
[2] = {
.name = "Kernel",
.offset = 0x00060000,
.size = 0x00500000,
},
[3] = {
.name = "root",
.offset = 0x00560000,
.size = 1024 * 1024 * 1024,
},
[4] = {
.name = "nand",
.offset = 0x00000000,
.size = 1024 * 1024 * 1024, //
}
};
上面可以知道第4個分割槽就是root根檔案系統分割槽,這個在一直nand驅動的時候要和uboot傳進的引數對應起來。
相關推薦
學習筆記 --- LINUX核心啟動第二階段分析(不考慮自解壓過程)
上篇文章中分析了Linux核心從head.s啟動: .section ".text.head", "ax" ENTRY(stext) setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
Linux核心啟動第二階段之setup_arch函式分析
轉自:http://blog.chinaunix.net/uid-20672257-id-2383451.html 執行setup_arch()函式 回到start_kernel當中,569行,呼叫setup_arch函式,傳給他的引數是
學習筆記 --- LINUX核心裡面的likely與unlikely
看核心時總遇到if(likely( )){}或是if(unlikely( ))這樣的語句,最初不解其意,現在有所瞭解,所以也想介紹一下。 likely() 與 unlikely()是核心(我看的是2.6.22.6版本,2.6的版本應該都有)中定義的兩個巨集。位於/in
嵌入式 arm平臺kernel啟動第二階段分析
其中:PHYS_OFFSET在arch/arm/mach-s3c2410/include/mach/memory.h定義,為UL(0x30000000),而TEXT_OFFSET在arch/arm/Makefile中定義,為核心映象在記憶體中到記憶體開始位置的偏移(位元組),為$(textofs-y) tex
網易公開課《Linux核心分析》學習心得-Linux核心如何裝載和啟動一個可執行程式
實驗 設定斷點sys_execeve,並繼續 程式碼執行到了SyS_execve。在QEMU中執行exec,可以看到只能出現兩句,沒有完全執行完畢。 設定斷點load_elf_binary和start_thread,並執行,可以看到程式碼停在了
儲存管理(二)--學習《Linux核心原始碼情景分析》第二章(方便理解,內容在註釋中)
2.7 物理頁面的分配 分配若干頁面時,分配頁面用於DMA(direct memory assess)當然應該是連續的,其實出於物理儲存空間質地一致性考慮,記憶
儲存管理(一)--學習《Linux核心原始碼情景分析》第二章(方便理解,內容在註釋中)
2.1 Linux記憶體管理基本框架 32位cpu上的頁式記憶體管理是採用兩層對映方式,但在64位cpu上採用兩層對映方式就不太合理了,所以在Linux中頁式管理採用的是三層對映方式:頁面目錄(PGD)、中間目錄(P
七.linux開發之uboot移植(七)——uboot原始碼分析2-啟動第二階段之start_armboot函式分析1
一.uboot啟動第二階段之start_armboot函式簡介 1.start_armboot函式簡介 (1)這個函式在uboot/lib_arm/board.c的第444行開始到908行結束。 (2)、即一個函式組成uboot第二階段 2、
Linux學習筆記之內核啟動流程與模塊機制
oid img 相關 call rootfs _exit alt 執行 分模塊 本文旨在簡單的介紹一下Linux的啟動流程與模塊機制: Linux啟動的C入口位於/Linux.2.6.22.6/init/main.c::start_kernel() 下圖簡要的描述了一下內核
Linux學習筆記第三周第二次課(2月6日)
gid ins 目錄 隨機 useradd 生成 echo tab 設置 3.4 usermod命令更改用戶屬性usermod,更改UID,命令為#usermod -u 編號;更改GID,命令為#usermod -g 編號;更改用戶家目錄,命令為#usermod -d 編號
Linux學習筆記第五周第二次課(3月6日)
yum wget curl -O make ./configure --prefi 7.6 yum更換國內源wget 下載網址,下載文件;安裝wget命令,#yum install -y wget;curl -O 下載網址,下載文件7.7 yum下載rpm包安裝擴展源epel安裝命令#yu
《2.uboot和系統移植-第6部分-2.6.uboot原始碼分析2-啟動第二階段》
《2.uboot和系統移植-第6部分-2.6.uboot原始碼分析2-啟動第二階段》 第一部分、章節目錄 2.6.1.start_armboot函式簡介 2.6.2.start_armboot解析1 2.6.3.記憶體使用排布 2.6.4.start_armboot解析2 2.6.5.s
Linux核心啟動過程分析(十)-----RTC驅動分析
參考https://blog.csdn.net/xuao20060793/article/details/46433263這篇博文 RTC驅動分析: Class.c (drivers\rtc):subsys_initcall(rtc_init); static int __init
Linux核心啟動流程分析(一)
1. 依據arch/arm/kernel/vmlinux.lds 生成linux核心原始碼根目錄下的vmlinux,這個vmlinux屬於未壓縮,帶除錯資訊、符號表的最初的核心,大小約23MB; 命令:arm-linux-gnu-ld -o vmlinux -T a
omapl138移植uboot系列之修改移植TI官方移植的Linux核心(啟動核心第二篇)
修改Linux核心原始碼 實際上,剛剛我們已經成功的啟動了TI移植過的Linux核心,但是從串列埠控制檯的現象來看,“Starting Kernel”之後什麼資訊都沒有輸出,這就需要我們在TI移植過的核心原始碼之上進行相應修改,以適合我們的639A板卡。
國嵌視訊學習---linux核心啟動流程
一、核心檔案uImage的構成 uImage:Uboot header和zImage zImage:解壓程式碼和壓縮後的vmlinux映象 二、zImage核心的構成 其中解壓程式碼由Head.s和misc.s組成。 三、vmlinux核心構成 1.啟動程式碼部分:
段式、頁式記憶體管理--學習《Linux核心原始碼情景分析》第一章
不得不說《Linux核心原始碼情景分析》這本書被那麼多人當作經典是有原因的,這裡只是該書的筆記遠不及毛老師描述的清楚。 對第一章做一個總結。這一章主要講解段式和頁式記憶體管理,當然還有一些其他東西。 Linux核心版本號的格式
Hadoop源碼學習筆記之NameNode啟動流程分析二:http server啟動源碼剖析
選擇 stop down gen java類 屬性 功能 集群 aslist NameNodeHttpServer啟動源碼剖析,這一部分主要按以下步驟進行: 一、源碼調用分析 二、偽代碼調用流程梳理 三、http server服務流程圖解 第一步,源碼調用分析
學習筆記 --- LINUX網絡卡驅動框架分析
網絡卡的驅動很簡單,就是填充net_device結構體,其應用層到網路協議層核心已經完成了,我們的工作就是填寫這個net_device,然後註冊就可以了。 修正一下:上面第三步應該是:register_netdev 下面程式碼實現一個虛擬網絡卡,這裡沒有實際的網絡卡,只是
Linux核心啟動過程分析
1、Linux核心啟動協議 閱讀文件\linux-2.6.35\Documentation\x86\boot.txt 傳統支援Image和zImage核心的啟動裝載記憶體佈局(2.4以前的核心裝載就是這樣的佈局): |