在分析Linux-3.0内核启动的时,当分析到自解压后的汇编部分,发现head.S (arch\arm\kernel)中并没有对
machine_type作任何的检查,只是检查了处理器ID(__lookup_processor_type)。
在2.6.38及以前的代码:
- __HEAD
-
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)?
-
THUMB( it eq ) @ force fixup-able long branch encoding
-
beq __error_p @ yes, error 'p'
-
bl __lookup_machine_type @ r5=machinfo
-
movs r8, r5 @ invalid machine (r5=0)?
-
THUMB( it eq ) @ force fixup-able long branch encoding
-
beq __error_a @ yes, error 'a'
bl __vet_atags
2.6.39-rc1及其之后的代码:
- __HEAD
-
ENTRY(stext)
-
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ CPU模式设置宏
-
@ (进入svc模式并且关闭中断)
-
mrc p15, 0, r9, c0, c0 @ 获取处理器id-->r9
-
bl __lookup_processor_type @ 返回r5=procinfo r9=cpuid
-
movs r10, r5 @ r10=r5,并可以检测r5=0?注意当前r10的值
-
THUMB( it eq ) @ force fixup-able long branch encoding
-
beq __error_p @ yes, error 'p'如果r5=0,则内核处理器不匹配,出错~死循环
- /*
- * 获取RAM的起始物理地址,并保存于 r8 = phys_offset
- * XIP内核与普通在RAM中运行的内核不同
- * (1)CONFIG_XIP_KERNEL
- * 通过运行时计算????
- * (2)正常RAM中运行的内核
- * 通过编译时确定(PLAT_PHYS_OFFSET 一般在arch/arm/mach-xxx/include/mach/memory.h定义)
- *
- */
- #ifndef CONFIG_XIP_KERNEL
- adr r3, 2f
- ldmia r3, {r4, r8}
- sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
- add r8, r8, r4 @ PHYS_OFFSET
- #else
- ldr r8, =PLAT_PHYS_OFFSET
- #endif
- /*
- * r1 = machine no, r2 = atags or dtb,
- * r8 = phys_offset, r9 = cpuid, r10 = procinfo
- */
- bl __vet_atags @ 判断r2(内核启动参数)指针的有效性
这引起了我的注意,难道内核不检查bootloader通过r1传递过来的machine_type参数了吗?
我的第一反应就是在git中找到相应的commit,看看其中有什么关于删除 __lookup_machine_type消失的解释。
首先确定了消失的版本是在2.6.38到2.6.39-rc1之间,让后通过 git whatchanged找到有修改arch/arm/kernel/head.S的commit,从中找到了删除 __lookup_machine_type的commit:
- commit 6fc31d54443bdc25a8166be15e3920a7e39d195d
-
Author: Russell King
-
Date: Wed Jan 12 17:50:42 2011 +0000
-
-
ARM: Defer lookup of machine_type to setup.c
- ARM:推迟machine_type的检查
-
-
Since the debug macros no longer depend on the machine type information,
-
the machine type lookup can be deferred to setup_arch() in setup.c which
-
simplifies the code somewhat.
- 由于调试宏不再依赖机器类型(machine type)信息,
- 机器类型(machine type)的查找可以被推迟到 setup.c 中的 setup_arch(),
- 这样从一定程度上简化了代码。
- (译者注:汇编变成了C代码,必然简化了编程,提高了移植性和通用性)
-
-
We also move the __error_a functionality into setup.c for displaying a
-
message when a bad machine ID is passed to the kernel via the LL debug
-
code. We also log this into the kernel ring buffer which makes it
-
possible to retrieve the message via a debugger.
- 我们同时也将__error_a函数移动到了setup.c中,
- 当一个错误的机器ID被传递给内核,它可以通过LL(底层)调试代码显示信息。
- 我们也将这个信息放进内核环型缓冲,使它可能通过调试器被检索到。
-
-
Original idea from Grant Likely.
- 原始的想法来源于Grant Likely(格兰特 莱克里)
-
-
Acked-by: Grant Likely
-
Tested-by: Tony Lindgren
-
Signed-off-by: Russell King
-
-
:100644 100644 8f57515... c84b57d... M arch/arm/kernel/head-common.S
-
:100644 100644 814ce1a... 6b1e0ad... M arch/arm/kernel/head-nommu.S
-
:100644 100644 c0225da... 8a154b9... M arch/arm/kernel/head.S
-
:100644 100644 420b8d6... 78678b0... M arch/arm/kernel/setup.c
从这里我们可以知道,其实这个__lookup_machine_type消失不是被删除了,而是这个功能被推迟到了C代码中:
init/main.c
- asmlinkage void __init start_kernel(void)
-
{
-
char * command_line;
-
extern const struct kernel_param __start___param[], __stop___param[];
-
-
smp_setup_processor_id();
-
-
/*
-
* Need to run as early as possible, to initialize the
-
* lockdep hash:
-
*/
-
lockdep_init();
-
debug_objects_early_init();
-
-
/*
-
* Set up the the initial canary ASAP:
-
*/
-
boot_init_stack_canary();
-
-
cgroup_init_early();
-
-
local_irq_disable();
-
early_boot_irqs_disabled = true;
-
-
/*
-
* Interrupts are still disabled. Do necessary setups, then
-
* enable them
-
*/
-
tick_init();
-
boot_cpu_init();
-
page_address_init();
-
printk(KERN_NOTICE "%s", linux_banner);
-
setup_arch(&command_line);
arch/arm/kernel/setup.c
- void __init setup_arch(char **cmdline_p)
-
{
-
struct machine_desc *mdesc;
-
-
unwind_init();
-
-
setup_processor();
-
mdesc = setup_machine_fdt(__atags_pointer);
-
if (!mdesc)
-
mdesc = setup_machine_tags(machine_arch_type);
- ......
这个函数首先检测bootloader导入的启动参数是否包含了fdt(flattened device tree扁平设备树)。这个函数一开始会核对启动参数区中是否有匹配的FDT魔数(OF_DT_HEADER),如果没有,则mdesc==NULL。那么下面就执行setup_machine_tags(machine_arch_type);。
这里还要解释一下此处调用setup_machine_tag的参数:machine_arch_type,这个其实就是全局变量__machine_arch_type(这是用了#define,参加arch/arm/tools/gen-mach-types),而这个__machine_arch_type中的数据是由内核启动代码的汇编部分(arch/arm/kernel/head.S)中将bootloader传递进来的r1数据拷贝得来的:
- /*
-
* 以下的代码段是在MMU开启的状态下执行的,
-
* 而且使用的是绝对地址; 这不是位置无关代码.
-
*
-
* r0 = cp#15 控制寄存器值
-
* r1 = machine ID
-
* r2 = atags/dtb pointer
-
* r9 = processor ID
-
*/
-
__INIT
-
__mmap_switched:
-
adr r3, __mmap_switched_data
-
-
ldmia r3!, {r4, r5, r6, r7}
-
cmp r4, r5 @ 如果有必要,拷贝数据段。
-
@ 对比__data_loc和_sdata
-
@ __data_loc是数据段在内核代码映像中的存储位置
-
@ _sdata是数据段的链接位置(在内存中的位置)
-
@ 如果是XIP技术的内核,这两个数据肯定不同
-
1: cmpne r5, r6 @ 检测数据是否拷贝完成
-
ldrne fp, [r4], #4
-
strne fp, [r5], #4
-
bne 1b
-
-
mov fp, #0 @ 清零 BSS 段(and zero fp)
-
1: cmp r6, r7 @ 检测是否完成
-
strcc fp, [r6],#4
-
bcc 1b
-
-
/* 这里将需要的数据从寄存器中转移到全局变量中,
-
* 因为最后会跳入C代码,寄存器会被使用。
-
*/
-
ARM( ldmia r3, {r4, r5, r6, r7, sp})
-
THUMB( ldmia r3, {r4, r5, r6, r7} )
-
THUMB( ldr sp, [r3, #16] )
-
str r9, [r4] @ 保存 processor ID到全局变量processor_id
-
str r1, [r5] @ 保存 machine type到全局变量__machine_arch_type
-
str r2, [r6] @ 保存 atags指针到全局变量__atags_pointer
-
bic r4, r0, #CR_A @ 清除cp15 控制寄存器值的 'A' bit(禁用对齐错误检查)
-
stmia r7, {r0, r4} @ 保存控制寄存器值到全局变量cr_alignment(在arch/arm/kernel/entry-armv.S)
-
b start_kernel @ 跳入C代码(init/main.c)
-
ENDPROC(__mmap_switched)
所以我们可以说:machine_arch_type == r1。
下面我们接着分析setup_machine_tag:
- 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;
-
-
init_tags.mem.start = PHYS_OFFSET;
-
-
/*
-
* locate machine in the list of supported machines.
-
*/
-
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 */
-
}
其中的(nr == p->nr)就是核对machine_arch_type:nr就是上面谈到的参数,p->nr其实就是在arch/arm/mach-*/mach-*.c中定义的结构体:MACHINE_START~MACHINE_END
这个宏的定义在arch/arm/include/asm/mach/arch.h
- /*
- * 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 \
- };
例如arch/arm/mach-s3c64xx/mach-mini6410.c 查看下面这个结构体:- MACHINE_START(MINI6410, "MINI6410")
- /* Maintainer: Darius Augulis */
- .boot_params = S3C64XX_PA_SDRAM + 0x100,
- .init_irq = s3c6410_init_irq,
- .map_io = mini6410_map_io,
- .init_machine = mini6410_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
所以我们可以这么说:如果r1和Linux内核代码定义的MACHINE_START(_type,_name)不匹配,那么就会打印出:
- Error: unrecognized/unsupported machine ID
并且运行 dump_machine_table():
- void __init dump_machine_table(void)
-
{
-
struct machine_desc *p;
-
-
early_print("Available machine support:\n\nID (hex)\tNAME\n");
-
for_each_machine_desc(p)
-
early_print("%08x\t%s\n", p->nr, p->name);
-
-
early_print("\nPlease check your kernel config and/or bootloader.\n");
-
-
while (true)
-
/* can't use cpu_relax() here as it may require MMU setup */;
-
}
同样死循环,挂了~~~!!!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
所以我们可以得出结论:虽然 __lookup_machine_type消失了,并不代表内核不检查bootloader通过r1传递过来的machine_type参数,只是把检查机制推迟到了C代码中(除非你实现了FDT)。
阅读(295) | 评论(0) | 转发(0) |