内核编译:
1 make menuconfig 生成.config文件和autoconf.h
.config会生成auto.conf
2 make
查看配置项:CONFIG_DM9000定义的位置
[root@localhost linux-2.6.38]# grep "CONFIG_DM9000" * -nR
1 c源码中定义 arch/arm/mach-s3c64xx/mach-mini6410.c CONFIG_DM9000(宏定义)
2 Makefile中 drivers/net/Makefile
子目录下的makefile:obj-$(CONFIG_DM9000) += dm9000.o
CONFIG_DM9000值起以下作用:
obj-y+=xxx.o 表示最终会编译进内核
obj-m+=yyy.o 表最终会编译成可加载模块.ko
3 include/config/auto.conf,由.config生成此文件, CONFIG_DM9000的值在此定义
config_linux_mini6410:778:CONFIG_DM9000=y (供子目录的makefile使用)
4 include/linux/autoconf.h
=======================================================================
分析makefile
===========================================================================
注:documentation/kbuild/Makefile.txt 需学习
linux内核中makefile的分类
1项层Makefile
2.config 配置文件,在配置内核时生成,所有makefile都根据此文件来决定使用哪些文件
3arch/$(ARCH)/Makefile 对应体系的makefile,决定哪些体系结构的相关文件参与内核的生成
4scripts/Makefile.* makefile共用的通用规则、脚本
5kbuild Makefile 各级子目录下的makefile,被上层makefile调用编译当前目录下文件
编译进内核
编译为可加载模块
两个c文件编译为同一个可加载模块: obj-m+=ab.o
ab-objs :=a.o b.o
make uImage时会跳到arch/arm/Makefile中
示例代码如下:
#uImage依赖于vmlinux
zImage Image xipImage bootpImage uImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
......
#Default value
head-y:= arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
在顶层Makefile中可以看到如下定义:(会将子目录makefile包含进来)
#ARCH?= $(SUBARCH)
ARCH?= arm
CROSS_COMPILE?= $(CONFIG_CROSS_COMPILE:"%"=%)
# Architecture as present in compile.h
UTS_MACHINE := $(ARCH)
SRCARCH := $(ARCH)
.........
# used for 'make defconfig'
include $(srctree)/arch/$(SRCARCH)/Makefile
.........
#以下两句=init/built-in.o
init-y:= init/
init-y:= $(patsubst %/, %/built-in.o, $(init-y))
#以下三句=usr/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o
core-y:= usr/
core-y+= kernel/ mm/ fs/ ipc/ security/ crypto/ block/
core-y:= $(patsubst %/, %/built-in.o, $(core-y))
#以下语句=lib/lib.a lib/built-in.o
libs-y:= lib/
libs-y1:= $(patsubst %/, %/lib.a, $(libs-y))
libs-y2:= $(patsubst %/, %/built-in.o, $(libs-y))
libs-y:= $(libs-y1) $(libs-y2)
#以下语句=drivers/built-in.o sound/built-in.o firmware/built-in.o
drivers-y:= drivers/ sound/ firmware/
drivers-y:= $(patsubst %/, %/built-in.o, $(drivers-y))
#以下语句=net/built-in.o
net-y:= net/
net-y:= $(patsubst %/, %/built-in.o, $(net-y))
#head-y定义在子目录中
vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds := arch/$(SRCARCH)/kernel/vmlinux.lds
# vmlinux image - including updated kernel symbols
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
#查看以上内空是怎么组织进内核:1 查看makefile
2 直接编译 make V=1
查看最后一条命令
.config生成:autoconfig.h (供原码使用)
auto.conf
在顶层Makefile中可以看到定义如下:
# Read in config
-include include/config/auto.conf
arm-linux-ld -EL -p --no-undefined -X --build-id -o vmlinux -T //生成vmlinux文件
arch/arm/kernel/vmlinux.lds //链接文件
arch/arm/kernel/head.o //第一个文件
arch/arm/kernel/init_task.o
//init-y
init/built-in.o --start-group
//core-y
usr/built-in.o arch/arm/nwfpe/built-in.o arch/arm/vfp/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/mach-s3c64xx/built-in.o arch/arm/plat-samsung/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o
//libs-y
arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o
//drivers-y
drivers/built-in.o sound/built-in.o firmware/built-in.o
//net-y
net/built-in.o --end-group .tmp_kallsyms2.o
从以上分析可以得出:
1 第一个文件:arch/arm/kernel/head.S
2 链接角本:arch/arm/kernel/vmlinux.lds.S
内核所作工作:
1 处理uboot传入的参数(机器id,启动参数)
head.S所作的事情
a 检查是不支持该cpu架构
b 判断是否支持该单板
c 建立一级页表
d 使能mmu
e start_kernel第一个c函数
挂载 根文件系统
start_kernel
setup_arch //解析uboot传入的启动参数
setup_command_line//解析uboot传入的启动参数
parse_early_param
do_early_param //调用early函数,命令行参数不是在此调用
unknown_bootoption
obsolete_checksetup //调用非early函数
rest_init
kernel_init (命令行已打开)
prepare_namespace
mount_root//挂载根文件系统
init_post//根文件系统挂载完成后,执行应用程序
命令行参数的处理:
static int __init root_dev_setup(char *line)
{
strlcpy(saved_root_name, line, sizeof(saved_root_name));
return 1;
}
__setup("root=", root_dev_setup);
//include/linux/init.h文件中定义
#define __setup(str, fn)\
__setup_param(str, fn, fn, 0)
....
#define __setup_param(str, unique_id, fn, early)\
static const char __setup_str_##unique_id[] __initconst\
__aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id\
__used __section(.init.setup)\ //强行设置为.init.setup段,定义在vmlinux.S
__attribute__((aligned((sizeof(long)))))\
= { __setup_str_##unique_id, fn, early }
//查看哪些函数会调用.init.setup中的内容,即从__setup_start到__setup_end
//有以下两个函数调用
1static int __init obsolete_checksetup(char *line)
{
const struct obs_kernel_param *p;
int had_early_param = 0;
p = __setup_start;
do {
int n = strlen(p->str);
if (!strncmp(line, p->str, n)) {
if (p->early) {
/* Already done in parse_early_param?
* (Needs exact match on param part).
* Keep iterating, as we can have early
* params and __setups of same names 8( */
if (line[n] == '\0' || line[n] == '=')
had_early_param = 1;
} else if (!p->setup_func) {
printk(KERN_WARNING "Parameter %s is obsolete,"
" ignored\n", p->str);
return 1;
} else if (p->setup_func(line + n))
return 1;
}
p++;
} while (p < __setup_end);
return had_early_param;
}
2 /* Check for early params. */
static int __init do_early_param(char *param, char *val)
{
const struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if ((p->early && strcmp(param, p->str) == 0) ||
(strcmp(param, "console") == 0 &&
strcmp(p->str, "earlycon") == 0)
) {
if (p->setup_func(val) != 0)
printk(KERN_WARNING
"Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
内核的最终目的: 启动应用程序
分区表定义在: /arch/arm/mach-s3c64xx/Mach-mini6410.c文件中
=====================================================================
arch/arm/kernel/head.s文件
====================================================================
/*
* linux/arch/arm/kernel/head.S
*
* Copyright (C) 1994-2002 Russell King
* Copyright (c) 2003 ARM Limited
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Kernel startup code for all 32-bit CPUs
*/
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/assembler.h>
#include <asm/domain.h>
#include <asm/ptrace.h>
#include <asm/asm-offsets.h>
#include <asm/memory.h>
#include <asm/thread_info.h>
#include <asm/system.h>
#ifdef CONFIG_DEBUG_LL
#include <mach/debug-macro.S>
#endif
#if (PHYS_OFFSET & 0x001fffff)
#error "PHYS_OFFSET must be at an even 2MiB boundary!"
#endif
#define KERNEL_RAM_VADDR(PAGE_OFFSET + TEXT_OFFSET)
#define KERNEL_RAM_PADDR(PHYS_OFFSET + TEXT_OFFSET)
/*
* swapper_pg_dir is the virtual address of the initial page table.
* We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must
* make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect
* the least significant 16 bits to be 0x8000, but we could probably
* relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
*/
#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
#error KERNEL_RAM_VADDR must start at 0xXXXX8000
#endif
.globlswapper_pg_dir
.equswapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
.macropgtbl, rd
ldr\rd, =(KERNEL_RAM_PADDR - 0x4000)
.endm
#ifdef CONFIG_XIP_KERNEL
#define KERNEL_STARTXIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
#define KERNEL_END_edata_loc
#else
#define KERNEL_STARTKERNEL_RAM_VADDR
#define KERNEL_END_end
#endif
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags pointer.
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
*
* We're trying to keep crap to a minimum; DO NOT add any machine specific
* crap here - that's what the boot loader (or in extreme, well justified
* circumstances, zImage) is for.
*/
__HEAD
ENTRY(stext)
/*1 检查内核是否支持该处理器*/
setmodePSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
mrcp15, 0, r9, c0, c0@ get processor id
bl__lookup_processor_type@ r5=procinfo r9=cpuid
movsr10, r5@ invalid processor (r5=0)?
THUMB( iteq )@ force fixup-able long branch encoding
beq__error_p@ yes, error 'p'
/*检查机器id */
bl__lookup_machine_type@ r5=machinfo 定义在head-common.S中
movsr8, r5@ invalid machine (r5=0)?
THUMB( iteq )@ force fixup-able long branch encoding
beq__error_a@ yes, error 'a'
/*
* r1 = machine no, r2 = atags,
* r8 = machinfo, r9 = cpuid, r10 = procinfo
*/
bl__vet_atags
#ifdef CONFIG_SMP_ON_UP
bl__fixup_smp
#endif
bl__create_page_tables @2 建立页表
/*
* The following calls CPU specific code in a position independent
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
* xxx_proc_info structure selected by __lookup_machine_type
* above. On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.
*/
ldrr13, =__mmap_switched@ 4 address to jump to after 跳转到head_common.S执行
@ mmu has been enabled
adrlr, BSYM(1f)@ return (PIC) address
ARM(addpc, r10, #PROCINFO_INITFUNC)
THUMB(addr12, r10, #PROCINFO_INITFUNC)
THUMB(movpc, r12)
1:b__enable_mmu @3 使能mmu
ENDPROC(stext)
.ltorg
/*
* Setup the initial page tables. We only setup the barest
* amount which are required to get the kernel running, which
* generally means mapping in the kernel code.
*
* r8 = machinfo
* r9 = cpuid
* r10 = procinfo
*
* Returns:
* r0, r3, r5-r7 corrupted
* r4 = physical page table address
*/
__create_page_tables:
pgtblr4@ page table address
/*
* Clear the 16K level 1 swapper page table
*/
movr0, r4
movr3, #0
addr6, r0, #0x4000
1:strr3, [r0], #4
strr3, [r0], #4
strr3, [r0], #4
strr3, [r0], #4
teqr0, r6
bne1b
ldrr7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
/*
* Create identity mapping to cater for __enable_mmu.
* This identity mapping will be removed by paging_init().
*/
adrr0, __enable_mmu_loc
ldmiar0, {r3, r5, r6}
subr0, r0, r3@ virt->phys offset
addr5, r5, r0@ phys __enable_mmu
addr6, r6, r0@ phys __enable_mmu_end
movr5, r5, lsr #20
movr6, r6, lsr #20
1:orrr3, r7, r5, lsl #20@ flags + kernel base
strr3, [r4, r5, lsl #2]@ identity mapping
teqr5, r6
addner5, r5, #1@ next section
bne1b
/*
* Now setup the pagetables for our kernel direct
* mapped region.
*/
movr3, pc
movr3, r3, lsr #20
orrr3, r7, r3, lsl #20
addr0, r4, #(KERNEL_START & 0xff000000) >> 18
strr3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
ldrr6, =(KERNEL_END - 1)
addr0, r0, #4
addr6, r4, r6, lsr #18
1:cmpr0, r6
addr3, r3, #1 << 20
strlsr3, [r0], #4
bls1b
#ifdef CONFIG_XIP_KERNEL
/*
* Map some ram to cover our .data and .bss areas.
*/
orrr3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
.if(KERNEL_RAM_PADDR & 0x00f00000)
orrr3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
.endif
addr0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18
strr3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
ldrr6, =(_end - 1)
addr0, r0, #4
addr6, r4, r6, lsr #18
1:cmpr0, r6
addr3, r3, #1 << 20
strlsr3, [r0], #4
bls1b
#endif
/*
* Then map first 1MB of ram in case it contains our boot params.
*/
addr0, r4, #PAGE_OFFSET >> 18
orrr6, r7, #(PHYS_OFFSET & 0xff000000)
.if(PHYS_OFFSET & 0x00f00000)
orrr6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
strr6, [r0]
#ifdef CONFIG_DEBUG_LL
#ifndef CONFIG_DEBUG_ICEDCC
/*
* Map in IO space for serial debugging.
* This allows debug messages to be output
* via a serial console before paging_init.
*/
addruart r7, r3
movr3, r3, lsr #20
movr3, r3, lsl #2
addr0, r4, r3
rsbr3, r3, #0x4000@ PTRS_PER_PGD*sizeof(long)
cmpr3, #0x0800@ limit to 512MB
movhir3, #0x0800
addr6, r0, r3
movr3, r7, lsr #20
ldrr7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
orrr3, r7, r3, lsl #20
1:strr3, [r0], #4
addr3, r3, #1 << 20
teqr0, r6
bne1b
#else /* CONFIG_DEBUG_ICEDCC */
/* we don't need any serial debugging mappings for ICEDCC */
ldrr7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
#endif /* !CONFIG_DEBUG_ICEDCC */
#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
/*
* If we're using the NetWinder or CATS, we also need to map
* in the 16550-type serial port for the debug messages
*/
addr0, r4, #0xff000000 >> 18
orrr3, r7, #0x7c000000
strr3, [r0]
#endif
#ifdef CONFIG_ARCH_RPC
/*
* Map in screen at 0x02000000 & SCREEN2_BASE
* Similar reasons here - for debug. This is
* only for Acorn RiscPC architectures.
*/
addr0, r4, #0x02000000 >> 18
orrr3, r7, #0x02000000
strr3, [r0]
addr0, r4, #0xd8000000 >> 18
strr3, [r0]
#endif
#endif
movpc, lr
ENDPROC(__create_page_tables)
.ltorg
.align
__enable_mmu_loc:
.long.
.long__enable_mmu
.long__enable_mmu_end
#if defined(CONFIG_SMP)
__CPUINIT
ENTRY(secondary_startup)
/*
* Common entry point for secondary CPUs.
*
* Ensure that we're in SVC mode, and IRQs are disabled. Lookup
* the processor type - there is no need to check the machine type
* as it has already been validated by the primary processor.
*/
setmodePSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
mrcp15, 0, r9, c0, c0@ get processor id
bl__lookup_processor_type
movsr10, r5@ invalid processor?
moveqr0, #'p'@ yes, error 'p'
THUMB( iteq )@ force fixup-able long branch encoding
beq__error_p
/*
* Use the page tables supplied from __cpu_up.
*/
adrr4, __secondary_data
ldmiar4, {r5, r7, r12}@ address to jump to after
subr4, r4, r5@ mmu has been enabled
ldrr4, [r7, r4]@ get secondary_data.pgdir
adrlr, BSYM(__enable_mmu)@ return address
movr13, r12@ __secondary_switched address
ARM(addpc, r10, #PROCINFO_INITFUNC) @ initialise processor
@ (return control reg)
THUMB(addr12, r10, #PROCINFO_INITFUNC)
THUMB(movpc, r12)
ENDPROC(secondary_startup)
/*
* r6 = &secondary_data
*/
ENTRY(__secondary_switched)
ldrsp, [r7, #4]@ get secondary_data.stack
movfp, #0
bsecondary_start_kernel
ENDPROC(__secondary_switched)
.align
.type__secondary_data, %object
__secondary_data:
.long.
.longsecondary_data
.long__secondary_switched
#endif /* defined(CONFIG_SMP) */
/*
* Setup common bits before finally enabling the MMU. Essentially
* this is just loading the page table pointer and domain access
* registers.
*
* r0 = cp#15 control register
* r1 = machine ID
* r2 = atags pointer
* r4 = page table pointer
* r9 = processor ID
* r13 = *virtual* address to jump to upon completion
*/
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
orrr0, r0, #CR_A
#else
bicr0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
bicr0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
bicr0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
bicr0, r0, #CR_I
#endif
movr5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT))
mcrp15, 0, r5, c3, c0, 0@ load domain access register
mcrp15, 0, r4, c2, c0, 0@ load page table pointer
b__turn_mmu_on
ENDPROC(__enable_mmu)
/*
* Enable the MMU. This completely changes the structure of the visible
* memory space. You will not be able to trace execution through this.
* If you have an enquiry about this, *please* check the linux-arm-kernel
* mailing list archives BEFORE sending another post to the list.
*
* r0 = cp#15 control register
* r1 = machine ID
* r2 = atags pointer
* r9 = processor ID
* r13 = *virtual* address to jump to upon completion
*
* other registers depend on the function called upon completion
*/
.align5
__turn_mmu_on:
movr0, r0
mcrp15, 0, r0, c1, c0, 0@ write control reg
mrcp15, 0, r3, c0, c0, 0@ read id reg
movr3, r3
movr3, r13
movpc, r3
__enable_mmu_end:
ENDPROC(__turn_mmu_on)
#ifdef CONFIG_SMP_ON_UP
__INIT
__fixup_smp:
andr3, r9, #0x000f0000@ architecture version
teqr3, #0x000f0000@ CPU ID supported?
bne__fixup_smp_on_up@ no, assume UP
bicr3, r9, #0x00ff0000
bicr3, r3, #0x0000000f@ mask 0xff00fff0
movr4, #0x41000000
orrr4, r4, #0x0000b000
orrr4, r4, #0x00000020@ val 0x4100b020
teqr3, r4@ ARM 11MPCore?
moveqpc, lr@ yes, assume SMP
mrcp15, 0, r0, c0, c0, 5@ read MPIDR
andr0, r0, #0xc0000000@ multiprocessing extensions and
teqr0, #0x80000000@ not part of a uniprocessor system?
moveqpc, lr@ yes, assume SMP
__fixup_smp_on_up:
adrr0, 1f
ldmiar0, {r3 - r5}
subr3, r0, r3
addr4, r4, r3
addr5, r5, r3
b__do_fixup_smp_on_up
ENDPROC(__fixup_smp)
.align
1:.word.
.word__smpalt_begin
.word__smpalt_end
.pushsection .data
.globlsmp_on_up
smp_on_up:
ALT_SMP(.long1)
ALT_UP(.long0)
.popsection
#endif
.text
__do_fixup_smp_on_up:
cmpr4, r5
movhspc, lr
ldmiar4!, {r0, r6}
ARM(strr6, [r0, r3])
THUMB(addr0, r0, r3)
#ifdef __ARMEB__
THUMB(movr6, r6, ror #16)@ Convert word order for big-endian.
#endif
THUMB(strhr6, [r0], #2)@ For Thumb-2, store as two halfwords
THUMB(movr6, r6, lsr #16)@ to be robust against misaligned r3.
THUMB(strhr6, [r0])
b__do_fixup_smp_on_up
ENDPROC(__do_fixup_smp_on_up)
ENTRY(fixup_smp)
stmfdsp!, {r4 - r6, lr}
movr4, r0
addr5, r0, r1
movr3, #0
bl__do_fixup_smp_on_up
ldmfdsp!, {r4 - r6, pc}
ENDPROC(fixup_smp)
#include "head-common.S"
====================================================================
arch/arm/kernel/head-common.S
===================================================================
/*
* linux/arch/arm/kernel/head-common.S
*
* Copyright (C) 1994-2002 Russell King
* Copyright (c) 2003 ARM Limited
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#define ATAG_CORE 0x54410001
#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
#define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
/*
* Exception handling. Something went wrong and we can't proceed. We
* ought to tell the user, but since we don't have any guarantee that
* we're even running on the right architecture, we do virtually nothing.
*
* If CONFIG_DEBUG_LL is set we try to print out something about the error
* and hope for the best (useful if bootloader fails to pass a proper
* machine ID for example).
*/
__HEAD
__error_a:
#ifdef CONFIG_DEBUG_LL
movr4, r1@ preserve machine ID
adrr0, str_a1
blprintascii
movr0, r4
blprinthex8
adrr0, str_a2
blprintascii
adrr3, __lookup_machine_type_data
ldmiar3, {r4, r5, r6}@ get machine desc list
subr4, r3, r4@ get offset between virt&phys
addr5, r5, r4@ convert virt addresses to
addr6, r6, r4@ physical address space
1:ldrr0, [r5, #MACHINFO_TYPE]@ get machine type
blprinthex8
movr0, #'\t'
blprintch
ldr r0, [r5, #MACHINFO_NAME]@ get machine name
addr0, r0, r4
blprintascii
movr0, #'\n'
blprintch
addr5, r5, #SIZEOF_MACHINE_DESC@ next machine_desc
cmpr5, r6
blo1b
adrr0, str_a3
blprintascii
b__error
ENDPROC(__error_a)
str_a1:.asciz"\nError: unrecognized/unsupported machine ID (r1 = 0x"
str_a2:.asciz").\n\nAvailable machine support:\n\nID (hex)\tNAME\n"
str_a3:.asciz"\nPlease check your kernel config and/or bootloader.\n"
.align
#else
b__error
#endif
/*
* 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
*/
__lookup_machine_type:
adrr3, __lookup_machine_type_data @r3的地址=__lookup_machine_type_data 此时mmu还未启动,所以是物理地址
ldmiar3, {r4, r5, r6} @ r4=“.”(虚拟地址) r5=__arch_info_begin r6=__arch_info_end
subr3, r3, r4@ get offset between virt&phys 虚拟地址和物理地址的偏差
addr5, r5, r3@ convert virt addresses to r5+偏差=物理地址
addr6, r6, r3@ physical address space r6+偏差=物理地址
1:ldrr3, [r5, #MACHINFO_TYPE]@ get machine type
teqr3, r1@ matches loader number?
beq2f@ found
addr5, r5, #SIZEOF_MACHINE_DESC@ next machine_desc
cmpr5, r6
blo1b
movr5, #0@ unknown machine
2:movpc, lr
ENDPROC(__lookup_machine_type)
/*
* Look in arch/arm/kernel/arch.[ch] for information about the
* __arch_info structures.
*/
.align2
.type__lookup_machine_type_data, %object
__lookup_machine_type_data:
.long.
.long__arch_info_begin @定义在arch/arm/kernel/vmlinux.lds.S中
.long__arch_info_end @定义在arch/arm/kernel/vmlinux.lds.S中
.size__lookup_machine_type_data, . - __lookup_machine_type_data
#############################################################################
/* __arch_info_begin = .;
* *(.arch.info.init) //.arch.info.init定义在arch/arm/include/asm/mach/arch.h中
* __arch_info_end = .;
*/
// arch.h的部分内空如下:
#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/mach-s3c64xx/mach-mini6410.c的部分内容如下:
MACHINE_START(MINI6410, "MINI6410")
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.boot_params= S3C64XX_PA_SDRAM + 0x100,
.init_irq= s3c6410_init_irq,
.map_io= mini6410_map_io,
.init_machine= mini6410_machine_init,
.timer= &s3c24xx_timer,
MACHINE_END
//以上宏展开后为:(机器描述的结构体)
//该段就被放到指定的段位置去.arch.info.init
#define MACHINE_START(_type,_name)\
static const struct machine_desc __mach_desc_MINI6410\
__used\
__attribute__((__section__(".arch.info.init"))) = {\
.nr= MACH_TYPE_MINI6410,\
.name= "MINI6410",
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.boot_params= S3C64XX_PA_SDRAM + 0x100,
.init_irq= s3c6410_init_irq,
.map_io= mini6410_map_io,
.init_machine= mini6410_machine_init,
.timer= &s3c24xx_timer,
};
/*机器描述的结构体*/
struct machine_desc {
unsigned intnr;/* architecture number*/
const char*name;/* architecture name*/
unsigned longboot_params;/* tagged list*/
unsigned intnr_irqs;/* number of IRQs */
unsigned intvideo_start;/* start of video RAM*/
unsigned intvideo_end;/* end of video RAM*/
unsigned intreserve_lp0 :1;/* never has lp0*/
unsigned intreserve_lp1 :1;/* never has lp1*/
unsigned intreserve_lp2 :1;/* never has lp2*/
unsigned intsoft_reboot :1;/* soft reboot*/
void(*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void(*reserve)(void);/* reserve mem blocks*/
void(*map_io)(void);/* IO mapping function*/
void(*init_early)(void);
void(*init_irq)(void);
struct sys_timer*timer;/* system tick timer*/
void(*init_machine)(void);
#ifdef CONFIG_MULTI_IRQ_HANDLER
void(*handle_irq)(struct pt_regs *);
#endif
};
#####################################################################
/* Determine validity of the r2 atags pointer. The heuristic requires
* that the pointer be aligned, in the first 16k of physical RAM and
* that the ATAG_CORE marker is first and present. Future revisions
* of this function may be more lenient with the physical address and
* may also be able to move the ATAGS block if necessary.
*
* r8 = machinfo
*
* Returns:
* r2 either valid atags pointer, or zero
* r5, r6 corrupted
*/
__vet_atags:
tstr2, #0x3@ aligned?
bne1f
ldrr5, [r2, #0]@ is first tag ATAG_CORE?
cmpr5, #ATAG_CORE_SIZE
cmpner5, #ATAG_CORE_SIZE_EMPTY
bne1f
ldrr5, [r2, #4]
ldrr6, =ATAG_CORE
cmpr5, r6
bne1f
movpc, lr@ atag pointer is ok
1:movr2, #0
movpc, lr
ENDPROC(__vet_atags)
/*
* The following fragment of code is executed with the MMU on in MMU mode,
* and uses absolute addresses; this is not position independent.
*
* r0 = cp#15 control register
* r1 = machine ID
* r2 = atags pointer
* r9 = processor ID
*/
__INIT
__mmap_switched: @mmap_switched所作操作
adrr3, __mmap_switched_data
ldmiar3!, {r4, r5, r6, r7}
cmpr4, r5@ Copy data segment if needed 1复制数据段
1:cmpner5, r6
ldrnefp, [r4], #4
strnefp, [r5], #4
bne1b
movfp, #0@ Clear BSS (and zero fp) 2 清bss段
1:cmpr6, r7
strccfp, [r6],#4 @设置栈指针
bcc1b
ARM(ldmiar3, {r4, r5, r6, r7, sp})
THUMB(ldmiar3, {r4, r5, r6, r7})
THUMB(ldrsp, [r3, #16]) @保存机器信息
strr9, [r4]@ Save processor ID
strr1, [r5]@ Save machine type
strr2, [r6]@ Save atags pointer
bicr4, r0, #CR_A@ Clear 'A' bit
stmiar7, {r0, r4}@ Save control register values
bstart_kernel @跳转到start_kernel(第一个c函数),定义在/init/main.c 中
ENDPROC(__mmap_switched)
.align2
.type__mmap_switched_data, %object
__mmap_switched_data:
.long__data_loc@ r4
.long_sdata@ r5
.long__bss_start@ r6
.long_end@ r7
.longprocessor_id@ r4
.long__machine_arch_type@ r5
.long__atags_pointer@ r6
.longcr_alignment@ r7
.longinit_thread_union + THREAD_START_SP @ sp
.size__mmap_switched_data, . - __mmap_switched_data
/*
* This provides a C-API version of __lookup_machine_type
*/
ENTRY(lookup_machine_type)
stmfdsp!, {r4 - r6, lr}
movr1, r0
bl__lookup_machine_type
movr0, r5
ldmfdsp!, {r4 - r6, pc}
ENDPROC(lookup_machine_type)
/*
* This provides a C-API version of __lookup_processor_type
*/
ENTRY(lookup_processor_type)
stmfdsp!, {r4 - r6, r9, lr}
movr9, r0
bl__lookup_processor_type
movr0, r5
ldmfdsp!, {r4 - r6, r9, pc}
ENDPROC(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)
*/
__CPUINIT
__lookup_processor_type:
adrr3, __lookup_processor_type_data
ldmiar3, {r4 - r6}
subr3, r3, r4@ get offset between virt&phys
addr5, r5, r3@ convert virt addresses to
addr6, r6, r3@ physical address space
1:ldmiar5, {r3, r4}@ value, mask
andr4, r4, r9@ mask wanted bits
teqr3, r4
beq2f
addr5, r5, #PROC_INFO_SZ@ sizeof(proc_info_list)
cmpr5, r6
blo1b
movr5, #0@ unknown processor
2:movpc, lr
ENDPROC(__lookup_processor_type)
/*
* Look in <asm/procinfo.h> for information about the __proc_info structure.
*/
.align2
.type__lookup_processor_type_data, %object
__lookup_processor_type_data:
.long.
.long__proc_info_begin
.long__proc_info_end
.size__lookup_processor_type_data, . - __lookup_processor_type_data
__error_p:
#ifdef CONFIG_DEBUG_LL
adrr0, str_p1
blprintascii
movr0, r9
blprinthex8
adrr0, str_p2
blprintascii
b__error
str_p1:.asciz"\nError: unrecognized/unsupported processor variant (0x"
str_p2:.asciz").\n"
.align
#endif
ENDPROC(__error_p)
__error:
#ifdef CONFIG_ARCH_RPC
/*
* Turn the screen red on a error - RiscPC only.
*/
movr0, #0x02000000
movr3, #0x11
orrr3, r3, r3, lsl #8
orrr3, r3, r3, lsl #16
strr3, [r0], #4
strr3, [r0], #4
strr3, [r0], #4
strr3, [r0], #4
#endif
1:movr0, r0
b1b
ENDPROC(__error)
==============================================================================
init/main.c
==============================================================================
部分代码如下;
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const 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();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
tick_init();
boot_cpu_init();
page_address_init();
printk(KERN_NOTICE "%s", linux_banner);
setup_arch(&command_line); //跳转到setup.c中setup_atch函数,取得启动参数,代码如下
mm_init_owner(&init_mm, &init_task);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu();/* arch-specific boot-cpu hooks */
build_all_zonelists(NULL);
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
pidhash_init();
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();
/*
* 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 functioning scheduler.
*/
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it\n");
local_irq_disable();
}
idr_init_cache();
perf_event_init();
rcu_init();
radix_tree_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
prio_tree_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
profile_init();
if (!irqs_disabled())
printk(KERN_CRIT "start_kernel(): bug: interrupts were "
"enabled early\n");
early_boot_irqs_disabled = false;
local_irq_enable();
/* Interrupts are enabled now so all GFP allocations are safe. */
gfp_allowed_mask = __GFP_BITS_MASK;
kmem_cache_init_late();
/*
* 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 &&
page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
"disabling it.\n",
page_to_pfn(virt_to_page((void *)initrd_start)),
min_low_pfn);
initrd_start = 0;
}
#endif
page_cgroup_init();
enable_debug_pagealloc();
kmemleak_init();
debug_objects_mem_init();
setup_per_cpu_pageset();
numa_policy_init();
if (late_time_init)
late_time_init();
sched_clock_init();
calibrate_delay();
pidmap_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
proc_caches_init();
buffer_init();
key_init();
security_init();
dbg_late_init();
vfs_caches_init(totalram_pages);
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cgroup_init();
cpuset_init();
taskstats_init_early();
delayacct_init();
check_bugs();
acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();
ftrace_init();
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
.........
static noinline void __init_refok rest_init(void)
{
int pid;
rcu_scheduler_starting();
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //启动线程,调用kernel_init函数
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
rcu_read_lock();
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
rcu_read_unlock();
complete(&kthreadd_done);
/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);
preempt_enable_no_resched();
schedule();
preempt_disable();
/* Call into cpu_idle with preempt disabled */
cpu_idle();
}
.........
static int __init kernel_init(void * unused)
{
/*
* Wait until kthreadd is all set-up.
*/
wait_for_completion(&kthreadd_done);
/*
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_HIGH_MEMORY]);
/*
* init can run on any cpu.
*/
set_cpus_allowed_ptr(current, cpu_all_mask);
/*
* Tell the world that we're going to be the grim
* reaper of innocent orphaned children.
*
* We don't want people to have to make incorrect
* assumptions about where in the task array this
* can be found.
*/
init_pid_ns.child_reaper = current;
cad_pid = task_pid(current);
smp_prepare_cpus(setup_max_cpus);
do_pre_smp_initcalls();
lockup_detector_init();
smp_init();
sched_init_smp();
do_basic_setup();
/* Open the /dev/console on the rootfs, this should never fail */
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
/*
* check if there is an early userspace init. If yes, let it do all
* the work
*/
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace(); //定义在/init/D0_mounts.c文件中
}
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
init_post(); //根文件系统挂载完成
return 0;
}
==========================================================================
init/Do_mounts.c
==========================================================================
部分代码如下:
void __init prepare_namespace(void)
{
int is_floppy;
if (root_delay) {
printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}
/*
* wait for the known devices to complete their probing
*
* Note: this is a potential source of long boot delays.
* For example, it is not atypical to wait 5 seconds here
* for the touchpad of a laptop to initialize.
*/
wait_for_device_probe();
md_run_setup();
if (saved_root_name[0]) {
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
if (initrd_load())
goto out;
/* wait for any asynchronous scanning to complete */
if ((ROOT_DEV == 0) && root_wait) {
printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(100);
async_synchronize_full();
}
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;
mount_root(); //挂载根文件系统
out:
devtmpfs_mount("dev");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot((const char __user __force *)".");
}
.........
static noinline int init_post(void)
{
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
free_initmem();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
current->signal->flags |= SIGNAL_UNKILLABLE;
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
/* 执行应用程序
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
}
=========================================================================
arch/arm/kernel/setup.c
=========================================================================
部分代码如下:(取得启动参数)
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line; //默认的命令行参数
unwind_init();
setup_processor();
mdesc = setup_machine(machine_arch_type);
machine_desc = mdesc;
machine_name = mdesc->name;
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);
#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
/*
* If we have the old style parameters, convert them to
* a tag list.
*/
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
#endif
if (tags->hdr.tag != ATAG_CORE)
tags = (struct tag *)&init_tags;
if (mdesc->fixup)
mdesc->fixup(mdesc, tags, &from, &meminfo);
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags);
}
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;
/* parse_early_param needs a boot_command_line */
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
/* populate cmd_line too for later use, preserving boot_command_line */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
parse_early_param();
arm_memblock_init(&meminfo, mdesc);
paging_init(mdesc);
request_standard_resources(mdesc);
#ifdef CONFIG_SMP
if (is_smp())
smp_init_cpus();
#endif
reserve_crashkernel();
cpu_init();
tcm_init();
#ifdef CONFIG_MULTI_IRQ_HANDLER
handle_arch_irq = mdesc->handle_irq;
#endif
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
early_trap_init();
if (mdesc->init_early)
mdesc->init_early();
}