linux kernel setup_arch

在 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();
}

MIPS 体系实现自己 设置体系结构相关的函数,自己需要做的事情毕竟自己最清楚。

在设置体系结构中每个体系做的具体细节不尽相同,但是大体上步骤类似。

比如首先需要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();
}

首先,setup processor; 之后, setup machine。

/*
* 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 and arch/arm/kernel/arch.[ch] for
 * 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

你可能感兴趣的:(Linux,Kernel,ARM)