- 具體問題
- 參考解決方案
- 解決思路
- 深入解決
1.具體問題:
在移植3.6.6的核心後,下載下傳啟動卡死,具體是序列槽列印資訊停留在“Uncompressing Linux… done, booting the kernel.”
确認了移植是正常的,肯定沒有錯誤。
2. 參考解決方案:
依據網上的說法要確定如下情況:
- 2.1 核心的時鐘頻率正确
- 2.2 boot和kerel 配置一緻的MACH_TYPE,即闆子MACHINE ID
-
2.3 序列槽驅動配置正常
在核心配置device drivers->character devices->serial 中
<*> Samsung SoC serial support
[*] Support for console on Samsung SoC serial port
-
2.4. 啟動參數設定正常
console 的ttySAC0 ,波特率115200對應正确
3.解決思路
檢查了前面三項都是OK的,剛開始以為是核心無法啟動,後來将顯示驅動移植成功後(其它沒變)下載下傳到開發闆,發現啟動資訊同樣也無法通過序列槽列印到minicom或其它dnw視窗中,但在開發闆的LCD上卻列印出了核心的啟動資訊。由此我知道了核心移植是成功的,隻是沒有通過序列槽列印出來,那就是序列槽設定不對了,可檢查發現bootloader的console這些設定都是對的,突然想起來核心裡也可以設定啟動參數,試了下果然成功了(在Boot options —> Default kernel command string 中,填上console=ttySAC0,115200等啟動參數就行了),但按理說也可以通過bootloader傳遞呀?為何這裡卻不行?
4.深入解決
由于以前移植2.6.35版本的核心時,在改變啟動參數時都是從bootloader裡設定的而忽略了核心也可以設定啟動參數,當時弄2.6.35以前的版本都不需要設定這裡,而且我在更改NFS啟動時核心設定的參數也不起作用,就以為此項沒用,是以就沒考慮到這,實際原因是2.6.35核心配置裡要選中always use default…這個才會使用核心配置的啟動參數,不然的話由于飛淩的bootloader裡預設設了一些啟動參數,也就是說無論你在bootloader裡設沒設,它實質上都設定了,是以核心就用外來設定的啟動參數覆寫了kernel裡預設的啟動參數。但3.6.6的版本在這方面可能是有改進,在核心boot option配置中可以看到如下Kernel command line type:
(X) Use bootloader kernel arguments if available
( ) Extend bootloader kernel arguments
( ) Always use the default kernel command string
而在2.6.35的版本中隻有一個
Always use the default kernel command string
可選項,是以确實有改進
,是以從此處看也有可能是我的bootloader與核心在傳遞啟動參數時有問題(bootloader啟動參數的位址在核心中沒有設定好)導緻無法把bootloader裡設定的參數傳遞給核心,造成核心找不到正确的序列槽進而無法列印核心資訊,是以核心從bootloader中沒有擷取到可用參數就使用核心設定的啟動參數了。經測試,無論我怎麼改變bootloader裡的啟動參數都沒效果,應該是核心中的bootloader傳參位址設定有問題,如果無法從bootloader設定那每次更換都要編譯核心,太麻煩,有沒有解決方法呢?
首先了解下 kernel啟動參數傳遞方式
了解核心啟動參數可以參考
文章1
文章2
或自行百度
據此我明白了,kernel啟動參數有兩種,一種是通過核心設定預設啟動參數中,一種是從某段記憶體中讀取進來取代核心啟動參數(而這個設定是由bootloader來設定并儲存在某段事先與核心協商好的記憶體位址中),在啟動時如果在“Kernel command line type”選中的是第一項,就先去尋找這段記憶體看有沒有參數,沒有就直接使用核心預設啟動參數,有就覆寫掉核心設定的。(在查錯Uncompressing linux…done,boot這種卡死的問題時,如果确認其它都OK還是無法啟動,那就直接選中永遠使用核心參數起動,這樣可以檢查是否為Bootloader的問題)
那麼核心是如何知道bootloader将啟動參數儲存在哪裡的呢?
看了下2.6.35版裡的
/arch/arm/mach-s3c24xx/mach-smdk2440.c
在最後的MACHINE_START中有個
boot_params
,好了猜測就是它了,但在3.6.6中加入會提示錯誤,那應該是改了名之類的,先尋找下MACHINE_START的定義在檔案
arch/arm/include/asm/mach/arch.h中
struct machine_desc {
+--- lines: Note! The first four elements are used--------------------------------------------------------
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 :; /* never has lp0 */
unsigned int reserve_lp1 :; /* never has lp1 */
unsigned int reserve_lp2 :; /* never has lp2 */
unsigned int soft_reboot :; /* 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);
};
可以看到
boot_params;
那我們到3.6.6版中去看看,果然更換了如下
struct machine_desc {
unsigned int nr; /* architecture number */
const char *name; /* architecture name */
unsigned long atag_offset; /* tagged list (relative) */
const char *const *dt_compat; /* array of device tree
* 'compatible' strings */
unsigned int nr_irqs; /* number of IRQs */
#ifdef CONFIG_ZONE_DMA
unsigned long dma_zone_size; /* size of DMA-able area */
#endif
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned char reserve_lp0 :; /* never has lp0 */
unsigned char reserve_lp1 :; /* never has lp1 */
unsigned char reserve_lp2 :; /* never has lp2 */
char restart_mode; /* default restart mode */
void (*fixup)(struct tag *, char **,
struct meminfo *);
void (*reserve)(void);/* reserve mem blocks */
void (*map_io)(void);/* IO mapping function */
void (*init_early)(void);
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
void (*init_late)(void);
#ifdef CONFIG_MULTI_IRQ_HANDLER
void (*handle_irq)(struct pt_regs *);
#endif
void (*restart)(char, const char *);
};
,好了,試着修改此參數為S3C2410_SDRAM_PA + 0x100(原值為0x100)編譯下載下傳後居然無法啟動了,卡死在Uncompressing Linux… done, booting the kernel.這一步,修改回原來的值,尋找了下發現在arch/arm/kernel/setup.c的setup_machine_tags函數中有所改變
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
else if (mdesc->atag_offset)
tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);//實際是進入這一步,
跟2.6.35的不同,
if (__atags_pointer)
{
printk("using __atags_pointer to get tags.\n");
tags = phys_to_virt(__atags_pointer);
}
else if (mdesc->boot_params)
{
tags = phys_to_virt(mdesc->boot_params);//不同點
printk("using mdesc->boot_params to get tags.the tags is 0x%08x\n",tags);//anzyelay
printk("tags->hdr.tag = 0x%08x\n",tags->hdr.tag);
}
是以我直接列印輸出下發現mdesc->boot_params結果居然不是0x30000100(S3C2410_SDRAM_PA + 0x100)而是
mdesc->boot_params=0xc0000100(為什麼??不知道呀不知道 ,我看了下bootloader源碼裡也是0x30000100這個位址呀,懶得去跟蹤了,反正舊版傳過來是這個值。)
而3.6.6版本的從arch/arm/mach-s3c24xx/mach-smdk2440.c中傳過來的0x0100加上PAGE_OFFSET(0xc000000正好是0xc0000100,是以你一改MACHINE_START裡的.
__atag_offset
還會出錯。是以結論是位址是正确設定了的。
現在既然不是核心與bootloader傳參位址錯誤這個原因那怎麼辦?我對比兩個版本分析調試了下setup_machine_tags函數,發現原來是
CONFIG_DEPRECATED_PARAM_STRUCT
這個宏沒有定義的問題,導緻後面核心讀取到的參數位址tags是錯誤的(不在是0xc0000100)。
首先核心設定參數的路徑是在main.c函數中, start_kernel–>setup_arch(&command_line)–>setup_command_line(command_line);
重點就在setup_arch函數中,此函數位于arch/arm/kernel/setup.c ,如下
void __init setup_arch(char **cmdline_p)
{
struct machine_desc *mdesc;
setup_processor();
mdesc = setup_machine_fdt(__atags_pointer);//因為__atags_pointer為空,直接跳出來了,
//這個參數是在R2寄存器給過來的吧,可以看head-common.s中的__mmap_switched
if (!mdesc)
mdesc = setup_machine_tags(machine_arch_type);//然後進入此處,
//可以對比下2.6.35,3.6.6這裡優化了下。這次出錯的原因也主要是在這個函數裡
machine_desc = mdesc;
machine_name = mdesc->name;
setup_dma_zone(mdesc);
if (mdesc->restart_mode)
reboot_setup(&mdesc->restart_mode);
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;
/* populate cmd_line too for later use, preserving boot_command_line */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;//擷取到啟動參數
parse_early_param();
sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[]), meminfo_cmp, NULL);
sanity_check_meminfo();
arm_memblock_init(&meminfo, mdesc);
paging_init(mdesc);
request_standard_resources(mdesc);
if (mdesc->restart)
arm_pm_restart = mdesc->restart;
unflatten_device_tree();
#ifdef CONFIG_SMP
if (is_smp())
smp_init_cpus();
#endif
reserve_crashkernel();
tcm_init();
#ifdef CONFIG_MULTI_IRQ_HANDLER
handle_arch_irq = mdesc->handle_irq;
#endif
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
if (mdesc->init_early)
mdesc->init_early();
}
我們來看setup_machine_tags這個函數,在同一檔案中
static struct machine_desc * __init setup_machine_tags(unsigned int nr)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc = NULL, *p;
char *from = default_command_line;//編譯核心時配置的Boot Options
init_tags.mem.start = PHYS_OFFSET;
/*
* locate machine in the list of supported machines.
* 這裡是根據你在MACHINE_START(arg1,arg2)給的arg1=nr才找出
* 對應的裝置描述結構p,是以,如果你沒有設定正确arg1時,或者你的arg1
* 所對應的Machine ID(在檔案arch/arm/tools/mach-types中)不對
* 時,那也會出現卡在Uncompres...這個錯誤。你開啟kernel hacking中的
* early print時就會看到錯誤原因。(這就是所謂的機器碼導緻的卡死原因。)
*/
for_each_machine_desc(p)
if (nr == p->nr) {
printk("Machine: %s\n", p->name);
mdesc = p;
break;
}
if (!mdesc) {
early_print("\nError: unrecognized/unsupported machine ID"
" (r1 = 0x%08x).\n\n", nr);
dump_machine_table(); /* does not return */
}
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
/*前面說過這個參數為空,直接路過了,跟參考文章2說的什麼
* 設定了bootloader參數就直接擷取過來有些不對,可能與我
* 用的飛淩的bootloader,如果用UBOOT估計從這裡進吧,我沒
* 有弄uboot,望知道的人告知下*/
else if (mdesc->atag_offset)
tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);//我們是進入到這裡,
//前面說了在配置檔案中的atag_offset就是0x0100,不要改。tags=0xc0000100
/*2.6.35版的是沒有下面這個宏的,由此直接跳過了convert_to_tag_list這一步
*導緻後面的tags值改變,目前我的bootloader傳來的參數就是old style,是以這一步
*不能省。這個在kernel配置裡的kernel feature裡選中Provide old way to
* pass kernel parameters 這項就行。*/
#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
/*
* If we have the old style parameters, convert them to
* a tag list.
*/
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
#endif
if (tags->hdr.tag != ATAG_CORE) {
#if defined(CONFIG_OF)
/*
* If CONFIG_OF is set, then assume this is a reasonably
* modern system that should pass boot parameters
*/
early_print("Warning: Neither atags nor dtb found\n");
#endif
tags = (struct tag *)&init_tags;
}
//到這裡tags=0xc0000100依舊,如果變了那肯定不對了(我就是在跟蹤到此處不對才發現問題在上面這一步的)。
//同時tags->hdr.tag == ATAG_CORE(0x54410001)了,
if (mdesc->fixup)
mdesc->fixup(tags, &from, &meminfo);
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != )
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags);
}
/* parse_early_param needs a boot_command_line */
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
return mdesc;
}
好了,依據上面的說法,make menuconfig 勾上kernel feature裡的Provide old way to pass kernel parameters ,make zImage後,下載下傳啟動,可以正常通過 bootloader設定啟動參數了。同時我們可以在啟動提示資訊中看到
ATAG_INITRD is deprecated; please update your bootloader.
這句,是以bootloader與核心版本不相容時也會導緻參數傳遞錯錯引起輸出停留在Uncompressing Linux… done, booting the kernel.
另附加問題:我用arm-linux-gcc-4.9.4編譯已經成功移植好的2.6.35核心也出現停留在此的現象,換回其它版本的編譯器又能正常啟動(隻換個編譯器,其它未做任何修改)。開始以為是4.9.4的編譯器有問題,今天用來編譯了下3.6.6的核心居然是可以的,是以應該是2.6.35核心移植還有問題不相容4.9.4的gcc,具體不知原因!!!!