您的位置:首页 > 运维架构 > Linux

论 __lookup_machine_type的消失(Linux-3.0 ARMv7)

2015-07-03 10:44 591 查看
http://blog.chinaunix.net/uid-20543672-id-3019566.html

在分析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

adrr3, 2f

ldmiar3, {r4, r8}

subr4, r3, r4@
(PHYS_OFFSET - PAGE_OFFSET)

addr8, r8, r4@
PHYS_OFFSET

#else

ldrr8, =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);。
而在我们正常的情况下,ARM的构架中我没有见过实现扁平设备树的,这个似乎是原先PPC的东西,具体的资料请看:《全面解析PowerPC架构下的扁平设备树FDT》,所以可以说正常情况下都是去执行setup_machine_tag。

这里还要解释一下此处调用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)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: