在 linux kernel booting 阶段,
asmlinkage void __init start_kernel(void)
中很重要的一个步骤是 setup arch,
setup_arch(&command_line);
对于不同的体系结构,比如 ARM 和 MIPS,它们分别需要实现这个函数,在编译的时候链接进来。
linux 内核公共部分并不关心各个体系结构的实现情况。
如 MIPS:
linux\arch\mips\kernel\setup.c
void __init setup_arch(char **cmdline_p)
{
cpu_probe();
prom_init();
#ifdef CONFIG_EARLY_PRINTK
setup_early_printk();
#endif
cpu_report();
check_bugs_early();
#if defined(CONFIG_VT)
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
arch_mem_init(cmdline_p);
resource_init();
plat_smp_setup();
cpu_cache_init();
}
在设置体系结构中每个体系做的具体细节不尽相同,但是大体上步骤类似。
比如首先需要probe cpu,再处理machine.
再看ARM:
umts_sholes\kernel\arch\arm\kernel\setup.c
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_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup("s");
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
else if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);
/*
* 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)
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);
paging_init(mdesc);
#ifdef CONFIG_OF
unflatten_device_tree();
#define DT_PATH_MACHINE "/Machine@0"
#define DT_PROP_MACHINE_TYPE "machine_type"
#define DT_PROP_CPU_TIER "cpu_tier"
{
struct device_node *machine_node;
const void *machine_prop;
const void *cpu_tier_prop;
cpu_tier = NULL;
machine_name = (char *)"mapphone_";
}
#endif
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();
}
/*
* locate processor in the list of supported processor
* types. The linker builds this table for us from the
* entries in arch/arm/mm/proc-*.S
*/
list = lookup_processor_type(read_cpuid_id());
查找处理器的类型,
read_cpuid_id() 这个函数读取 CP15 ARM 协处理器中的一个寄存器,在ARM CP15 协处理器中的一个寄存器中存放着处理器的相关信息。
#define read_cpuid(reg) \
({ \
unsigned int __val; \
asm("mrc p15, 0, %0, c0, c0, " __stringify(reg) \
: "=r" (__val) \
: \
: "cc"); \
__val; \
})
从协处理器中读取到当前硬件ARM 处理器的类型之后就需要查找当前内核中有没有处理这个类型ARM 处理器的代码了,
lookup_processor_type()
这个函数是用汇编实现的。
链接时会将 android/umts_sholes/kernel/arch/arm/mm/proc-*.S 这些文件中的section 放在一个段中,
android/umts_sholes/kernel/arch/arm/mm$ ls proc-*
proc-arm1020e.S proc-arm1026.S proc-arm740.S proc-arm922.S proc-arm940.S proc-fa526.S proc-mohawk.S proc-syms.c proc-xsc3.S
proc-arm1020.S proc-arm6_7.S proc-arm7tdmi.S proc-arm925.S proc-arm946.S proc-feroceon.S proc-sa1100.S proc-v6.S proc-xscale.S
proc-arm1022.S proc-arm720.S proc-arm920.S proc-arm926.S proc-arm9tdmi.S proc-macros.S proc-sa110.S proc-v7.S
函数定义在: ./arch/arm/kernel/head-common.S (arch/arm/kernel/head.S 将这个文件include 了, #include "head-common.S")
/*
* Read processor ID register (CP#15, CR0), and look up in the linker-built
* supported processor list. Note that we can't use the absolute addresses
* for the __proc_info lists since we aren't running with the MMU on
* (and therefore, we are not in the correct address space). We have to
* calculate the offset.
*
* r9 = cpuid
* Returns:
* r3, r4, r6 corrupted
* r5 = proc_info pointer in physical address space
* r9 = cpuid (preserved)
*/
__lookup_processor_type:
adr r3, 3f
ldmia r3, {r5 - r7}
add r3, r3, #8
sub r3, r3, r7@ get offset between virt&phys
add r5, r5, r3@ convert virt addresses to
add r6, r6, r3@ physical address space
1: ldmia r5, {r3, r4} @ value, mask
and r4, r4, r9@ mask wanted bits
teq r3, r4
beq 2f
add r5, r5, #PROC_INFO_SZ@ sizeof(proc_info_list)
cmp r5, r6
blo 1b
mov r5, #0@ unknown processor
2: mov pc, lr
ENDPROC(__lookup_processor_type)
/*
* This provides a C-API version of the above function.
*/
ENTRY(lookup_processor_type)
stmfd sp!, {r4 - r7, r9, lr}
mov r9, r0
bl __lookup_processor_type
mov r0, r5
ldmfd sp!, {r4 - r7, r9, pc}
ENDPROC(lookup_processor_type)
/*
* Look in
* more information about the __proc_info and __arch_info structures.
*/
.align 2
3: .long __proc_info_begin
.long __proc_info_end
4: .long .
.long __arch_info_begin
.long __arch_info_end
看下,各个ARM 处理器的类型对于这个段的定义部分,
android/umts_sholes/kernel/arch/arm/mm/proc-v7.S
.section ".proc.info.init", #alloc, #execinstr
/*
* Match any ARMv7 processor core.
*/
.type __v7_proc_info, #object
__v7_proc_info:
.long 0x000f0000@ Required ID value
.long 0x000f0000@ Mask for ID
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ | \
PMD_FLAGS
.long PMD_TYPE_SECT | \
PMD_SECT_XN | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __v7_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_v7_name
.long v7_processor_functions
.long v7wbi_tlb_fns
.long v6_user_fns
.long v7_cache_fns
.size __v7_proc_info, . - __v7_proc_info
因此,可以在 kernel booting 的log 中看到
# dmesg
<6>[ 0.000000] Initializing cgroup subsys cpu
<5>[ 0.000000] Linux version 2.6.32.60-cust-g801e7be (qiang@qiang-desktop) (gcc version 4.4.0 (GCC) ) #1 PREEMPT Mon Aug 19 16:41:23 CST 2013
<4>[ 0.000000] CPU: ARMv7 Processor [411fc083] revision 3 (ARMv7), cr=10c53c7f
<4>[ 0.000000] CPU: VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
<4>[ 0.000000] Machine: mapphone_
就是下面的语句打印出来的,
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);
接下来,就到了设置机器 Machine的时候了,
mdesc = setup_machine(machine_arch_type);
首先看 machine_arch_type 这个变量从哪定义的呢?
./kernel_build/include/asm-arm/mach-types.h
#define MACH_TYPE_MAPPHONE 2241
#ifdef CONFIG_MACH_MAPPHONE
# ifdef machine_arch_type
# undef machine_arch_type
# define machine_arch_type __machine_arch_type
# else
# define machine_arch_type MACH_TYPE_MAPPHONE
# endif
# define machine_is_mapphone() (machine_arch_type == MACH_TYPE_MAPPHONE)
#else
# define machine_is_mapphone() (0)
#endif
再看__machine_arch_type
ulg
decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,
int arch_id)
{
output_data= (uch *)output_start;/* Points to kernel start */
free_mem_ptr= free_mem_ptr_p;
free_mem_end_ptr= free_mem_ptr_end_p;
__machine_arch_type= arch_id;
arch/arm/boot/compressed/head.S调用了解压内核的函数:
mov r5, r2@ decompress after malloc space
mov r0, r5
mov r3, r7
bl decompress_kernel
可以看到,这个函数的最后一个参数是通过 r3 传过去的, 也就是 ARCH ID。
start:
.type start,#function
.rept 8
mov r0, r0
.endr
b 1f
.word 0x016f2818@ Magic numbers to help the loader
.word start@ absolute load/run zImage address
.word _edata@ zImage end address
1: mov r7, r1 @ save architecture ID
mov r8, r2@ save atags pointer
bootloader 将 architecture ID 放入 r1 传递给内核。
参考:
http://blog.csdn.net/sehrich/article/details/7208752
http://blog.csdn.net/liuyangxiyou/article/details/5933756
http://www.cnblogs.com/lknlfy/archive/2012/05/06/2486479.html
http://blog.chinaunix.net/uid-20672257-id-2891129.html
http://www.cnblogs.com/armlinux/archive/2011/11/07/2396784.html
http://blog.csdn.net/aaronychen/article/details/2838341
再看下Machine 设置的代码,
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
看下查找的方法:
/*
* locate machine in the list of supported machines.
*/
list = lookup_machine_type(nr);
printk("Machine: %s\n", list->name);
查找到了话,打印出log:
<4>[ 0.000000] Machine: mapphone_
lookup_machine_type 还是汇编实现的,
arch/arm/kernel/head-common.S
4: .long .
.long __arch_info_begin
.long __arch_info_end
/*
* Lookup machine architecture in the linker-build list of architectures.
* Note that we can't use the absolute addresses for the __arch_info
* lists since we aren't running with the MMU on (and therefore, we are
* not in the correct address space). We have to calculate the offset.
*
* r1 = machine architecture number
* Returns:
* r3, r4, r6 corrupted
* r5 = mach_info pointer in physical address space
*/
__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
#if defined(CONFIG_MACH_MAPPHONE)
rsb r3, r5, r6@ number of machine types
teq r3, #SIZEOF_MACHINE_DESC@ only one?
ldreq r1, [r5]@ if so do not bother with r1
beq 2f@ ...and be happy.
#endif
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)
/*
* This provides a C-API version of the above function.
*/
ENTRY(lookup_machine_type)
stmfd sp!, {r4 - r6, lr}
mov r1, r0
bl __lookup_machine_type
mov r0, r5
ldmfd sp!, {r4 - r6, pc}
ENDPROC(lookup_machine_type)
查找还是在 规定的段区间中进行: __arch_info_begin ~ __arch_info_end
arch/arm/kernel/vmlinux.lds.S
.init : { /* Init code and data */
INIT_TEXT
_einittext = .;
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
. = ALIGN(16);
这样,我们只需要关心代码实现中用 attribute 指定到*(.arch.info.init) 这个段中的。
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-omap2/board-mapphone.c
MACHINE_START(MAPPHONE, "mapphone_")
/* Maintainer: Motorola, Inc. */
.phys_io = 0x48000000,
.io_pg_offst = ((0xfa000000) >> 18) & 0xfffc,
.boot_params = 0x80C00100,
.map_io = mapphone_map_io,
.init_irq = mapphone_init_irq,
.init_machine = mapphone_init,
.timer = &omap_timer,
MACHINE_END