Linux 内存管理系统:初始化 - 嵌入式
来源:百度文库 编辑:神马文学网 时间:2024/04/28 00:29:40
Linux 内存管理系统:初始化
0推荐Linux 内存管理系统:初始化
作者:Joe Knapka
臭翻:colyli
内存管理系统的初始化处理流程分为三个基本阶段:
激活页内存管理
在swapper_pg_dir中初始化内核的页表
初始化一系列和内存管理相关的内核数据
Turning On Paging (i386)
启动分页机制(i386)
Kernel 代码被加载到物理地址0x100000(1MB),在分页机制打开后被重新映射到
PAGE_OFFSET + 0x100000的位置(PAGE_OFFSET在IA32上为3GB,即进程虚拟地址中用户
空间与内核空间的分界处)。这是通过将物理地址映射到编译进来的页表 (在
arch/i386/kernel/head.S中)的0-8MB以及PAGE_OFFSET-PAGE_OFFSET+8MB实现的。然后
我们跳转到init/main.c中的start_kernel,这个函数被定位到PAGE_OFFSET+某一个地址。
这看起来有些狡猾。要注意到在head.S中启动分页机制的代码是通过让它自己所执行的地
址空间不再有效的方式来实现这一点的;因此0-4MB被映射(不明白:hence the 0-4MB
identity mapping.)。在分页机制没有启动之前,start_kernel是不会被调用的,我们假
定他运行在PAGE_OFFSET+某一个地方的位置。因此head.S中的页表必须同样映射内核代码
所使用的地址,这样后继才能跳转到staert_kernel处;因此PAGE_OFFSET被映射(不明
白:hence the PAGE_OFFSET mapping.)。
下面在head.S中分页机制启动时的一些神奇的代码:
/*
* Enable paging
*/
3:
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
jmp 1f /* flush the prefetch-queue */
1:
movl $1f,%eax
jmp *%eax /* make sure eip is relocated */
1:
在两个1的label之间的代码将第二个label 1的地址加载到EAX中,然后跳转到那里。这
时,指令指针寄存器EIP指向1MB+某个数值的物理地址。而label都在内核的虚拟地址空
间(PAGE_OFFSET+某个位置),所以这段代码将EIP有效的从物理地址空间重新定位到了虚
拟地址空间。
Start_kernel函数初始化了所有的内核数据,然后启动了init内核线程。Start_kernel中
最初的几件事情之一就是调用setup_arch函数,这是一个和具体的体系结构相关的设置函
数, 调用了更底层的初始化细节。对于x86 平台而言, 这些函数在
arch/i386/kernel/setup.c中。
在setup_arch 中和内存相关的第一件事就是计算低端内存(low-memory) 和高端内存
(high-memory)的有效页的数目;每种内存类型(each memory type)最高端的页的数目分别
保存在全局变量highstart_pfn和highend_pfn中。高端内存并不是直接映射到内核的虚拟
内存(VM)中;这是后面要讨论的。
接下来,setup_arch 调用init_bootmem 函数以初始化启动时的内存分配器(boot-time
memory allocator)。Bootmem内存分配器仅仅在系统boot的过程中使用,为永久的内核数
据分配页。因此我们不会对它涉及太多。需要记住的就是bootmem 分配器(bootmem
allocator)在内核初始化时提供页,这些页为内核专门预留,就好像他们是从内核景象文
件中载入的一样,他们在系统启动以后不参与任何的内存管理活动。
初始化内核页表
之后,setup_arch调用在arch/i386/mm/init.c中的paging_init函数。这个函数做了一些
事情。首先它调用pagetable_init函数去映射整个的物理内存,或者在PAGE_OFFSET到4GB
之间的尽可能多的物理内存,这是从PAGE_OFFSET处开始。
在pagetable_init函数中,我们在swapper_pg_dir中精确的建立了内核的页表,映射到
截至PAGE_OFFSET的整个物理内存。
这是一个将正确的数值填充到页目录和页表中去的简单的算术活。映射建立在
swapper_pg_dir中,即kernel页目录;这也是初始化页机制时所使用的页目录。(当使用
4MB的页表的时候,直到下一个4MB边界的虚拟地址才会被映射在这里,但这没有什么,
因为我们不会使用这个内存所以没有什么问题)。如果有这里有剩下物理内存没有被映射,
那就是大于4GB-PAGE_OFFSET范围的内存,这些内存只有CONFIG_HIGHMEM选项被设置后
才能使用(即使用大于4GB的内存)。
在接近pagetable_init函数的尾部,我们调用了fixrange_init为编译时固定的虚拟内存
映射预留页表。这些表将硬编码到Kernel中的虚拟地址进行映射,但是他们并不是已经加
载的内核数据的一部分。Fixmap表在运行时调用set_fixmap函数被映射到物理内存。
在初始化了fixmap之后,如果CONFIG_HIGHMEM被设置了,我们还要分配一些页表给kmap
分配器。Kmap允许kernel将物理地址的任何页映射到kernel的虚拟地址空间,以临时使用。
这很有用,例如对在pagetable_init中不能直接映射的物理内存进行映射。
Fixmap 和kmap 页表们占据了kernel 虚拟空间顶部的一部分——因此这些地址不能在
PAGE_OFFSET映射中被永久的映射到物理页上。由于这个原因,Kernel虚拟内存的顶部的
128MB就被预留了(vmalloc分配器仍然是用这个范围内的地址)。(下面这句实在是不知
道怎么翻译) Any physical pages that would otherwise be mapped into the
PAGE_OFFSET mapping in the 4GB-128MB range are instead (if CONFIG_HIGHMEM is
specified) included in the high memory zone, accessible to the kernel only via
kmap()。如果没有设置CONFIG_HIGMEM,这些页就完全是不可用的。这仅针对配置有大量内
存的机器(900多MB或者更多)。例如,如果PAGE_OFFSET=3GB,并且机器有2GB的RAM,
那么只有开始的1GB-128MB的物理内存可以被映射到PAGE_OFFSET和fixmap/kmap地址范
围之间。剩余的页是不可用的——实际上对于用户进程映射来说,他们是可以直接映射的页
——但是内核不能够直接访问它们。
回到paging_init,我们可以通过调用kmap_init函数来初始化kmap系统,kmap_init简
单的缓存了最先的kmap_pagetable[在TLB?]。然后,我们通过计算zone的大小并调用
free_area_init 去建立mem_map 和初始化freelist,初始化了zone 分配器。所有的
freelist被初始化为空,并且所有的页都被标志为reserved(不可被VM系统访问);这
种情况之后会被纠正。
当paging_init完成后,物理内存的分布如下[注意在2.4的内核中这不全对]:
0x00000000: 0-page
0x00100000: kernel-text
0x????????: kernel_data
0x???????? =_end: whole-mem pagetables
0x????????: fixmap pagetables
0x????????: zone data (mem_map, zone_structs, freelists &c)
0x???????? =start_mem: free pages
这块内存被swapper_pg_dir和whole-mem-pagetables映射以寻址PAGE_OFFSET。
进一步的VM子系统初始化
现在我们回到start_kernel。在paging_init完成后,我们为内核的其他子系统进行一些
额外的配置工作,它们中的一些使用bootmem分配器分配额外的内核内存。从内存管理的观
点来看,这其中最重要的是kmem_cache_init,他初始化了slab分配器的数据。
在kmem_cache_init 调用之后不久,我们调用了mem_init。这个通过清除空闲物理页的
zone数据中的PG_RESERVED位在free_area_init的开始完成了初始化freelist的工作;
为不能被用为DMA的页清除PG_DMA位;然后释放所有可用的页到他们各自的zone中。最后
一步,在bootmem.c 中的free_all_bootmem函数中完成,很有趣。他建立了伙伴位图和
freelist描述了所有存在的没有预留的页,这是通过简单的释放他们并让free_page_ok
做正确的事情。一旦mem_init被调用了,bootmem分配器就不再使用了,所以它的所有的
页也会被释放到zone分配器的世界中。
段
段用来将线性地址空间划分为专用的块。线性空间是被VM子系统管理的。X86体系结构从硬
件上支持段机制;你可以按照段+段内偏移量的方式指定一个地址,这里地址被描述为一定
范围的线性(虚拟地址)并带有特定的属性(如保护属性)。实际上,在x86体系结构中你
必须使用段机制。所以我们要设置4个段:
一个kernel text段:从0 到4GB
一个kernel data段:从0 到4GB
一个user text段:从0 到4GB
一个user data段:从0 到4GB
因此我们可以使用任何一个有效的段选择器(segment selector)访问整个虚拟地址空间。
问题:
段是在哪里被设置的?
答案:
全局描述符表(GDT)定义在head.s的450行。 GDT寄存器在250行被加载。
问题:
为什么将内核段和用户端分离开。是否他们都有权限访问整个4GB的范围?
答案:
这是因为内核和用户段的保护机制有区别:
.quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */
.quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */
.quad 0x00cff2000000ffff /* 0x2B user 4GB data at 0x00000000 */
段寄存器(CS,DS等)包含有一个13位的描述符表的索引,索引指向的描述符告诉CPU所选
择的段的属性。段选择器的低3位没有被用来索引描述符表,而是用来保存描述符类型(全
局或局部)以及需要的特权级。因此内核段选择器0x10和0x18使用特权级0(RPL0),用
户选择器0x23和0x2B使用最特权级RPL 3。
要注意到第三个高序字节的高位组对应内核和用户也是不同的:对内核,描述符特权级
(DPL)为0;对用户DPL为3。如果你阅读了Intel的文档,你将看到确切的含义,但是由
于Linux内核的x86段保护没有涉及太多,所以我就不再讨论太多了。
下面的命令是我自己增加的命令,不使用uImage,直接引导zImage文件。
具体方法是使用tftp命令从网络下载zImage文件到内存中或者直接读取flash数据,拷贝到内存中,假设拷贝到了A地址处,接下来就可以调用:
# bootzimage A
来引导linux内核了,具体分析见下面说明。
Uboot在设置启动命令的时候使用的是Tag方式,也就是内核现在期望使用的参数传递方式。还有一种引导设置方式,就是采用2.2以及以前版本使用的参数设置方式,2.4和2.6内核为了兼容之前版本参数设置,对老版本参数数据进行了解析,转换成了内部tag方式。这样我们完全可以使用老版本的参数传递方式。
Setup.h文件中定义了老版本参数传递结构:
#define COMMAND_LINE_SIZE 1024 // 命令行最多1024字节
/* This is the old deprecated way to pass parameters to the kernel */
struct param_struct {
union {
struct {
unsigned long page_size; // 内存的页面大小
unsigned long nr_pages; // 内存页面数量
unsigned long ramdisk_size; // RAM disk配置, 可以不用
unsigned long flags; /* 12 */
#define FLAG_READONLY 1
#define FLAG_RDLOAD 4
#define FLAG_RDPROMPT 8
unsigned long rootdev; /* 16 */
unsigned long video_num_cols; /* 20 */
unsigned long video_num_rows; /* 24 */
unsigned long video_x; /* 28 */
unsigned long video_y; /* 32 */
unsigned long memc_control_reg; /* 36 */
unsigned char sounddefault; /* 40 */
unsigned char adfsdrives; /* 41 */
unsigned char bytes_per_char_h; /* 42 */
unsigned char bytes_per_char_v; /* 43 */
unsigned long pages_in_bank[4]; /* 44 */
unsigned long pages_in_vram; /* 60 */
unsigned long initrd_start; /* 64 */
unsigned long initrd_size; /* 68 */
unsigned long rd_start; /* 72 */
unsigned long system_rev; /* 76 */
unsigned long system_serial_low; /* 80 */
unsigned long system_serial_high; /* 84 */
unsigned long mem_fclk_21285; /* 88 */
} s;
char unused[256];
} u1;
union {
char paths[8][128];
struct {
unsigned long magic;
char n[1024 - sizeof(unsigned long)];
} s;
} u2;
char commandline[COMMAND_LINE_SIZE]; // 命令行启动参数
};
在整个结构中,最关键的部分是u1.s.page_size,u1.s.nr_pages和commandline域。这三个域也是必须设置的域!
代码如下:
#define LINUX_MACHINE_ID 406 // 见后面说明
#define LINUX_PAGE_SHIFT 12 // 页面大小4K
#define LINUX_PAGE_SIZE (1<
int do_bootzimage(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int i;
u32 addr;
char *cmdline = getenv ("bootargs"); // 获得命令行参数
void (*run)(int zero, int arch); // 定义引导内核的函数原型
// 设置引导参数所处的位置
struct param_struct *params = (struct param_struct *)0xa0000100;
// 设置内核加载地址
if (argc<2)
addr = load_addr; // 默认的加载地址
else
// 调用命令时参数传递了地址, 那么就使用指定的地址
addr = simple_strtoul(argv[1], NULL, 16);
// 接下来将结构的数据清零, 不用的全部清零, 防止出错
for(i=0; i<(sizeof(struct param_struct)>>2); i++)
((u32 *)params)[i] = 0;
// 设置u1.s.page_size和u1.s.nr_pages参数, 这两个参数由内核自动解析.
params->u1.s.page_size = LINUX_PAGE_SIZE;
params->u1.s.nr_pages = (0x04000000 >> LINUX_PAGE_SHIFT);
// 拷贝命令行参数到命令位置
memcpy(params->commandline, cmdline, strlen(cmdline));
run = (void (*)(int, int))addr;
// 执行内核程序, 传递两个参数进去, 第2个参数指定了ARCH值, 必须与
// 内核配置的相同, 否则会出现”Error: a”错误!!
run(0, LINUX_MACHINE_ID);
}
// 最后定义bootzImage命令,实现函数是do_bootzimage。
U_BOOT_CMD(
bootzimage, 2, 1, do_bootzimage,
"bootzimage - boot zImage from ram.n",
" [addr] boot zImage directoly. "
);
这里我们在引导的时候使用了老版本的参数传递方式,下面时Linux内核在进行参数解析时的代码,可以看到,内核将这些参数自动转换成能够识别的类型:
内核(2.6.9)的init/main.c文件中的start_kernel()函数中调用了setup_arch()函数:
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
page_address_init();
printk(linux_banner);
setup_arch(&command_line); // 这个函数中将对传递的参数进行解析
setup_per_cpu_areas();
…
setup_arch()函数位于体系结构相关的代码中,本例中存在于arch/arm/kernel/setup.c文件中。
以下是该函数的部分内容
…
struct tag *tags = (struct tag *)&init_tags;
…
// 判断是不是tag的ATAG_CORE, 老式的参数是不能满足要求的, 如果是老式的
// 参数那么将会执行convert_to_tag_list(tags)将老式参数转换成新的参数类型.
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
convert_to_tag_list()函数实现在arch/arm/kernel/compat.c文件中,该函数随后调用build_tag_list()函数进行参数重组,具体可以参考内核的源代码。
vivi与Linux kernel的参数传递情景分析(下) - Vivi - CalmArrow
文章说明:calmarrow(lqm)原创
文章引自:http://piaoxiang.cublog.cn
下面进入Linux kernel部分,分析与bootloader参数传递对应的部分。 移植Linux需要很大的工作量,其中之一就是HAL层的编写。在具体实现上,HAL层以arch目录的形式存在。显然,该层需要与bootloader有一定的约定,否则就不能很好的支持。其实,这个地方应该思考一个问题,就是说,boot loader可以做到Linux kernel里面,但是这样带来的问题就是可移植性和灵活性都大为降低。而且,bootloader的功能并非操作系统的核心范畴,Linux的核心应该始终关注操作系统的核心功能上,将其性能达到最优。所以,bootloader分离出来单独设计,是有一定的道理的。bootloader现在除了完成基本功能外,慢慢地变得“肥胖”了。在高性能bootloader设计中,可能会把调试内核等的一些功能集成进来,这样在内核移植尚未完成阶段,bootloader可以充当调试器的作用。功能趋于完善,也慢慢趋于复杂。废话不说,进入正题。 三、Linux kernel接受参数分析 这部分主要分析如下问题: ·Linux kernel支持压缩映象和非压缩映象两种方式启动,那么这两种流程和函数入口有何不同? ·如何使用非压缩映象?做一下测试。 ·zImage是如何生成的?其格式如何? ·启动之后,Linux kernel如何接收参数? 这里不具体区分每个问题,按照理解和开发的思路来进行。 1、思考:前面做的基本实验中,并没有采用压缩映象。因为程序规模太小,压缩带来的时间开销反而降低了性能。但是对Linux kernel来说,映象还是比较大的,往往采用了压缩。但是,同样有需求希望Linux kernel小一些,不采用压缩方式来提高内核启动的速度,对时间要求比较苛刻。那么,这样就出现了两种情况:压缩映象和非压缩映象。由此带来的问题就在于:如果是压缩映象,那么必须首先解压缩,然后跳转到解压缩之后的代码处执行;如果是非压缩映象,那么直接执行。Linux必须对这两种机制提供支持,这里就需要从整体上来看一下生成的映象类型了。 因为vivi的Makefile都是直接来源于Linux,前面对vivi的Makefile已经分析清楚了,这里看Linux的Makefile就容易多了,大同小异,而且还有丰富的文档支持。 (1)非压缩映象 $make vmlinux[armlinux@lqm linux-2.4.18]$ ls -l vmlinux
-rwxrwxr-x 1 armlinux armlinux 1799697 Sep 11 14:06 vmlinux
[armlinux@lqm linux-2.4.18]$ file vmlinux
vmlinux: ELF 32-bit LSB executable, ARM, version 1 (ARM), statically linked, not stripped
vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
--start-group \
$(CORE_FILES) \
$(DRIVERS) \
$(NETWORKS) \
$(LIBS) \
--end-group \
-o vmlinux
$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
$(OBJCOPY) -O binary -R .comment -R .stab -R .stabstr -S vmlinux vmlinux.bin
NOW, Booting Linux......
VIVI has completed the mission of
From now on, Linux kernel takes charge of all
Linux version 2.4.18-rmk7-pxa1 (armlinux@lqm) (gcc version 2.95.3 20010315 (release)) #2 Tue Sep 11 14:06:14 CST 2007
bzImage zImage zinstall Image bootpImage install: vmlinux
@$(MAKEBOOT) $@
SYSTEM =$(TOPDIR)/vmlinux
Image: $(CONFIGURE) $(SYSTEM)
$(OBJCOPY) -O binary -R .note -R .comment -S $(SYSTEM) $@
bzImage: zImage
zImage: $(CONFIGURE) compressed/vmlinux
$(OBJCOPY) -O binary -R .note -R .comment -S compressed/vmlinux $@
@echo " ^_^ The kernel image file is:" $(shell /bin/pwd)/$@
vmlinux: $(HEAD) $(OBJS) piggy.o vmlinux.lds
$(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(LIBGCC) -o vmlinux
$(HEAD): $(HEAD:.o=.S) \
$(wildcard $(TOPDIR)/include/config/zboot/rom.h) \
$(wildcard $(TOPDIR)/include/config/cpu/32.h) \
$(wildcard $(TOPDIR)/include/config/cpu/26.h)
$(CC) $(AFLAGS) -traditional -c $(HEAD:.o=.S)
piggy.o: $(SYSTEM)
$(OBJCOPY) -O binary -R .note -R .comment -S $(SYSTEM) piggy
gzip $(GZFLAGS) < piggy > piggy.gz
$(LD) -r -o $@ -b binary piggy.gz
rm -f piggy piggy.gz
font.o: $(FONTC)
$(CC) $(CFLAGS) -Dstatic= -c -o $@ $(FONTC)
vmlinux.lds: vmlinux.lds.in Makefile $(TOPDIR)/arch/$(ARCH)/boot/Makefile $(TOPDIR)/.config
@sed "$(SEDFLAGS)" < vmlinux.lds.in > $@
clean:; rm -f vmlinux core piggy* vmlinux.lds
.PHONY: clean
misc.o: misc.c $(TOPDIR)/include/asm/arch/uncompress.h $(TOPDIR)/lib/inflate.c
.align
start:
.type start,#function
//重复如下指令8次
.rept 8
mov r0, r0
.endr
//跳转指令,跳到下面第一个标号1处
b 1f
//这就是第10条指令的位置,也就是偏移为4*9个字节
.word 0x016f2818 @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
1: mov r7, r1 @ save architecture ID
mov r8, #0 @ save r0
/*
* Kernel startup entry point.
*
* The rules are:
* r0 - should be 0
* r1 - unique architecture number
* MMU - off
* I-cache - on or off
* D-cache - off
*
* See linux/arch/arm/tools/mach-types for the complete list of numbers
* for r1.
*/
asmlinkage void __init start_kernel(void)
{
char * command_line;
unsigned long mempages;
extern char saved_command_line[];
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
printk(linux_banner);
setup_arch(&command_line);
printk("Kernel command line: %s\n", saved_command_line);
parse_options(command_line);
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = NULL;
struct machine_desc *mdesc;
char *from = default_command_line;
ROOT_DEV = MKDEV(0, 255);
setup_processor();
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup("s");
if (mdesc->param_offset)
tags = phys_to_virt(mdesc->param_offset);
/*
* Do the machine-specific fixups before we parse the
* parameters or tags.
*/
if (mdesc->fixup)
mdesc->fixup(mdesc, (struct param_struct *)tags,
&from, &meminfo);
/*
* If we have the old style parameters, convert them to
* a tag list before.
*/
if (tags && tags->hdr.tag != ATAG_CORE)
convert_to_tag_list((struct param_struct *)tags,
meminfo.nr_banks == 0);
if (tags && tags->hdr.tag == ATAG_CORE)
parse_tags(tags);
if (meminfo.nr_banks == 0) {
meminfo.nr_banks = 1;
meminfo.bank[0].start = PHYS_OFFSET;
meminfo.bank[0].size = MEM_SIZE;
}
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(&meminfo, cmdline_p, from);
bootmem_init(&meminfo);
paging_init(&meminfo, mdesc);
request_standard_resources(&meminfo, mdesc);
/*
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
}
unsigned int __machine_arch_type;
MRPROPER_FILES += \
arch/arm/tools/constants.h* \
include/asm-arm/arch \
include/asm-arm/proc \
include/asm-arm/constants.h* \
include/asm-arm/mach-types.h
# We use MRPROPER_FILES and CLEAN_FILES now
archmrproper:
@/bin/true
archclean:
@$(MAKEBOOT) clean
archdep: scripts/mkdep archsymlinks
@$(MAKETOOLS) dep
@$(MAKEBOOT) dep
MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
MAKETOOLS = $(MAKE) -C arch/$(ARCH)/tools
all: $(TOPDIR)/include/asm-arm/mach-types.h \
$(TOPDIR)/include/asm-arm/constants.h
$(TOPDIR)/include/asm-arm/mach-types.h: mach-types gen-mach-types
awk -f gen-mach-types mach-types > $@
#ifdef CONFIG_S3C2410_SMDK
# ifdef machine_arch_type
# undef machine_arch_type
# define machine_arch_type __machine_arch_type
# else
# define machine_arch_type MACH_TYPE_SMDK2410
# endif
# define machine_is_smdk2410() (machine_arch_type == MACH_TYPE_SMDK2410)
#else
# define machine_is_smdk2410() (0)
#endif
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head-armv.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_ram; /* start of physical ram */
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 int param_offset; /* parameter page */
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 param_struct *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
};
/*
* Set of macros to define architecture features. This is built into
* a table by the linker.
*/
#define MACHINE_START(_type,_name) \
const struct machine_desc __mach_desc_##_type \
__attribute__((__section__(".arch.info"))) = { \
nr: MACH_TYPE_##_type, \
name: _name,
#define MAINTAINER(n)
#define BOOT_MEM(_pram,_pio,_vio) \
phys_ram: _pram, \
phys_io: _pio, \
io_pg_offst: ((_vio)>>18)&0xfffc,
#define BOOT_PARAMS(_params) \
param_offset: _params,
#define VIDEO(_start,_end) \
video_start: _start, \
video_end: _end,
#define DISABLE_PARPORT(_n) \
reserve_lp##_n: 1,
#define BROKEN_HLT /* unused */
#define SOFT_REBOOT \
soft_reboot: 1,
#define FIXUP(_func) \
fixup: _func,
#define MAPIO(_func) \
map_io: _func,
#define INITIRQ(_func) \
init_irq: _func,
#define MACHINE_END \
};
MACHINE_START(SMDK2410, "Embest EduKit III (S3C2410x)")
BOOT_MEM(0x30000000, 0x48000000, 0xe8000000)
BOOT_PARAMS(0x30000100)
FIXUP(fixup_smdk)
MAPIO(smdk_map_io)
INITIRQ(s3c2410_init_irq)
MACHINE_END
const struct machine_desc __mach_desc_smdk2410 = {
nr: 193,
name: "EDUKIT-III (s3c2410)",
phys_ram: 0x30000000,
phys_to: 0x48000000,
io_pg_offset: 0x3a00,
param_offset: 0x30000100,
fixup: fixup_smdk,//实际上为空
map_io: smdk_map_io,
init_irq: s3c2410_init_irq,
};
static struct machine_desc * __init setup_machine(unsigned int nr)
{
extern struct machine_desc __arch_info_begin, __arch_info_end;
struct machine_desc *list;
/*
* locate architecture in the list of supported architectures.
*/
for (list = &__arch_info_begin; list < &__arch_info_end; list++)
if (list->nr == nr)
break;
/*
* If the architecture type is not recognised, then we
* can co nothing...
*/
if (list >= &__arch_info_end) {
printk("Architecture configuration botched (nr %d), unable "
"to continue.\n", nr);
while (1);
}
printk("Machine: %s\n", list->name);
return list;
}
.init : { /* Init code and data */
_stext = .;
__init_begin = .;
*(.text.init)
__proc_info_begin = .;
*(.proc.info)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist)
__tagtable_end = .;
if (mdesc->param_offset)
tags = phys_to_virt(mdesc->param_offset);
if (tags && tags->hdr.tag != ATAG_CORE)
convert_to_tag_list((struct param_struct *)tags,
meminfo.nr_banks == 0);
if (tags && tags->hdr.tag == ATAG_CORE)
parse_tags(tags);
/*
* Scan the tag table for this tag, and call its parse function.
* The tag table is built by the linker from all the __tagtable
* declarations.
*/
static int __init parse_tag(const struct tag *tag)
{
extern struct tagtable __tagtable_begin, __tagtable_end;
struct tagtable *t;
for (t = &__tagtable_begin; t < &__tagtable_end; t++)
if (tag->hdr.tag == t->tag) {
t->parse(tag);
break;
}
return t < &__tagtable_end;
}
#define __tag __attribute__((unused, __section__(".taglist")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
static struct tagtable __tagtable_ATAG_CMDLINE __tag = {
ATAG_CMDLINE,
parse_tag_cmdline
};
static int __init parse_tag_cmdline(const struct tag *tag)
{
#ifndef CONFIG_NO_TAG_CMDLINE
strncpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
#endif
default_command_line[COMMAND_LINE_SIZE - 1] = '\0';
return 0;
}
char *from = default_command_line;
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
linux2.6内核中的MACHINE_START宏
现在正在阅读linux2.6.18内核,在mainstone.c文件中,有如下的宏定义:
MACHINE_START(MAINSTONE, "Intel HCDDBBVA0 Development Platform (aka Mainstone)")
/* Maintainer: MontaVista Software Inc. */
.phys_io = 0x40000000,
.boot_params = 0xa0000100, /* BLOB boot parameter setting */
.io_pg_offst = (io_p2v(0x40000000) >> 1 & 0xfffc,
.map_io = mainstone_map_io,
.init_irq = mainstone_init_irq,
.timer = &pxa_timer,
.init_machine = mainstone_init,
MACHINE_END
请问各位大侠,这个宏定义甚么时候调用的,是谁调用的它,象里面的mainstone_init是哪个函数调用的它?是不是在main函数中的初始化的时候?
自己看宏的定义,主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
kernel boot 起来的时候期望 bootloader 传参数进来,其中包括 Machine Type,参考
arch/arm/tools/mach-types 并和 MACHINE_START() 第一个参数对上号。因此,哪个
MACHINE 是 run-time 的时候决定的,this way, you can pack as many machine as you
want, and dynamically initialize the specific platforms.
各个成员函数在不同时期被调用:
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用 start_kernel,参考 init/main.c
2. init_irq在start_kernel() --> init_IRQ() --> init_arch_irq() 被调用
3. map_io 在 setup_arch() --> paging_init() --> devicemaps_init()
其他主要都在 setup_arch() 中用到