Linux内核---13.启动分析1之arch/arm/kernel/head.S
2016-07-02 14:51
681 查看
0、实验环境:
硬件: TQ2440
内核: 2.6.25
uboot将内核从nand flash读到内存的0x3000800处,并解压,此时:
r0 = 0
r1 = machine_number (uboot中设为168)
r2 = 0x30008000 (r2不是参数地址)
真正的参数是在uboot的setup_linux_param设置的,
uboot1.1.6/lib_arm/test_zImage.c
int test_zImage(void)
--> setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET); //将参数拷贝到了0x3000100处
注意: 虽然跳过了uncompress阶段,但是arch/arm/kernel/head.S还是运行在0x3000800处。
一、 整体分析
1.1
从arch/arm/kernel/vmlinux.lds.S可以看出
.text.head : {
_stext = .;
_sinittext = .;
*(.text.head)
}
1.2 程序的起始在 arch/arm/kernel/head.S
79 ENTRY(stext)
80 msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
@ ensure svc mode
81 @ and irqs disabled
82 mrc p15, 0, r9, c0, c0
@ get processor id
83 bl __lookup_processor_type @ r5=procinfo r9=cpuid
84 movs r10, r5 @ invalid processor (r5=0)?
85 beq __error_p @ yes, error 'p'
86 bl __lookup_machine_type @ r5=machinfo
87 movs r8, r5 @ invalid machine (r5=0)?
88 beq __error_a @ yes, error 'a'
89 bl __vet_atags
90 bl __create_page_tables
91
92 /*
93 * The following calls CPU specific code in a position independent
94 * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
95 * xxx_proc_info structure selected by __lookup_machine_type
96 * above. On return, the CPU will be ready for the MMU to be
97 * turned on, and r0 will hold the CPU control register value.
98 */
99 ldr r13, __switch_data @ address to jump to after
100 @ mmu has been enabled
101 adr lr, __enable_mmu @ return (PIC) address
102 add pc, r10, #PROCINFO_INITFUNC
80行:
将CPSR的I与F位置1,关闭IRQ与FRQ,同时模式设为SVC模式
82行 将cpu_id读到r9中
83行 查找_proc_info段,看有没有与以硬件寄存器中的cpu_id相匹配的proc_info, r5指向查找到的procinfo
84行 将r5保存在r10中,即r10指向查找到的procinfo的首地址
85行 为0,打印错误信息
86行 在arch_info中查找machine_nr=r1=168的machine_desc, r5指向查找到的machine_desc
87行 将r5保存在r8中,即r8是指向machine_desc的首地址
88行 为0,打印错误信息
89行 检查参数, 因为r2=0x30008000,不是参数列表的地址,所以执行完后r2=0
90行 创建页表
99-102行 执行顺序 initfunc->__enble_mmu->switch_data->start_kernel
1.2 试想如果有以下要求,自己怎么写?
把r5结果保存到r10中,并且判断是否为0,为0则打印错误
肯定会写出:
mov r10, r5
cmp r10, #0
beq __error_p
但是还有更简炼的写法:
movs r10, r5
beq __error_p
二、__lookup_processor_type分析
2.1 在文件arch/arm/kernel/head_common.S中
151 .type __lookup_processor_type, %function
152 __lookup_processor_type:
153 adr r3, 3f //L184行的运行地址(0x30008000+offset)存到r3中
154 ldmda r3, {r5 - r7}
//r5=_proc_info_begin; r6=__proc_info_end; r7=3f; r5,r6,r7都是程序的存储地址
155 sub r3, r3, r7 @ get offset between virt&phys //
r3=r3-r7=0x30008000
156 add r5, r5, r3 @ convert virt addresses to //
r5=r5+r3=0x30008000+__proc_info_begin(即r5指向当前内存中第一个proc_info结构体首地址)
157 add r6, r6, r3 @ physical address space //
r6=r6+r3=0x30008000+__proc_info_end
158 1: ldmia r5, {r3, r4} @ value, mask
// r3=cpu_val=0x41009200; r4=cpu_mask=0xff00fff0
159 and r4, r4, r9 @ mask wanted
bits // r4=r4&r9 r9是从协处理器中读取出来的cpu_val
160 teq r3, r4 // 将程序中的cpu_val与硬件中的cpu_val相比较看是否相同
161 beq 2f //相等说明找到了这个结构体,就返回
162 add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) //不相等查找下一个结构体
163 cmp r5, r6
164 blo 1b
165 mov r5, #0 @ unknown processor
166 2: mov pc, lr
167
168 /*
169 * This provides a C-API version of the above function.
170 */
171 ENTRY(lookup_processor_type)
172 stmfd sp!, {r4 - r7, r9, lr}
173 mov r9, r0
174 bl __lookup_processor_type
175 mov r0, r5
176 ldmfd sp!, {r4 - r7, r9, pc}
177
178 /*
179 * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
180 * more information about the __proc_info and __arch_info structures.
181 */
182 .long __proc_info_begin
183 .long __proc_info_end
184 3: .long .
185 .long __arch_info_begin
186 .long __arch_info_end
2.2 struct
proc_info_list
arch/arm/kernel/asm-offsets.c
struct proc_info_list {
unsigned int cpu_val;
unsigned int cpu_mask;
unsigned long __cpu_mm_mmu_flags; /* used by head.S */
unsigned long __cpu_io_mmu_flags; /* used by head.S */
unsigned long __cpu_flush; /* used by head.S */
const char *arch_name;
const char *elf_name;
unsigned int elf_hwcap;
const char *cpu_name;
struct processor *proc;
struct cpu_tlb_fns *tlb;
struct cpu_user_fns *user;
struct cpu_cache_fns *cache;
};
2.3 从arch/arm/kernel/vmlinux.lds.S可以看出
.init : { /* Init code and data */
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
}
同时 arch/arm/mm/proc-arm920.S中
.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
三、__lookup_machine_type:
3.1 在文件arch/arm/kernel/head_common.S中
182 .long __proc_info_begin
183 .long __proc_info_end
184 3: .long .
185 .long __arch_info_begin
186 .long __arch_info_end
199 .type __lookup_machine_type, %function
200 __lookup_machine_type:
201 adr r3, 3b // r3=r3-r7=0x30008000;小三还是可以重复利用的
202 ldmia r3, {r4, r5, r6} //
r4=3f, r5=arch_info_begin; r6=arch_info_end; arch_info紧跟 proc_info
203 sub r3, r3, r4 @ get offset between virt&phys //
r3=r3-r4=0x30008000
204 add r5, r5, r3 @ convert virt addresses to //
r5=r5+r3
205 add r6, r6, r3 @ physical address space //
r6=r6+r3
206 1: ldr r3, [r5, #MACHINFO_TYPE] @
get machine type // r3=struct machine_desc->nr,因machine_nr并没有存在结构体的首位,所以需要加上偏移
207 teq r3, r1 @ matches loader number? // r3是较内核中的machine_nr, r1是uboot传入的machine_nr,比较两者是否相同
208 beq 2f @ found
209 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc //
不相同,则查找下一个machine_desc
210 cmp r5, r6
211 blo 1b
212 mov r5, #0 @ unknown machine
213 2: mov pc, lr
3.2 从arch/arm/kernel/vmlinux.lds.S可以看出
.init : { /* Init code and data */
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
arch_info紧跟 proc_info
3.2.1 在arch/arm/mach/arch.h中
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head.S, head-common.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
#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 \
}
3.4 在arch/arm/mach-s3c440/mach-smdk2440.c中
对结构体machine_desc进行初始化
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
四、检查uboot的参数
4.1 在文件arch/arm/kernel/head_common.S中
238 .type __vet_atags, %function
239 __vet_atags:
240 tst r2, #0x3 @ aligned?
241 bne 1f
242
243 ldr r5, [r2, #0] @
is first tag ATAG_CORE?
244 subs r5, r5, #ATAG_CORE_SIZE
245 bne 1f
246 ldr r5, [r2, #4]
247 ldr r6, =ATAG_CORE
248 cmp r5, r6
249 bne 1f
250
251 mov pc, lr @ atag pointer is ok
252
253 1: mov r2, #0
254 mov pc, lr
这儿的r2=0x30008000,内核就拷贝到了这个地址,并不是参数链表,所以这个地方的检查 bne 1f, 然后r2=0;
附:
1.协处理寄存器->arm寄存器
mrc {} p15, 0, , , {,}
a. 条件码,忽略时无条件执行
b. 永远为 0
c. , 保存结果
d. , 协处理器中的寄存器
d. , 协寄存器的附加信息,当不需要附加信息时,要设为c0
d. , 附加信息,省略时默为0
2: 指令
2.1 movs指令
S决定指令操作是否影响CPSR的值
2.2 ldmda指令:
使用多数据传送指令(LDM 和 STM)来装载和存储多个字的数据从/到内存。
LDM/STM 的主要用途是把需要保存的寄存器复制到栈上。如我们以前见到过的 STMFD R13!, {R0-R12, R14}。
指令格式是:
xxM{条件}{类型} Rn{!}, <寄存器列表>{^}
‘xx’是 LD 表示装载,或 ST 表示存储。
再加 4 种‘类型’就变成了 8 个指令:
栈 其他
LDMED (空递减) LDMIB 预先增加装载
LDMFD (满递减) LDMIA 过后增加装载
LDMEA (空递增) LDMDB 预先减少装载
LDMFA ?(满递增) LDMDA 过后减少装载
STMFA ?(满递增) STMIB 预先增加存储
STMEA (空递增) STMIA 过后增加存储
STMFD (满递减) STMDB 预先减少存储
STMED ?(空递减) STMDA 过后减少存储
关于空递减和满递减等等,理解:
指针存储往减少方向发展的是“递减”,反之,存储往增加方向发展的是“递增”。
指针指向下一个存储位置的是“空”,反之,指针指向最后一个存储位置的是“满”。
[参考文章]
1. arm linux kernel 从入口到start_kernel的代码分析
http://bbs.chinaunix.net/thread-2039668-1-1.html
2. Linux 内核启动分析之"arch/arm/kernel/head.S"
http://blog.sina.com.cn/s/blog_63ac1cef0100vbcb.html
3. linux内核启运过程分析
http://chxxxyg.blog.163.com/blog/static/150281193201072603030285/
4. arm体系结构与编程-杜春雷
硬件: TQ2440
内核: 2.6.25
uboot将内核从nand flash读到内存的0x3000800处,并解压,此时:
r0 = 0
r1 = machine_number (uboot中设为168)
r2 = 0x30008000 (r2不是参数地址)
真正的参数是在uboot的setup_linux_param设置的,
uboot1.1.6/lib_arm/test_zImage.c
int test_zImage(void)
--> setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET); //将参数拷贝到了0x3000100处
注意: 虽然跳过了uncompress阶段,但是arch/arm/kernel/head.S还是运行在0x3000800处。
一、 整体分析
1.1
从arch/arm/kernel/vmlinux.lds.S可以看出
.text.head : {
_stext = .;
_sinittext = .;
*(.text.head)
}
1.2 程序的起始在 arch/arm/kernel/head.S
79 ENTRY(stext)
80 msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
@ ensure svc mode
81 @ and irqs disabled
82 mrc p15, 0, r9, c0, c0
@ get processor id
83 bl __lookup_processor_type @ r5=procinfo r9=cpuid
84 movs r10, r5 @ invalid processor (r5=0)?
85 beq __error_p @ yes, error 'p'
86 bl __lookup_machine_type @ r5=machinfo
87 movs r8, r5 @ invalid machine (r5=0)?
88 beq __error_a @ yes, error 'a'
89 bl __vet_atags
90 bl __create_page_tables
91
92 /*
93 * The following calls CPU specific code in a position independent
94 * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
95 * xxx_proc_info structure selected by __lookup_machine_type
96 * above. On return, the CPU will be ready for the MMU to be
97 * turned on, and r0 will hold the CPU control register value.
98 */
99 ldr r13, __switch_data @ address to jump to after
100 @ mmu has been enabled
101 adr lr, __enable_mmu @ return (PIC) address
102 add pc, r10, #PROCINFO_INITFUNC
80行:
将CPSR的I与F位置1,关闭IRQ与FRQ,同时模式设为SVC模式
82行 将cpu_id读到r9中
83行 查找_proc_info段,看有没有与以硬件寄存器中的cpu_id相匹配的proc_info, r5指向查找到的procinfo
84行 将r5保存在r10中,即r10指向查找到的procinfo的首地址
85行 为0,打印错误信息
86行 在arch_info中查找machine_nr=r1=168的machine_desc, r5指向查找到的machine_desc
87行 将r5保存在r8中,即r8是指向machine_desc的首地址
88行 为0,打印错误信息
89行 检查参数, 因为r2=0x30008000,不是参数列表的地址,所以执行完后r2=0
90行 创建页表
99-102行 执行顺序 initfunc->__enble_mmu->switch_data->start_kernel
1.2 试想如果有以下要求,自己怎么写?
把r5结果保存到r10中,并且判断是否为0,为0则打印错误
肯定会写出:
mov r10, r5
cmp r10, #0
beq __error_p
但是还有更简炼的写法:
movs r10, r5
beq __error_p
二、__lookup_processor_type分析
2.1 在文件arch/arm/kernel/head_common.S中
151 .type __lookup_processor_type, %function
152 __lookup_processor_type:
153 adr r3, 3f //L184行的运行地址(0x30008000+offset)存到r3中
154 ldmda r3, {r5 - r7}
//r5=_proc_info_begin; r6=__proc_info_end; r7=3f; r5,r6,r7都是程序的存储地址
155 sub r3, r3, r7 @ get offset between virt&phys //
r3=r3-r7=0x30008000
156 add r5, r5, r3 @ convert virt addresses to //
r5=r5+r3=0x30008000+__proc_info_begin(即r5指向当前内存中第一个proc_info结构体首地址)
157 add r6, r6, r3 @ physical address space //
r6=r6+r3=0x30008000+__proc_info_end
158 1: ldmia r5, {r3, r4} @ value, mask
// r3=cpu_val=0x41009200; r4=cpu_mask=0xff00fff0
159 and r4, r4, r9 @ mask wanted
bits // r4=r4&r9 r9是从协处理器中读取出来的cpu_val
160 teq r3, r4 // 将程序中的cpu_val与硬件中的cpu_val相比较看是否相同
161 beq 2f //相等说明找到了这个结构体,就返回
162 add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) //不相等查找下一个结构体
163 cmp r5, r6
164 blo 1b
165 mov r5, #0 @ unknown processor
166 2: mov pc, lr
167
168 /*
169 * This provides a C-API version of the above function.
170 */
171 ENTRY(lookup_processor_type)
172 stmfd sp!, {r4 - r7, r9, lr}
173 mov r9, r0
174 bl __lookup_processor_type
175 mov r0, r5
176 ldmfd sp!, {r4 - r7, r9, pc}
177
178 /*
179 * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
180 * more information about the __proc_info and __arch_info structures.
181 */
182 .long __proc_info_begin
183 .long __proc_info_end
184 3: .long .
185 .long __arch_info_begin
186 .long __arch_info_end
2.2 struct
proc_info_list
arch/arm/kernel/asm-offsets.c
struct proc_info_list {
unsigned int cpu_val;
unsigned int cpu_mask;
unsigned long __cpu_mm_mmu_flags; /* used by head.S */
unsigned long __cpu_io_mmu_flags; /* used by head.S */
unsigned long __cpu_flush; /* used by head.S */
const char *arch_name;
const char *elf_name;
unsigned int elf_hwcap;
const char *cpu_name;
struct processor *proc;
struct cpu_tlb_fns *tlb;
struct cpu_user_fns *user;
struct cpu_cache_fns *cache;
};
2.3 从arch/arm/kernel/vmlinux.lds.S可以看出
.init : { /* Init code and data */
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
}
同时 arch/arm/mm/proc-arm920.S中
.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
三、__lookup_machine_type:
3.1 在文件arch/arm/kernel/head_common.S中
182 .long __proc_info_begin
183 .long __proc_info_end
184 3: .long .
185 .long __arch_info_begin
186 .long __arch_info_end
199 .type __lookup_machine_type, %function
200 __lookup_machine_type:
201 adr r3, 3b // r3=r3-r7=0x30008000;小三还是可以重复利用的
202 ldmia r3, {r4, r5, r6} //
r4=3f, r5=arch_info_begin; r6=arch_info_end; arch_info紧跟 proc_info
203 sub r3, r3, r4 @ get offset between virt&phys //
r3=r3-r4=0x30008000
204 add r5, r5, r3 @ convert virt addresses to //
r5=r5+r3
205 add r6, r6, r3 @ physical address space //
r6=r6+r3
206 1: ldr r3, [r5, #MACHINFO_TYPE] @
get machine type // r3=struct machine_desc->nr,因machine_nr并没有存在结构体的首位,所以需要加上偏移
207 teq r3, r1 @ matches loader number? // r3是较内核中的machine_nr, r1是uboot传入的machine_nr,比较两者是否相同
208 beq 2f @ found
209 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc //
不相同,则查找下一个machine_desc
210 cmp r5, r6
211 blo 1b
212 mov r5, #0 @ unknown machine
213 2: mov pc, lr
3.2 从arch/arm/kernel/vmlinux.lds.S可以看出
.init : { /* Init code and data */
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
arch_info紧跟 proc_info
3.2.1 在arch/arm/mach/arch.h中
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head.S, head-common.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
#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 \
}
3.4 在arch/arm/mach-s3c440/mach-smdk2440.c中
对结构体machine_desc进行初始化
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
四、检查uboot的参数
4.1 在文件arch/arm/kernel/head_common.S中
238 .type __vet_atags, %function
239 __vet_atags:
240 tst r2, #0x3 @ aligned?
241 bne 1f
242
243 ldr r5, [r2, #0] @
is first tag ATAG_CORE?
244 subs r5, r5, #ATAG_CORE_SIZE
245 bne 1f
246 ldr r5, [r2, #4]
247 ldr r6, =ATAG_CORE
248 cmp r5, r6
249 bne 1f
250
251 mov pc, lr @ atag pointer is ok
252
253 1: mov r2, #0
254 mov pc, lr
这儿的r2=0x30008000,内核就拷贝到了这个地址,并不是参数链表,所以这个地方的检查 bne 1f, 然后r2=0;
附:
1.协处理寄存器->arm寄存器
mrc {} p15, 0, , , {,}
a. 条件码,忽略时无条件执行
b. 永远为 0
c. , 保存结果
d. , 协处理器中的寄存器
d. , 协寄存器的附加信息,当不需要附加信息时,要设为c0
d. , 附加信息,省略时默为0
2: 指令
2.1 movs指令
S决定指令操作是否影响CPSR的值
2.2 ldmda指令:
使用多数据传送指令(LDM 和 STM)来装载和存储多个字的数据从/到内存。
LDM/STM 的主要用途是把需要保存的寄存器复制到栈上。如我们以前见到过的 STMFD R13!, {R0-R12, R14}。
指令格式是:
xxM{条件}{类型} Rn{!}, <寄存器列表>{^}
‘xx’是 LD 表示装载,或 ST 表示存储。
再加 4 种‘类型’就变成了 8 个指令:
栈 其他
LDMED (空递减) LDMIB 预先增加装载
LDMFD (满递减) LDMIA 过后增加装载
LDMEA (空递增) LDMDB 预先减少装载
LDMFA ?(满递增) LDMDA 过后减少装载
STMFA ?(满递增) STMIB 预先增加存储
STMEA (空递增) STMIA 过后增加存储
STMFD (满递减) STMDB 预先减少存储
STMED ?(空递减) STMDA 过后减少存储
关于空递减和满递减等等,理解:
指针存储往减少方向发展的是“递减”,反之,存储往增加方向发展的是“递增”。
指针指向下一个存储位置的是“空”,反之,指针指向最后一个存储位置的是“满”。
[参考文章]
1. arm linux kernel 从入口到start_kernel的代码分析
http://bbs.chinaunix.net/thread-2039668-1-1.html
2. Linux 内核启动分析之"arch/arm/kernel/head.S"
http://blog.sina.com.cn/s/blog_63ac1cef0100vbcb.html
3. linux内核启运过程分析
http://chxxxyg.blog.163.com/blog/static/150281193201072603030285/
4. arm体系结构与编程-杜春雷
相关文章推荐
- Linux内核---12.S3C6410平台中SD启动详细分析
- Linux内核---11.关于结构体对齐
- linux下管道问题
- 聊聊Linux 五种IO模型
- Linux内核---10.obj-y中将.c编译为.o流程
- Linux内核---9.prepare0流程
- Linux内核---8.filechk函数分析
- 探索linux管道的容量
- Linux内核---7.Makefile学习笔记
- Linux内核---6.make menuconfig 流程分析
- Linux内核---5.Makefile显示打印信息
- Linux内核---4.产生内核head.s反汇编文件
- Linux内核---3.linux 内核zImage生成过程
- Linux内核---2.TQ2440系统移植出现的问题总结
- Linux内核---1.TQ2440使用总结
- Linux netstat命令
- Linux(Ubuntu)下MySQL的安装与配置
- Linux 下五个顶级的开源命令行 Shell
- 交叉编译环境
- linux 学习 设置固定网Ip