分析 ARM LINUX 启动 (二)

来源:互联网 发布:微领袖学堂源码 编辑:程序博客网 时间:2024/05/01 15:47
start_kernel->setup_arch->setup_processor


通过函数 lookup_processor_type 读取 processor 结构体的指针,这个函数在汇编文件 head-common.S 中实现。


那系统具体注册了什么样的结构,则在 /arch/arm/mm/proc-*.S 文件中描述。例如 2440 是 920T ,则查看 proc-arm920t.S


.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


具体域的定义查看 struct proc_info_list;


不过其实这里是架构的问题,一般都不需要修改,内核黑客已经处理好了。






接着重点分析machine系列函数,这里的machine代表的就是开发板
mdesc = setup_machine(machine_arch_type);
将开发板编号(也就是启动的时候 R1 数值)传入,查找,然后返回指针




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


说白了就是一个结构体,加入 .arch.info.init 段


#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 \
};
MACH_TYPE_SMDK2410_type
SMDK2410


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


查找只是得到指向该结构体的指针,并没有任何调用。


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);
判断启动参数是否传递了启动命令, 有的话优先使用,没有的话可以从机器的结构中读取


接着分析命令行,可以不看了,主要是看 mdesc 相关的所有函数,因为这些都是与开发板有关系的。


paging_init(mdesc); ->
devicemaps_init(mdesc);
初始化page,可以先不看了,主要还是板子相关的


/*
* Ask the machine support to map in the statically mapped devices.
*/
if (mdesc->map_io)
mdesc->map_io();


其实调用的是 smdk2410_map_io ,就这样,第一个开发板调用的相关函数被调用了。


struct map_desc {
unsigned long virtual;
unsigned long pfn;
unsigned long length;
unsigned int type;
};


首先调用
s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc));
static struct map_desc smdk2410_iodesc[] __initdata = {
  /* nothing here yet */
};


map_io 的作用是将 S3C2440 CPU 的外围建立映射,这样linux的虚拟地址才能访问到
s3c24xx_init_io 是 24xx 系列通用的函数,已经默认将 CPU 的一些必要设备加入映射,用户
可以根据需要添加其他设备。下面看这个函数
默认提供的映射
static struct map_desc s3c_iodesc[] __initdata = {
IODESC_ENT(GPIO),
IODESC_ENT(IRQ),
IODESC_ENT(MEMCTRL),
IODESC_ENT(UART)
};
#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, __phys_to_pfn(S3C24XX_PA_##x), S3C24XX_SZ_##x, MT_DEVICE }
例如 GPIO 替换后得到 
{ (unsigned long)S3C24XX_VA_GPIO, __phys_to_pfn(S3C24XX_PA_GPIO), S3C24XX_SZ_GPIO, MT_DEVICE }
这里建立的是静态映射表,具体用法目前不清楚,先百度出结果以后再研究


接着进行 具体cpu的初始化
s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));


static struct cpu_table cpu_ids[] __initdata = {
{
.idcode = 0x32410000,
.idmask = 0xffffffff,
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
.init = s3c2410_init,
.name = name_s3c2410
},
idcode 是根据CP15读出的架构来读出来的,所以用户不需要设置也会正确识别,例如对于 2410 的cpu
cpu->map_io(); 也就是 s3c2410_map_io
static struct map_desc s3c2410_iodesc[] __initdata = {
IODESC_ENT(CLKPWR),
IODESC_ENT(TIMER),
IODESC_ENT(WATCHDOG),
};
这个函数继续做映射,可以看到增加了以下的映射。








回到 smdk2410_map_io 函数继续执行
s3c24xx_init_clocks 初始化时钟,参数0 表示使用默认的 12MHZ
(cpu->init_clocks)(xtal); 实际调用的是 s3c2410_init_clocks
注册时钟,然后设置时钟
s3c24xx_register_baseclocks(xtal);
s3c2410_setup_clocks();
s3c2410_baseclk_add();
公共代码,应该是不需要修改的了,迟点再分析了。


接着调用的是 s3c24xx_init_uarts,实际是 s3c2410_init_uarts
将3个串口的配置传进去设置初始化,根据其他复制代码就行了,
static struct s3c2410_uartcfg smdk2410_uartcfgs[] = {
[0] = {
.hwport     = 0,
.flags     = 0,
.ucon     = UCON,
.ulcon     = ULCON,
.ufcon     = UFCON,
},
[1] = {
....
....
主要是注册 platform_device 设备,填充 s3c24xx_uart_devs
这个设备是必须的,因为串口调试信息的输出就靠它了。
其他设备可以在之后添加。


到这里为止,整个 smdk2410_map_io 就结束了。










回到 setup_arch ,继续执行
request_standard_resources(&meminfo, mdesc);
主要是申请 video RAM 和 lp 打印机的资源,这里可以暂时不看。




init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;


到这里,系统就和开发板的是实际函数联系起来了。
setup_arch 也结束了






回到 start_kernel 
init_IRQ --> init_arch_irq 实际就是调用 s3c24xx_init_irq
看名字就是24xx系列的公共函数


system_timer 本来就是系统使用的全局变量。


最后就剩下 init_machine 的初始化了


static void (*init_machine)(void) __initdata;


static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (init_machine)
init_machine();
return 0;
}
arch_initcall(customize_machine);


在 arch 初始化的时候就会被执行的。




smdk2410_init 进行的动作差不多


接着将这些注册为设备
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
};


最后再 smdk_machine_init 中
static struct platform_device __initdata *smdk_devs[] = {
&s3c_device_nand,
&smdk_led4,
&smdk_led5,
&smdk_led6,
&smdk_led7,
};
将这些也注册为设备,需要注意的是 NAND 设备,他将被 MTD驱动所使用。


struct platform_device s3c_device_nand = {
.name  = "s3c2410-nand",
.id  = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource  = s3c_nand_resource,
};


s3c_device_nand.dev.platform_data 是驱动自定义的结构,device 核心并不管理相关内容
主要为了传递参数


以后再分析 driver 和 device 的关系,和怎么实现访问的。
原创粉丝点击