1: 信息和文件
代码版本:Linux-3.4.2
2:板级信息传递
目的:板级信息如何传递到linux内核中去
文件:
linux-3.4.2\arch\arm\kernel\head-common.S
linux-3.4.2/arc/arm/kernel/vimlinux.lds.S : 链接文件
linux-3.4.2\arch\arm\include\asm\mach\arch.h
linux-3.4.2\arch\arm\mach-s3c24xx\mach-smdk2440.c
2.1:段信息内容
vimlinux.lds.S 中定义可以存放的段,后续可以根据段的名称存放自己的信息
//vimlinux.lds.S
INIT_TEXT_SECTION(8)
.exit.text : {
ARM_EXIT_KEEP(EXIT_TEXT)
}
.init.proc.info : {
ARM_CPU_DISCARD(PROC_INFO)
}
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
.init.tagtable : {
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
}
2.1 对段的赋值
例如这个段的信息*(.arch.info.init)
在arch.h 定义了使用这个段名的结构体 struct machine_desc
/*
* 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 \
};
注意最后一行的#define MACHINE_END };
代表MACHINE_END 是符号" } "
在mach-smdk2440.c中就用了了这个结构体
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.atag_offset = 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
.restart = s3c244x_restart,
MACHINE_END
对齐宏展开得到
#define MACHINE_START(_type,_name)
MACHINE_START(S3C2440, "SMDK2440")
static const struct machine_desc __mach_desc_S3C2440\
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_S3C2440, \
.name = "SMDK2440",
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.atag_offset = 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
.restart = s3c244x_restart,
};
上面的函数,创建一个静态常量的结构体static const struct machine_desc
并通过 __attribute__((__section__(".arch.info.init")))
把结构体放到了段名为".arch.info.init"的位置, 后面可以通过这个段来找到定义的结构体
其中这个结构体的定义
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 DMA区域大小*/
#endif
unsigned int video_start; /* start of video RAM RAM的起始*/
unsigned int video_end; /* end of video RAM RAM的结束*/
unsigned char reserve_lp0 :1; /* never has lp0 */
unsigned char reserve_lp1 :1; /* never has lp1 */
unsigned char reserve_lp2 :1; /* 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);
#ifdef CONFIG_MULTI_IRQ_HANDLER
void (*handle_irq)(struct pt_regs *); //中断处理函数
#endif
void (*restart)(char, const char *);
};
所有的板级文件都定义了这样的结构体,用于Linux来做板子的识别结构体,这些结构体被限定在了内存的某个区域
并且通过UBOOT传递过来参数进行结构体的配置 (通过检索tagged list)
并且在移植Linux的时候,也要对结构体的变量进行赋值
并且在之后的启动或其他函数中,对该结构体的变量进行调用
3:内核启动
head-common.S linux-3.4.2\arch\arm\kernel
ENTRY(lookup_processor_type)
stmfd sp!, {r4 - r6, r9, lr}
mov r9, r0
bl __lookup_processor_type
mov r0, r5
ldmfd sp!, {r4 - r6, r9, pc}
ENDPROC(lookup_processor_type)
使用uboot传递的id号和 内核指定的id号进行对比,如果一致,则许局运行
__CPUINIT
__lookup_processor_type:
adr r3, __lookup_processor_type_data //r3指定区域内核支持的号
ldmia r3, {r4 - 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
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)
//将旧的地址转花成虚拟地址
__mmap_switched:
adr r3, __mmap_switched_data
ldmia r3!, {r4, r5, r6, r7}
cmp r4, r5 @ Copy data segment if needed
1: cmpne r5, r6
ldrne fp, [r4], #4
strne fp, [r5], #4
bne 1b
mov fp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b
ARM( ldmia r3, {r4, r5, r6, r7, sp})
THUMB( ldmia r3, {r4, r5, r6, r7} )
THUMB( ldr sp, [r3, #16] )
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
str r2, [r6] @ Save atags pointer
bic r4, r0, #CR_A @ Clear 'A' bit
stmia r7, {r0, r4} @ Save control register values
b start_kernel //最终跳转到start_kernel 函数
ENDPROC(__mmap_switched)
3.1 __lookup_processor_type_data
其中__lookup_processor_type_data在下面定义
/*
* Look in <asm/procinfo.h> for information about the __proc_info structure.
*/
.align 2
.type __lookup_processor_type_data, %object
__lookup_processor_type_data:
.long .
.long __proc_info_begin
.long __proc_info_end
.size __lookup_processor_type_data, . - __lookup_processor_type_data
其中__lookup_processor_type_data是__proc_info_begin 和__proc_info_end的内存区域,这两个名称在链接脚本能找到
//vmlinux.lds.S linux-3.4.2\arch\arm\kernel 6265 2012/6/9
#define PROC_INFO \
. = ALIGN(4); \
VMLINUX_SYMBOL(__proc_info_begin) = .; \ \\段开始
*(.proc.info.init) \ \\段区域
VMLINUX_SYMBOL(__proc_info_end) = .; \\段结尾
#define IDMAP_TEXT \
ALIGN_FUNCTION(); \
VMLINUX_SYMBOL(__idmap_text_start) = .; \
*(.idmap.text) \
VMLINUX_SYMBOL(__idmap_text_end) = .;
其中*(.proc.info.init) 的区域这个段对应各个芯片的信息
3.1.1 对应CPU信息示例
以arm920为例
这个段对应arm920的信息如下, Linux写好的CPU proID号
//proc-arm920.S linux-3.4.2\arch\arm\mm 11770 2012/6/9
.align
.section ".proc.info.init", #alloc, #execinstr
.type __arm920_proc_info,#object
__arm920_proc_info:
.long 0x41009200
.long 0xff00fff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm920_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
.long cpu_arm920_name
.long arm920_processor_functions
.long v4wbi_tlb_fns
.long v4wb_user_fns
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
.long arm920_cache_fns
#else
.long v4wt_cache_fns
#endif
.size __arm920_proc_info, . - __arm920_proc_info
3.2 start_kernel
//main.c linux-3.4.2\init
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init(); //锁初始化
smp_setup_processor_id(); //并行处理设置CPU id
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(); //初始化tick jiffes
boot_cpu_init(); //初始化cpu
page_address_init();//初始化页地址
printk(KERN_NOTICE "%s", linux_banner);
setup_arch(&command_line); //找到machine_desc,并对一些全局变量赋值,供下面函数使用,例如下面的用的init_mm command_line
mm_init_owner(&init_mm, &init_task);
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
build_all_zonelists(NULL);
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
parse_early_param(); //参数早期解析,复制setup_arch设置的全面变量
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
0, 0, &unknown_bootoption);
jump_label_init();
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
setup_log_buf(0);
pidhash_init();
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it\n");
local_irq_disable();
}
idr_init_cache();
perf_event_init();
rcu_init();
radix_tree_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
prio_tree_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
profile_init();
call_function_init();
if (!irqs_disabled())
printk(KERN_CRIT "start_kernel(): bug: interrupts were "
"enabled early\n");
early_boot_irqs_disabled = false;
local_irq_enable();
kmem_cache_init_late();
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
console_init();
if (panic_later)
panic(panic_later, panic_param);
lockdep_info();
/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
locking_selftest();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
"disabling it.\n",
page_to_pfn(virt_to_page((void *)initrd_start)),
min_low_pfn);
initrd_start = 0;
}
#endif
page_cgroup_init();
debug_objects_mem_init();
kmemleak_init();
setup_per_cpu_pageset();
numa_policy_init();
if (late_time_init)
late_time_init();
sched_clock_init();
calibrate_delay();
pidmap_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
proc_caches_init();
buffer_init();
key_init();
security_init();
dbg_late_init();
vfs_caches_init(totalram_pages);
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cgroup_init();
cpuset_init();
taskstats_init_early();
delayacct_init();
check_bugs();
acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();
ftrace_init();
/* Do the rest non-__init'ed, we're now alive */
rest_init(); //开辟了线程,运行了kernel_init函数其中执行了init_post(系统运行的第一个应用程序)
}
3.2.1 setup_arch
setup_arch:根据uboot的参数,找到最合适的machine_desc 结构体,并非对其进行赋值,然后通过赋值完的machine_desc定义各种全局变量,之后内核会使用这样这写全局变量
setup_arch(&command_line);
//setup.c linux-3.4.2\arch\arm\kernel
void __init setup_arch(char **cmdline_p)
{
struct machine_desc *mdesc;
setup_processor();
mdesc = setup_machine_fdt(__atags_pointer); //找到machine_desc, 依据是__atags_pointer参数 代表的是Uboot的taglist进行解析
if (!mdesc) //没找到
mdesc = setup_machine_tags(machine_arch_type); //设置默认值
machine_desc = mdesc;//获取machine_desc(全局)
machine_name = mdesc->name; //获取machine_name(全局)
#ifdef CONFIG_ZONE_DMA
if (mdesc->dma_zone_size) {
extern unsigned long arm_dma_zone_size;
arm_dma_zone_size = mdesc->dma_zone_size;
}
#endif
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[0]), 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();
}
3.2.1.1 setup_processor();
//setup.c linux-3.4.2\arch\arm\kernel
static void __init setup_processor(void)
{
struct proc_info_list *list; //创建CPU指令集描述结构体
/*
* 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());//从内存中获取该描述结构体
if (!list) {
printk("CPU configuration botched (ID %08x), unable "
"to continue.\n", read_cpuid_id());
while (1);
}
cpu_name = list->cpu_name;//将获取的CPU名字赋值给全局变量cpu_name
__cpu_architecture = __get_cpu_architecture();
#ifdef MULTI_CPU
processor = *list->proc;
#endif
#ifdef MULTI_TLB
cpu_tlb = *list->tlb;
#endif
#ifdef MULTI_USER
cpu_user = *list->user;
#endif
#ifdef MULTI_CACHE
cpu_cache = *list->cache;
#endif
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);
snprintf(init_utsname()->machine, __NEW_UTS_LEN + 1, "%s%c",
list->arch_name, ENDIANNESS);
snprintf(elf_platform, ELF_PLATFORM_SIZE, "%s%c",
list->elf_name, ENDIANNESS);
elf_hwcap = list->elf_hwcap;
#ifndef CONFIG_ARM_THUMB
elf_hwcap &= ~HWCAP_THUMB;
#endif
feat_v6_fixup();
cacheid_init();
cpu_init();
}
关于struct proc_info_list
//head-common.S linux-3.4.2\arch\arm\kernel 5211 2012/6/9
__CPUINIT
__lookup_processor_type:
adr r3, __lookup_processor_type_data
ldmia r3, {r4 - 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
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) //这里使用这个结构体 proc_info_list
cmp r5, r6
blo 1b
mov r5, #0 @ unknown processor
2: mov pc, lr
ENDPROC(__lookup_processor_type)
3.2.1.2 setup_machine_fdt();
//找到最适合的 machine_desc
struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
struct boot_param_header *devtree;
struct machine_desc *mdesc, *mdesc_best = NULL;
unsigned int score, mdesc_score = ~1;
unsigned long dt_root;
const char *model;
if (!dt_phys)
return NULL;
devtree = phys_to_virt(dt_phys); //物理地址->虚拟地址
/* check device tree validity */
if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
return NULL;
/* Search the mdescs for the 'best' compatible value match 进行匹配*/
initial_boot_params = devtree;
dt_root = of_get_flat_dt_root();
for_each_machine_desc(mdesc) { //遍历mdesc,从写好的板级信息找到最适合的 machine_desc 赋值给mdesc_best
score = of_flat_dt_match(dt_root, mdesc->dt_compat);
if (score > 0 && score < mdesc_score) { //找到最为相似的结构体
mdesc_best = mdesc;
mdesc_score = score;
}
}
if (!mdesc_best) {
const char *prop;
long size;
early_print("\nError: unrecognized/unsupported "
"device tree compatible list:\n[ ");
prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
while (size > 0) {
early_print("'%s' ", prop);
size -= strlen(prop) + 1;
prop += strlen(prop) + 1;
}
early_print("]\n\n");
dump_machine_table(); /* does not return */
}
model = of_get_flat_dt_prop(dt_root, "model", NULL);
if (!model)
model = of_get_flat_dt_prop(dt_root, "compatible", NULL);
if (!model)
model = "<unknown>";
pr_info("Machine: %s, model: %s\n", mdesc_best->name, model);
/* Retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
/* Initialize {size,address}-cells info */
of_scan_flat_dt(early_init_dt_scan_root, NULL);
/* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
/* Change machine number to match the mdesc we're using */
__machine_arch_type = mdesc_best->nr;
return mdesc_best;
}
其中for_each_machine_desc§ 对应
#define for_each_machine_desc(p) \
for (p = __arch_info_begin; p < __arch_info_end; p++)
其中 __arch_info_begin
和__arch_info_end
对应的就是这个段 *(.arch.info.init)
//vimlinux.lds.S
INIT_TEXT_SECTION(8)
.exit.text : {
ARM_EXIT_KEEP(EXIT_TEXT)
}
.init.proc.info : {
ARM_CPU_DISCARD(PROC_INFO)
}
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
这个段中的数据在前面已经进行了赋值