linux的arch/arm/kernel/head-common.S
#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
.type __switch_data, %object /*由head.S跳转过来*/
__switch_data:
.long __mmap_switched /*会跳到__mmap_switched执行*/
.long __data_loc @ r4 /*数据存放位置*/
.long __data_start @ r5 /*数据开始位置,这些在vmlinux.lds.S里面会对应相应段*/
.long __bss_start @ r6 /*BSS段开始位置*/
.long _end @ r7 /*BSS结束位置,也是内核结束位置*/
.long processor_id @ r4 /*这些会在setup.c里面用到:unsigned int processor_id; EXPORT_SYMBOL(processor_id);,其中EXPORT_SYMBOL定义在include/module.h,由extern typeof(sym) sym;可知,是用extern表示定义在外部,即本处。*/
.long __atags_pointer @ r6
.long cr_alignment @ r7
.long init_thread_union + THREAD_START_SP @ sp
/*
* The following fragment of code is executed with the MMU on in MMU mode,
* and uses absolute addresses; this is not position independent.
*
* r0 = cp#15 control register
* r1 = machine ID
* r2 = atags pointer
* r9 = processor ID
*/
__mmap_switched: /*有上面__switch_data跳转进来,这是在MMU启用模式下执行的第一段指令*/
adr r3, __switch_data + 4
ldmia r3!, {r4, r5, r6, r7}
cmp r4, r5 @ Copy data segment if needed
1: cmpne r5, r6 /*将r4开始的数据段搬移到r5,就是把__data_loc搬移到__data_start处*/
ldrne fp, [r4], #4 /*fp为r11,参数指针*/
strne fp, [r5], #4
bne 1b
mov fp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7 /*把BSS段清0*/
strcc fp, [r6],#4
bcc 1b
ldmia r3, {r4, r5, r6, r7, sp}
str r9, [r4] @ Save processor ID /*保存处理器ID到processor_id*/
str r1, [r5] @ Save machine type /*保存机器类型到__machine_arch_type*/
str r2, [r6] @ Save atags pointer /*保存参数到cr_alignment*/
bic r4, r0, #CR_A @ Clear ‘A’ bit
stmia r7, {r0, r4} @ Save control register values /*设置控制寄存器*/
b start_kernel /*跳转到init/main.c的c函数start_kernel处执行。*/
ENDPROC(__mmap_switched)
/*
* Exception handling. Something went wrong and we can’t proceed. We
* ought to tell the user, but since we don’t have any guarantee that
* we’re even running on the right architecture, we do virtually nothing.
*
* If CONFIG_DEBUG_LL is set we try to print out something about the error
* and hope for the best (useful if bootloader fails to pass a proper
* machine ID for example).
*/
__error_p: /*处理错误*/
#ifdef CONFIG_DEBUG_LL
adr r0, str_p1
bl printascii /*打印ascii字符串,这个函数定义在debug.S。*/
mov r0, r9
bl printhex8 /*打印r9机cpuid,这个函数定义在debug.S*/
adr r0, str_p2
bl printascii /*输出完整*/
b __error /*跳到__error*/
str_p1: .asciz “\nError: unrecognized/unsupported processor variant (0x”
str_p2: .asciz “).\n”
.align
#endif
ENDPROC(__error_p)
__error_a: /*不支持的机器ID错误处理*/
#ifdef CONFIG_DEBUG_LL
mov r4, r1 @ preserve machine ID
adr r0, str_a1
bl printascii
mov r0, r4
bl printhex8
adr r0, str_a2
bl printascii
adr r3, 3f
ldmia r3, {r4, r5, r6} @ get machine desc list
sub r4, r3, r4 @ get offset between virt&phys
add r5, r5, r4 @ convert virt addresses to
add r6, r6, r4 @ physical address space
1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type
bl printhex8
mov r0, #’\t’
bl printch
ldr r0, [r5, #MACHINFO_NAME] @ get machine name
add r0, r0, r4
bl printascii
mov r0, #’\n’
bl printch
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
cmp r5, r6
blo 1b
adr r0, str_a3
bl printascii
b __error
ENDPROC(__error_a)
str_a1: .asciz “\nError: unrecognized/unsupported machine ID (r1 = 0x”
str_a2: .asciz “).\n\nAvailable machine support:\n\nID (hex)\tNAME\n”
str_a3: .asciz “\nPlease check your kernel config and/or bootloader.\n”
.align
#endif
__error: /*最终错误处理*/
#ifdef CONFIG_ARCH_RPC
/*
* Turn the screen red on a error – RiscPC only.
*/
mov r0, #0x02000000 /*屏幕显示红色*/
mov r3, #0x11
orr r3, r3, r3, lsl #8
orr r3, r3, r3, lsl #16
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
#endif
1: mov r0, r0 /*一条指令无限循环*/
b 1b
ENDPROC(__error)
/*
* 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: /*查找proc_info_list这个结构数组,取得对应的处理机信息,返回r5为指向结构的指针*/
adr r3, 3f /*得到下面3标签处的实际物理地址*/
ldmda r3, {r5 – r7} /*从3标签处往下载入地址到r5,r6,r7,都是虚拟地址,就是r5=__proc_info_begin,r6=__proc_info_end,r7=标签3的地址*/
add r5, r5, r3 @ convert virt addresses to /*得到物理地址*/
add r6, r6, r3 @ physical address space
1: ldmia r5, {r3, r4} @ value, mask /*取得结构的值, r3为值,r4为掩码*/
and r4, r4, r9 @ mask wanted bits /*根据cpuid和掩码得到实际cpu标记*/
teq r3, r4 /*查看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) /*c调用方式*/
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 <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for
* more information about the __proc_info and __arch_info structures.
*/
.long __proc_info_begin /*3标签往下地址是__proc_info_begin,定义在vmlinux.lds.S的.init段的proc.info.init,这个又定义在proc-(arch).S里面,对应的c结构为Procinfo.h的proc_info_list。*/
3: .long .
.long __arch_info_begin /*3标签往上地址是__arch_info_begin,定义在vmlinux.lds.S的.init段的arch.info.init,这个又定义在Arch.h的MACHINE_START处,对应的c结构为Arch.h的machine_desc。*/
/*
* 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: /*查找machine_desc这个结构数组,取得对应的机器信息,r5返回指针指向结构*/
adr r3, 3b /*得到上面3标签处的地址*/
ldmia r3, {r4, r5, r6} /*从3标签处往上载入地址到r4,r5,r6,就是r4=标签3的地址,r5=__arch_info_begin,r6=__arch_info_end*/
add r5, r5, r3 @ convert virt addresses to /*转换虚拟地址到物理地址*/
add r6, r6, r3 @ physical address space
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) /*c调用方式*/
stmfd sp!, {r4 – r6, lr}
mov r1, r0
bl __lookup_machine_type
mov r0, r5
ldmfd sp!, {r4 – r6, pc}
ENDPROC(lookup_machine_type)
/* Determine validity of the r2 atags pointer. The heuristic requires
* that the pointer be aligned, in the first 16k of physical RAM and
* that the ATAG_CORE marker is first and present. Future revisions
* of this function may be more lenient with the physical address and
* may also be able to move the ATAGS block if necessary.
*
* r8 = machinfo
*
* Returns:
* r2 either valid atags pointer, or zero
* r5, r6 corrupted
*/
关于参数链表:
内核参数链表的格式和说明可以从内核源代码目录树中的 include/asm-arm/setup.h中
找到,参数链表必须以ATAG_CORE 开始,以ATAG_NONE结束。这里的 ATAG_CORE,
ATAG_NONE是各个参数的标记,本身是一个32位值,例如:ATAG_CORE=0x54410001。
其它的参数标记还包括: ATAG_MEM32 , ATAG_INITRD , ATAG_RAMDISK ,
ATAG_COMDLINE 等。每个参数标记就代表一个参数结构体,由各个参数结构体构成了参
数链表。参数结构体的定义如下:
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
struct tag_acorn acorn;
struct tag_memclk memclk;
} u;
};
参数结构体包括两个部分,一个是 tag_header结构体,一个是u联合体。
tag_header结构体的定义如下:
struct tag_header {
u32 size;
u32 tag;
};
其中 size:表示整个 tag 结构体的大小(用字的个数来表示,而不是字节的个数),等于
tag_header的大小加上 u联合体的大小,例如,参数结构体 ATAG_CORE 的
size=(sizeof(tag->tag_header)+sizeof(tag->u.core))>>2,一般通过函数 tag_size(struct * tag_xxx)
来获得每个参数结构体的 size。其中 tag:表示整个 tag 结构体的标记,如:ATAG_CORE
等。
__vet_atags: /*检查参数链表合法性*/
tst r2, #0x3 @ aligned? /*r2指向该参数链表的起始位置,此处判断它是否字对齐*/
bne 1f
ldr r5, [r2, #0] @ is first tag ATAG_CORE? /*获取第一个tag结构的size,#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2) 判断该tag的长度是否合法*/
subs r5, r5, #ATAG_CORE_SIZE
bne 1f
ldr r5, [r2, #4] /*获取第一个tag结构体的标记*/
ldr r6, =ATAG_CORE /*判断第一个tag结构体的标记是不是ATAG_CORE*/
cmp r5, r6
bne 1f
mov pc, lr @ atag pointer is ok /*参数链表正常,退出*/
1: mov r2, #0 /*不正常,设置r2为0,退出*/
mov pc, lr
ENDPROC(__vet_atags)