linux内核移植s3c2410,移植正式开始1
在前面的准备工作完成之后,先实验一下,谈不上真正的移植 ,因为代码都没有改的。
首先修改顶层的Makefile,修改ARCH,CROSS_COMPLIE变量。
#ARCH ?= $(SUBARCH)
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
执行make smdk2410_defconfig
make
然后执行make uImage,注意需要含有mkimage工具,这个工具是在编译uboot时产生的,同时需要将mkimage拷贝到path
环境变量中。
最终在arch/arm/boot/目录下生成uImage文件,下载该文件到开发板中,既可以观察结果,当然此时是没有文件系统的支持,
内核肯定是启动不起来的。
为了开始内核代码的移植,首先必须得了解内核的启动过程。
内核最先执行的代码是在arch/arm/kernel/head.S文件,该文件只需要关注下面的一段代码:
.section ".text.head", "ax"
.type stext, %function
ENTRY(stext)
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
bl __lookup_machine_type @ r5=machinfo
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error 'a'
bl __create_page_tables
上面的代码首先设置特权模式,然后关闭中断,读取cpu的id,然后调用函数lookup_processor_typ来产看内核是否支持当前的
cpu,接着调用函数lookup_machine_type来查看内核是否支持当前的开发板,如果上面的两个函数调用中有一个函数返回0,
那么内核无法启动。
函数__lookup_processor_type和函数 __lookup_machine_type都是在文件arch/arm/kernel/head-common.S中定义:
__lookup_processor_type:
/*
* 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)
*/
.type __lookup_processor_type, %function
__lookup_processor_type:
adr r3, 3f
ldmda r3, {r5 - r7}
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
这个函数在kernel支持的processor list处理器链表中查找当前的处理器类型,如果找不到的话,函数返回0。上面的函数使用
了结构体proc_info_list,该结构题定义在include/asm-arm/procinfo.c文件中,定义如下:
/*
* Note! struct processor is always defined if we're
* using MULTI_CPU, otherwise this entry is unused,
* but still exists.
* 如果是使用的MULTI_CPU,才使用这个结构体,否则是不使用这个结构体的
* NOTE! The following structure is defined by assembly
* language, NOT C code. For more information, check:
* arch/arm/mm/proc-*.S and arch/arm/kernel/head.S
*/
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;
};
下面就产生一个问题,上面的链表是什么时候产生的?答案是:在内核的镜象中,定义了若该的该结构体,查看arch/arm/mm/
proc-arm920.S:
__arm920_proc_info:
.long 0x41009200
.long 0xff00fff0
不同的proc_info_list被用来支持不同的cpu,最后在arch/arm/kernel/vmlinux.lds连接脚本中:
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
不同的proc_info_list结构被用来支持不同的cpu,他们是定义在proc.info.init中,最终自己俄些结构体被组织起来,开始地址是
__proc_info_begin,结束地址是 __proc_info_end。
在看函数 __lookup_machine_type,函数定义:
/*
* 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
*/
.type __lookup_machine_type, %function
__lookup_machine_type:
adr r3, 3b
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
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
上面的函数使用到了结构体machine_desc,那么该结构定义在那里呢?
被和中对于每种支持的开发板都会使用宏来定义一个上面的结构machine_desc,在其中定义了有一些于开发板相关的属性和
函数。在arch/arm/mach-s3c2410/mach-smdk2410.c中:
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
* to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk2410_init,
.timer = &s3c24xx_timer,
MACHINE_END
上面的MACHINE_START和MACHINE_END是两个宏定义在include/asm-arm/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/kernel/vmlinux.lds:
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
通过上面的连接文件最终将上面的信息连接到vmlinux中。
内核太庞大了,好像偏离了内核启动很长时间了,下面继续开始启动内核:
如果上面的两个函数都能够成功的启动的话,那么接下来内核开始建立页表:
bl __create_page_tables
MMU有效:
adr lr, __enable_mmu @ return (PIC) address
最终调用函数start_kernel经如kernel启动的跌入个阶段。
作者:许强1. 本博客中的文章均是个人在学习和项目开发中总结。其中难免存在不足之处 ,欢迎留言指正。 2. 本文版权归作者和博客园共有,转载时,请保留本文链接。