Arm linux 内核移植及系统初始化过程分析
发布时间:2024-11-21
发布时间:2024-11-21
第四届云计算大会门票抢购:史上最低价,每日限5张! 【分享季1】:网友推荐130个经典资源,分享再赠分!
Arm linux 内核移植及系统初始化过程分析
分类: Linux2011-03-25 14:48282人阅读评论(0)收藏举报
================================================================ 浅谈分析Arm linux 内核移植及系统初始化的过程(一)
================================================================
学习嵌入式ARM linux,主要想必三个方向发展:
1、嵌入式linux应用软件开发
2、linux内核的剪裁和移植
3、嵌入式linux底层驱动的开发
本文就Arm linux 内核移植及系统初始化过程进行分析:咨询QQ:313807838
主要介绍内核移植过程中涉及文件的分布及其用途,以及简单介绍系统的初始化过程。整个arm linux内核的启动可分为三个阶段:第一阶段主要是进行cpu和体系结构的检查、cpu本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基础设施进行初始化;最后则是更高层次的初始化,如根设备和外部设备的初始化。了解系统的初始化过程,有益于更好地移植内核。
1. 内核移植
2. 涉及文件分布介绍
2.1. 内核移植
2.2. 涉及的头文件
/linux-2.6.18.8/include
[root@localhost include]# tree -L 1
.
|-- Kbuild
|-- acpi
|-- asm -> asm-arm
|-- asm-arm ------------------------------->(1)
|-- asm-sparc
|-- asm-sparc64
|-- config
|-- keys
|-- linux ------------------------------->(2)
|-- math-emu
|-- media
|-- mtd
|-- net
|-- pcmcia
|-- rdma
|-- rxrpc
|-- scsi
|-- sound
`-- video
内核移植过程中涉及到的头文件包括处理器相关的头文件(1)和处理器无关的头文件(2)。
2.3. 内核移植2.4. 涉及的源文件
/linux-2.6.18.8/arch/arm
[root@localhost arm]# tree -L 1
.
|-- Kconfig
|-- Kconfig-nommu
|-- Kconfig.debug
|-- Makefile
|-- boot ------------------------------->(2)
|-- common
|-- configs
|-- kernel ------------------------------->(3)
|-- lib
|-- mach-at91rm9200
……
|-- mach-omap2
|-- mach-realview
|-- mach-rpc
|-- mach-s3c2410 ------------------------------->(4)
|-- mach-sa1100
|-- mach-versatile
|-- mm ------------------------------->(5)
|-- nwfpe
|-- oprofile
|-- plat-omap
|-- tools ------------------------------->(1)
`-- vfp
(1)
/linux-2.6.18.8/arch/arm/tools
[root@localhost tools]# tree -L 1
.
|-- Makefile
|-- gen-mach-types
`-- mach-types
Mach-types 文件定义了不同系统平台的系统平台号。移植linux内核到新的平台上需要对新的平台登记系统平台号。
Mach-types文件格式如下:
# machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number
s3c2410 ARCH_S3C2410 S3C2410 182
smdk2410 ARCH_SMDK2410 SMDK2410 193
之所以需要这些信息,是因为脚本文件linux/arch/arm/tools/gen-mach-types需要
linux/arch/tools/mach-types来产生linux/include/asm-arm/mach-types.h文件,该文件中设置了一些宏定义,需要这些宏定义来为目标系统选择合适的代码。
(2)
linux-2.6.18.8/arch/arm/boot/compressed
[root@localhost compressed]# tree -L 1
.
|-- Makefile
|-- Makefile.debug
|-- big-endian.S
|-- head-at91rm9200.S
2 浅谈分析Arm linux 内核移植及系统初始化的过程
|-- head.S
|-- ll_char_wr.S
|-- misc.c
|-- ofw-shark.c
|-- piggy.S
`-- vmlinux.lds.in
Head.s 是内核映像的入口代码,是自引导程序。自引导程序包含一些初始化程序,这些程序都是体系结构相关的。在对系统作完初始化设置工作后,调用misc.c文件中的
decompress_kernel()函数解压缩内核映像到指定的位置,然后跳转到kernel的入口地址。
Vmlinux.lds.in用来生成内核映像的内存配置文件。
(3)
linux-2.6.18.8/arch/arm/kernel
[root@localhost kernel]# tree -L 1
.
|-- Makefile
|-- apm.c
|-- armksyms.c
|-- arthur.c
|-- asm-offsets.c
|-- bios32.c
|-- calls.S
|-- dma.c
|-- ecard.c
|-- entry-armv.S
|-- entry-common.S
|-- entry-header.S
|-- fiq.c
|-- head-common.S
|-- head-nommu.S
|-- head.S
|-- init_task.c
|-- io.c
|-- irq.c
|-- isa.c
|-- module.c
|-- process.c
|-- ptrace.c
|-- ptrace.h
|-- semaphore.c
|-- setup.c
|-- smp.c
|-- sys_arm.c
|-- time.c
|-- traps.c
`-- vmlinux.lds.S
内核入口处也是由一段汇编语言实现的,由head.s和head-common.s两个文件组成。 Head.s 是内核的入口文件, 在head.s的末尾处 #i nclude "head-common.S"。 经过一系列的初始化后,跳转到linux-2.6.18.8/init/main.c中的start_kernel()函数中,开始内核的基本初始化过程。
/linux-2.6.18.8/init
[root@localhost init]# tree
.
|-- Kconfig
|-- Makefile
|-- calibrate.c
|-- do_mounts.c
|-- do_mounts_initrd.c
|-- do_mounts_md.c
|-- do_mounts_rd.c
|-- initramfs.c
|-- main.c
`-- version.c
(4)
/linux-2.6.18.8/arch/arm/mach-s3c2410
[root@localhost mach-s3c2410]# tree -L 1
.
|-- Kconfig
|-- Makefile
|-- Makefile.boot
|-- bast-irq.c
|-- bast.h
|-- clock.c
|-- clock.h
|-- common-smdk.c
|-- common-smdk.h
|-- cpu.c
|-- cpu.h
|-- devs.c
|-- devs.h
|-- dma.c
|-- gpio.c
|-- irq.c
|-- irq.h
|-- mach-anubis.c
|-- mach-smdk2410.c
|-- pm-simtec.c
|-- pm.c
|-- pm.h
|-- s3c2400-gpio.c
|-- s3c2410-clock.c
|-- s3c2410-gpio.c
|-- s3c2410.c
|-- s3c2410.h
|-- sleep.S
|-- time.c
|-- usb-simtec.c
`-- usb-simtec.h
这个目录中的文件都是板级相关的,其中比较重要是如下几个:
linux/arch/arm/mach-s3c2410/cpu.c
linux/arch/arm/mach-s3c2410/common-smdk.c
linux/arch/arm/mach-s3c2410/devs.c
linux/arch/arm/mach-s3c2410/mach-smdk2410.c
linux/arch/arm/mach-s3c2410/Makefile.boot
linux/arch/arm/mach-s3c2410/s3c2410.c
3. 处理器和设备4.
这里主要介绍处理器和设备的描述和操作过程。设备描述在
linux/arch/arm/mach-s3c2410/devs.c和linux/arch/arm/mach-s3c2410/common-smdk.c中实现。最后以nand flash为例具体介绍。
================================================================ 浅谈分析Arm linux 内核移植及系统初始化的过程(二)
================================================================ 3 浅谈分析Arm linux 内核移植及系统初始化的过程。咨询QQ:313807838
4.1. 处理器、设备4.2. 描述
设备描述主要两个结构体完成:struct resource和struct platform_device。
先来看看着两个结构体的定义:
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
Resource结构体主要是描述了设备在系统中的起止地址、名称、标志以及为了链式描述方便指向本结构体类型的指针。Resource定义的实例将被添加到platform_device结构体对象中去。
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
Platform_device结构体包括结构体的名称、ID号、平台相关的信息、设备的数目以及上面定义的resource信息。Platform_device结构对象将被直接通过设备操作函数注册导系统中去。具体注册和注销过程在下一节介绍。
4.3. 处理器、设备4.4. 操作
(1) int platform_device_register(struct platform_device * pdev); 注册设备
(2) void platform_device_unregister(struct platform_device * pdev); 注销设备
(3) int platform_add_devices(struct platform_device **devs, int num);添加设备,通过调用上面两个函数实现。
4.5. 添加Nand flash设备4.6.
下面以nand flash 设备的描述为例,具体介绍下设备的描述和注册过程。
// resource结构体实例s3c_nand_resource 对nand flash 控制器描述,包括控制器的起止地址和标志。
static struct resource s3c_nand_resource[] = {
[0] = {
.start = S3C2410_PA_NAND,
.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
.flags = IORESOURCE_MEM,
}
};
//platform_device结构体实例s3c_device_nand定义了设备的名称、ID号并把resource对象作为其成员之一。
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
// nand flash 的分区情况,由mtd_partition结构体定义。
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "Boot Agent",
.size = SZ_16K,
.offset = 0,
},
[1] = {
.name = "S3C2410 flash partition 1",
.offset = 0,
.size = SZ_2M,
},
[2] = {
.name = "S3C2410 flash partition 2",
.offset = SZ_4M,
.size = SZ_4M,
},
[3] = {
.name = "S3C2410 flash partition 3",
.offset = SZ_8M,
.size = SZ_2M,
},
[4] = {
.name = "S3C2410 flash partition 4",
================================================================
浅谈分析Arm linux 内核移植及系统初始化的过程(三)
================================================================
4、浅谈分析Arm linux 内核移植及系统初始化的过程 QQ:313807838
.offset = SZ_1M * 10,
.size = SZ_4M,
},
[5] = {
.name = "S3C2410 flash partition 5",
.offset = SZ_1M * 14,
.size = SZ_1M * 10,
},
[6] = {
.name = "S3C2410 flash partition 6",
.offset = SZ_1M * 24,
.size = SZ_1M * 24,
},
[7] = {
.name = "S3C2410 flash partition 7",
.offset = SZ_1M * 48,
.size = SZ_16M,
}
};
static struct s3c2410_nand_set smdk_nand_sets[] = {
[0] = {
.name = "NAND",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
.partitions = smdk_default_nand_part,
},
};
/* choose a set of timings which should suit most 512Mbit
* chips and beyond.
*/
static struct s3c2410_platform_nand smdk_nand_info = {
.tacls = 20,
.twrph0 = 60,
.twrph1 = 20,
.nr_sets = ARRAY_SIZE(smdk_nand_sets),
.sets = smdk_nand_sets,
};
/* devices we initialise */
// 最后将nand flash 设备加入到系统即将注册的设备集合中。
static struct platform_device __initdata *smdk_devs[] = {
&s3c_device_nand,
&smdk_led4,
&smdk_led5,
&smdk_led6,
&smdk_led7,
};
然后通过smdk_machine_init()函数,调用设备添加函数platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs)) 完成设备的注册。具体过程参见系统初始化的相关部分。
5. 系统初始化
5.1. 系统初始化的主干线
Start_kernel() èsetup_arch() èreset_init() è kernel_thread(init …) è init() è
do_basic_setup() èdriver_init() è do_initcall()
Start_kernel()函数负责初始化内核各个子系统,最后调用reset_init(),启动一个叫做init的内核线程,继续初始化。Start_kernel()函数在init/main.c中实现。
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
smp_setup_processor_id();
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
local_irq_disable();
early_boot_irqs_off();
early_init_irq_lock_class();
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
boot_cpu_init();
page_address_init();
printk(KERN_NOTICE);
printk(linux_banner);
setup_arch(&command_line);
//setup processor and machine and destinate some pointers for do_initcalls() s
5、浅谈分析Arm linux 内核移植及系统初始化的过程 咨询QQ:313807838
// for example init_machine pointer is initialized with smdk_machine_init() , and
//init_machine() is called by customize_machine(), and the is processed by
//arch_initcall(fn). Therefore smdk_machine_init() is issured. by edwin
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
/*
* 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 ing scheduler.
*/
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
build_all_zonelists();
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s/n", saved_command_line);
parse_early_param();
parse_args("Booting kernel", command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
sort_main_extable();
unwind_init();
trap_init();
rcu_init();
init_IRQ();
pidhash_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
profile_init();
if (!irqs_disabled())
printk("start_kernel(): bug: interrupts were enabled early/n");
early_boot_irqs_on();
local_irq_enable();
/*
* 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 &&
initrd_start < min_low_pfn << PAGE_SHIFT) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
6、浅谈分析Arm linux 内核移植及系统初始化的过程 咨询QQ:313807838
"disabling it./n",initrd_start,min_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
#endif
vfs_caches_init_early();
cpuset_init_early();
mem_init();
kmem_cache_init();
setup_per_cpu_pageset();
numa_policy_init();
if (late_time_init)
late_time_init();
calibrate_delay();
pidmap_init();
pgtable_cache_init();
prio_tree_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
fork_init(num_physpages);
proc_caches_init();
buffer_init();
unnamed_dev_init();
key_init();
security_init();
vfs_caches_init(num_physpages);
radix_tree_init();
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cpuset_init();
taskstats_init_early();
delayacct_init();
check_bugs();
acpi_early_init(); /* before LAPIC and SMP init */
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
分析start_kernel()源码, 其中setup_arch() 和 reset_init()是两个比较关键的函数。下面将具体分析这两个函数。
5.2. setup_arch()函数分析
首先我们来分析下setup_arch()函数。
Setup_arch()函数主要工作是安装cpu和machine,并为start_kernel()后面的初始化函数指针指定值。
其中setup_processor()函数调用linux/arch/arm/kernel/head_common.S 中的
lookup_processor_type函数查询处理器的型号并安装。
Setup_machine()函数调用inux/arch/arm/kernel/head_common.S 中的
lookup_machine_type(__machine_arch_type)函数根据体系结构号__machine_arch_type,在__arch_info_begin和__arch_info_end段空间查询体系结构。问题是
__machine_arch_type是在什么时候赋的初值?__arch_info_begin和__arch_info_end段空间到底放的是什么内容?
__machine_arch_type是一个全局变量,在linux/boot/decompress/misc.c的解压缩函数中得以赋值。
decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p, int arch_id)
{
__machine_arch_type = arch_id;
}
__arch_info_begin和__arch_info_end段空间到底放的内容由链接器决定,存放
是http://.init段的内容。这个段是通过段属性__attribute__指定的。Grep一
下http://.init 得到./include/asm/mach/arch.h:53:
__attribute__((__section__("http://.init"))) = { / 在linux/include/asm-arm/mach/arch.h