1.介绍..........................................................................................2

2.相关定义介绍..........................................................................3

21TEXTADDR......................................................3

22stext....................................................................3

23swapper_pg_dir..................................................4

24(M)pgtbl.............................................................4

25(M)krnladr..........................................................5

26.proc.info.......................................................5

27__proc_info_begin/__proc_info_end.................6

28.arch.info.......................................................7

29__arch_info_begin/__arch_info_end.................8

3.代码分析..................................................................................9

31KERNEL ENTRY..............................................9

32__arm920_setup...............................................11

33__ret.................................................................13

34__mmap_switched...........................................14

35__lookup_processor_type................................16

36__lookup_architecture_type.............................16

37__create_page_tables.......................................19

 
 
1.介绍
 

这是一篇对armlinux内核启动的分析,主要是arch/arm/kernel/head-armv.S文件, head-armv.S文件是整个内核的入口,也就是说bootloader执行完毕后将跳转到head-armv.S的第一条指令,head-armv.S执行完后将跳转到start_kernel(),在head-armv.S的执行过程中也用到了其他一些文件,包括arch/arm/kernel/debug-armv.S、arch/arm/mm/proc-arm920.S等等

由于此分析基于MX1的内核启动过程,因此除了通用代码,只有定义在CONFIG_ARCH_MX1ADS下的代码和proc-arm920.S(arm920是MX1的CPU)的代码被分析

在下面程序流程的说明中,MX1板子启动过程中的寄存器值将会用绿色字体表示出来,而对于专门针对MX1的代码则会用下划线字体表示

 
 
 
2.相关定义介绍
 

21TEXTADDR

TEXTADDR是内核Image的映像地址,也是内核Image所处的虚拟地址,它在系统内核空间起始地址——通常是0xC0000000(这相对应于物理内存开始的地方)+32K的位置,也就是0xC0008000处TEXTADDR的赋值在arch/arm/Makefile文件中:(0xC0008000)

ifeq ($(CONFIG_CPU_32),y)

PROCESSOR = armv

TEXTADDR = 0xC0008000

LDSCRIPT = arch/arm/vmlinux-armv.lds.in

endif

在内核映像之前的16K空间用来存放内核的页目录表,这就是为什么TEXTADDR要在系统要放在0xC0008000的缘故——它必须留出足够的物理空间来放页表

在head-armv.S中TEXTADDR将被检测:

#if (TEXTADDR & 0xffff) != 0x8000 //TEXTADDR必须为0xXXXX8000

#error TEXTADDR must start at 0xXXXX8000

#endif

 
 

22stext

stext是TEXTADDR相对应的物理地址,由于内核空间的起始地址(0xC0000000)相对应的物理地址就是最低物理内存的地址,因此stext就是最低物理内存+32K处(虽然这并不是有内核指定,而是由bootloader指定),它的赋值在连接脚本vmlinux-armv.lds.in中实现:(0x08008000)

ENTRY(stext)

SECTIONS

{

. = TEXTADDR; //此处的映像地址为TEXTADDR

.init : { /* Init code and data */

_stext = .; //stext为此处的物理地址

 

23swapper_pg_dir

swapper_pg_dir是页表的映像地址,由于启动页表在内核Image之前的16K处,因此它等于0xC0004000,它的定义在head-armv.S文件中

.globl SYMBOL_NAME(swapper_pg_dir) //设置swapper_pg_dir

.equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000

在定义init进程的mm_struct结构的宏INIT_MM中,swapper_pg_dir作为页表基址被赋给init_mm,INIT_MM定义在include/linux/sched.h文件中: #define INIT_MM(name) /

{ /

pgd: swapper_pg_dir, /

}

 

24(M)pgtbl

pgtbl是一个用于获得启动页表物理地址的宏,它将stext减去16K给reg,它也在head-armv.S中定义:

.macro pgtbl, reg, rambase

adr /reg, stext

sub /reg, /reg, #0x4000 //reg=stext-0x4000

.endm

 
 

25(M)krnladr

这个宏用于由pgtable获得内核空间的起始物理地址的所在的段(MB),将pgtable,也就是页表地址(和内核空间的起始物理地址在同一个段内)和0x000FFFFF相与,因为页表地址后12位为零,所以将其和0x000FF000相与

/*

* Since the page table is closely related to the kernel start address, we

* can convert the page table base address to the base address of the section

* containing both.

*/

.macro krnladr, rd, pgtable, rambase

bic /rd, /pgtable, #0x000ff000

.endm

 
 
 

26.proc.info

.proc.info段中存放的是各种处理器的信息,每个处理器的信息用一个proc_info_list结构来表示,这个结构在include/asm/procinfo.h文件中声明:

struct proc_info_list {

unsigned int cpu_val; //处理器类型

unsigned int cpu_mask; //处理器类型掩码

unsigned long __cpu_mmu_flags; /* used by head-armv.S */

unsigned long __cpu_flush; /* used by head-armv.S */

const char *arch_name;

const char *elf_name;

unsigned int elf_hwcap;

struct proc_info_item *info;

#ifdef MULTI_CPU

struct processor *proc;

#else

void *unused;

#endif

};

虽然结构是在这里声明,但是真正的定义却是在proc-arm920.S文件的最后:

.section ".proc.info", #alloc, #execinstr //声明以下代码在.proc.info段中

 

.type __arm920_proc_info,#object

__arm920_proc_info:

.long 0x41009200 //cpu_val

.long 0xff00fff0 //cpu_mask

.long 0x00000c1e @ mmuflags

b __arm920_setup //是的!这是一条跳转指令

.long cpu_arch_name

.long cpu_elf_name

.long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT

.long cpu_arm920_info

.long arm920_processor_functions

 
 

27__proc_info_begin/__proc_info_end

__proc_info_begin是.proc.info段的起始地址,而__proc_info_end是终止地址,他们存放在head-armv.S中:

2: .long __proc_info_end

.long __proc_info_begin

在连接脚本vmlinux.lds.in文件中,他们被赋值:

__proc_info_begin = .;

*(.proc.info)

__proc_info_end = .;

 
 

28.arch.info

.arch.info段类似于.proc.info段,不过它是用来存放板子信息的,它的定义是在include/asm/mach/arch.h文件中:

struct machine_desc {

/*

* Note! The first four elements are used

* by assembler code in head-armv.S

*/

unsigned int nr; /* architecture number */ //板子ID

unsigned int phys_ram; /* start of physical ram */ //物理内存起始地址

unsigned int phys_io; /* start of physical io */ //IO空间起始地址

unsigned int io_pg_offst; /* byte offset for io

* page tabe entry */

//IO空间起始地址的虚拟地址在启动页表中的偏移

……

};

而具体MX1板子的machine_desc定义则通过宏在arch/arm/mach-XXXX/arch.c文件(或者该目录下的其他文件)中,这些宏的定义在include/asm/mach/arch.h文件中实现,通过这些宏定义了一个machine_desc:

MACHINE_START(MX1ADS, "Motorola MX1ADS")

MAINTAINER("WBSG SPS Motorola")

#ifdef CONFIG_ARCH_MX1ADS_SRAM

BOOT_MEM(0x12000000, 0x00200000, 0xf0200000)

#else

BOOT_MEM(0x08000000, 0x00200000, 0xf0200000)

//phys_ram=0x08000000phys_io=0x00200000

//io_pg_offset = ((0xf0200000)>>18)&0xfffc=0x00003c08

//右移20位获得IO空间虚拟地址在页表内偏移再乘以四(4个字节

#endif

FIXUP(mx1ads_fixup)

MAPIO(mx1ads_map_io)

INITIRQ(mx1ads_init_irq)

MACHINE_END

 
 

29__arch_info_begin/__arch_info_end

__arch_info_begin是.arch.info段的起始地址,而__arch_info_end是终止地址,他们存放在head-armv.S中:

.long __arch_info_begin

.long __arch_info_end

在连接脚本vmlinux.lds.in文件中,他们被赋值:

__arch_info_begin = .;

*(.arch.info)

__arch_info_end = .;

 
3.代码分析
 

31KERNEL ENTRY

下面是整个内核Image的入口,进入后必须满足以下条件,即r0为0,r1为板子ID,MMU和D-cache关闭,这其中r1的architecture ID是由bootloader传进来的

/*

* 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.

*/

.section ".text.init",#alloc,#execinstr //以下代码属于“.text.init”段

.type stext, #function

ENTRY(stext)

// mov r12, r0

mov r12, #0 // r12=0

#if defined(CONFIG_ARCH_MX1ADS)

mov fp, r1 @ r1 contain pointer to cmdline from bootloader

#endif //r1中包含的内核命令行指针移到fp

// for MX1ADS, we don't pass this from bootloader, so we'll set it here

#if defined(CONFIG_ARCH_MX1ADS)

mov r1, #MACH_TYPE_MX1ADS

#endif //此时,r1=0x000000a0

 

mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode

msr cpsr_c, r0 @ and all irqs disabled

//将模式设置为svc模式,并禁止IRQ和FIQ

 

bl __lookup_processor_type //检查处理器类型

//如果处理器有效:

//r8 = __cpu_mmu_flags, r8 = 0x00000c1e

//r9 =处理器ID

r10指向处理器结构

teq r10, #0 @ invalid processor? //如果是无效处理器

moveq r0, #'p' @ yes, error 'p' //打印“p”和出错信息

beq __error

bl __lookup_architecture_type //检查板子类型

//如果板子有效:

// r5=物理内存的起始地址, r5 = 0x08000000

// r6=IO空间的起始物理地址 r6=0x00200000

// r7=IO空间虚拟地址在页表中的偏移 r7=0x00003c08

teq r7, #0 @ invalid architecture? //如果是无效板子

moveq r0, #'a' @ yes, error 'a' //打印“a”和出错信息

beq __error

bl __create_page_tables //建立页表

//此时页表建立完毕,

//r1=板子ID,

//r4=页表地址 (stext-16K)

//r9=处理器ID,

//r10指向处理器结构

adr lr, __ret @ return address //将返回地址存放在lr中

add pc, r10, #12 @ initialise processor

@ (return control reg)

//跳转到处理器结构+12的位置,参看.proc.info段可以知

//道,这里是一条跳转指令“b __arm920_setup”,因此再

//跳转到proc-arm920.S中的__arm920_setup函数入口处

 

32__arm920_setup

__arm920_setup函数在proc-arm920.S文件中,在页表建立起来之后,此函数进行一些开启MMU之前的初始化操作

 

.section ".text.init", #alloc, #execinstr

 

__arm920_setup:

mov r0, #0

mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4

//使无效整个I-cache和整个D-cache

mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4

//将Write Buffer中的数据写进内存

mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4

//使无效整个I-TLB和整个D-TLB

mcr p15, 0, r4, c2, c0 @ load page table pointer

//将r4(页表地址)写进c2(页表基址寄存器)

mov r0, #0x1f @ Domains 0, 1 = client

mcr p15, 0, r0, c3, c0 @ load domain access register

//设置Domain0、Domain1和Domain2的访问权限

mrc p15, 0, r0, c1, c0 @ get control register v4

//将控制寄存器(control register)的值送给r0

/*

* Clear out 'unwanted' bits (then put them in if we need them)

*/

@ VI ZFRS BLDP WCAM

bic r0, r0, #0x0e00

bic r0, r0, #0x0002

bic r0, r0, #0x000c

bic r0, r0, #0x1000 @ ...0 000. .... 000.

/*

* Turn on what we want

*/

orr r0, r0, #0x0031

orr r0, r0, #0x2100 @ ..1. ...1 ..11 ...1

 

#ifdef CONFIG_CPU_ARM920_D_CACHE_ON

orr r0, r0, #0x0004 @ .... .... .... .1..

#endif

#ifdef CONFIG_CPU_ARM920_I_CACHE_ON

orr r0, r0, #0x1000 @ ...1 .... .... ....

#endif

//修改r0的某些位,使它的后16位为:XX1I 0001 XX11 0D01

//其中I表示根据CONFIG_CPU_ARM920_I_CACHE_ON设定

//D表示根据CONFIG_CPU_ARM920_D_CACHE_ON设定

//X表示不变,1表示置位,0表示清位

//具体含义如下:

//M(bit0)=1,打开MMU

//A(bit1)=0,关闭Alignment checking

//C(bit2)=D,D-cache打开/关闭

//W(bit3)=0,关闭Write Buffer

//P(bit4)=1,Exception handler进入32-bit模式

//D(bit5)=1,关闭32-bit address exception checking

//L(bit6)=X,选择Early Abort模式或者Late Abort模式

//B(bit7)=X,Little Endian/Big Endian模式

//S(bit8)=1,System Protection Bit

//R(bit9)=0,Rom Protection Bit

//F(bit10)=0,Implementation Defined

//Z(bit11)=0,关闭Branch prediction

//I(bit12)=I,I-cache打开/关闭

//V(bit13)=1,选择High exception vector

mov pc, lr //返回,跳转到head-armv.S的__ret处

 

33__ret

在proc-arm920.S中的__arm920_setup函数进行过一些启动MMU之前的初始化工作后,根据lr寄存器中的值跳转到__ret处执行,这里做了三件事:

 

1. 首先将__switch_data处的值(即__mmap_switched)作为返回值存放在lr寄存器中

2. 开启MMU

3. 最后返回(即跳转到__mmap_switched处)

请注意!__switch_data中保存的值(也就是__mmap_switched)是一个映像地址(也就是虚拟地址),也就是说,PC的值从此处由物理地址的值跳到内核空间(0xCXXXXXXX)

.type __switch_data, %object

__switch_data: .long __mmap_switched

.long SYMBOL_NAME(compat)

.long SYMBOL_NAME(__bss_start)

.long SYMBOL_NAME(_end)

.long SYMBOL_NAME(processor_id)

.long SYMBOL_NAME(__machine_arch_type)

.long SYMBOL_NAME(cr_alignment)

.long SYMBOL_NAME(init_task_union)+8192

#ifdef CONFIG_ARCH_MX1ADS

.long SYMBOL_NAME(cmdline_from_bootloader)

//这是为MX1板子定义的从bootloader传来的参数地址

#endif

 

.type __ret, %function

__ret: ldr lr, __switch_data //这里保存的是内核空间地址!

mcr p15, 0, r0, c1, c0 //将r0中的值送回c1,开启MMU!!

mov r0, r0 //执行三次NOP操作,清空流水线

mov r0, r0

mov r0, r0

mov pc, lr //跳到__mmap_switched处执行

 
 

34__mmap_switched

__ret开启MMU之后,通过将__switch_data中保存的__map_switched的值跳转到此处执行,也就是从此处开始PC值转为0xCXXXXXXX

/*

* This code follows on after the page

* table switch and jump above.

*

* r0 = processor control register

* r1 = machine ID

* r9 = processor ID

*/

.align 5

__mmap_switched:

adr r3, __switch_data + 4 //r3=__switch_data+4

ldmia r3, {r2, r4, r5, r6, r7, r8, sp}@ r2 = compat

@ sp = stack pointer

//r2=compat

//r4=bss_start=.bss段的起始地址

//r5=_end=.bss段的终止地址

//r6=processor_id=保存处理器ID的地址

//r7=__machine_arch_type=保存板子ID的地址

//r8=cr_alignment

//sp=initial_task+8192,由task_union结构可知,这是init进程的堆栈

str r12, [r2] //将r12中的值(0)存进compat

#ifdef CONFIG_ARCH_MX1ADS

mov r12, fp @ fp/r11 gets used below (it originally contain @ pointer to cmdline from bootloader)

#endif

//在内核入口的一开始,r1中的包含指向内核命令行的指针被送到fp寄存器中,//现在将它送给r12

 

mov fp, #0 @ Clear BSS (and zero fp)

1: cmp r4, r5 //将整个.bss段清零

strcc fp, [r4],#4

bcc 1b

 

str r9, [r6] @ Save processor ID //保存处理器ID

str r1, [r7] @ Save machine type //保存板子ID

#ifdef CONFIG_ARCH_MX1ADS

/* now save a pointer to the cmdline_from_bootloader */

adr r3, __switch_data + 32 @ cmdline_from_bootloader

//r3=__switch_data+32=cmdline_from_bootloader的地址

ldmia r3, {r4} @ r4 = address of above

//r4=[r3]=cmdline_from_bootloader

str r12, [r4] // [r4]=r12

//将指向内核命令行的指针赋给cmdline_from_bootloader

#endif

#ifdef CONFIG_ALIGNMENT_TRAP

orr r0, r0, #2 @ ...........A.

#endif

bic r2, r0, #2 @ Clear 'A' bit

stmia r8, {r0, r2} @ Save control register values

b SYMBOL_NAME(start_kernel) //跳到start_kernel处执行

 
 
 
 

35__lookup_processor_type

36__lookup_architecture_type

__lookup_processor_type例程用于检查当前的处理器是否有效,它没有输入,使用了r5,r6,r7三个寄存器,返回r8=__cpu_mmu_flags,r9=处理器ID,r10指向处理器结构

__lookup_architecture_type用于检查当前的板子是否有效,它需要输入r1为板子ID,使用了r2, r3, r4三个寄存器,返回r5=物理内存的起始地址,r6=IO空间的起始地址,r7=IO空间虚拟地址的段号

 

/*

* 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.

*

* Returns:

* r5, r6, r7 corrupted

* r8 = page table flags

* r9 = processor ID

* r10 = pointer to processor structure

*/

__lookup_processor_type: //__lookup_processor_type函数入口

adr r5, 2f // r5=__proc_info_end的物理地址,此处伪指令adr会被

//编译成add r5, pc , #xx获得运行时地址

ldmia r5, {r7, r9, r10} // r7=__proc_info_end

// r9=__proc_info_start

// r10=__proc_info_end的映像地址

sub r5, r5, r10 @ convert addresses // r5=物理地址减映像地址的差

add r7, r7, r5 @ to our address space

add r10, r9, r5 //r7=__proc_info_end对应的物理地址

//r10=__proc_info_start对应的物理地址

mrc p15, 0, r9, c0, c0 @ get processor id //r9=处理器ID

1: ldmia r10, {r5, r6, r8} @ value, mask, mmuflags

// r5=cpu_val,r6=cpu_mask,r8=__cpu_mmu_flags

// r5=0x41009200, r6=0xff00fff0, r8=0x00000c1e

and r6, r6, r9 @ mask wanted bits

teq r5, r6 //比较两个处理器ID是否相同,如果相同返回

moveq pc, lr

add r10, r10, #36 @ sizeof(proc_info_list)

cmp r10, r7 //如果不同尝试下一个处理器结构

blt 1b //直到__proc_info_end

mov r10, #0 @ unknown processor

mov pc, lr

 

/*

* Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for

* more information about the __proc_info and __arch_info structures.

*/

2: .long __proc_info_end //.proc.info段的终止映像地址

.long __proc_info_begin //.proc.info段的起始映像地址

.long 2b //__proc_info_end的映像地址

.long __arch_info_begin //.arch.info段的起始映像地址

.long __arch_info_end //.arch.info段的终止映像地址

/*

* 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:

* r2, r3, r4 corrupted

* r5 = physical start address of RAM

* r6 = physical address of IO

* r7 = byte offset into page tables for IO

*/

__lookup_architecture_type: //__lookup_architecture_type函数入口

adr r4, 2b // r4=__proc_info_end的物理地址

ldmia r4, {r2, r3, r5, r6, r7} @ throw away r2, r3

// r2=__proc_info_end,丢弃

// r3=__proc_info_start,丢弃

// r5=__proc_info_end的映像地址

// r6=__arch_info_begin

// r7=__arch_info_end

sub r5, r4, r5 @ convert addresses // r5=物理地址减映像地址的差

add r4, r6, r5 @ to our address space

add r7, r7, r5 //r7=__proc_info_end对应的物理地址

//r10=__proc_info_start对应的物理地址

//同__lookup_processor_type一样,将映像地址转化为物理地址

1: ldr r5, [r4] @ get machine type // r5=板子ID r5=0x000000a0

teq r5, r1 //比较两个板子ID

beq 2f //相同则跳到2

add r4, r4, #SIZEOF_MACHINE_DESC

cmp r4, r7

blt 1b

mov r7, #0 @ unknown architecture

mov pc, lr

2: ldmib r4, {r5, r6, r7} @ found, get results

// r5=物理内存的起始地址, r5=0x08000000

// r6=IO空间的起始地址 r6=0x00200000

// r7=IO空间虚拟地址的段号 r7=0x00003c08

mov pc, lr //返回

 
 

37__create_page_tables

__create_page_tables用来建立初始化一级页表,此时各个寄存器的情况如下:

r5=物理内存的起始地址 r5=0x08000000

r6=IO空间的起始地址 r6=0x00200000

r7=IO空间虚拟地址的段号 r7=0x00003c08

r8 = __cpu_mmu_flags r8=0x00000c1e

r9 =处理器ID

r10指向处理器结构

r0,r1,r2,r3和r4中没有有效数据

__create_page_tables:

pgtbl r4, r5 @ page table address // r4=stext-16K(即页表地址)

// r4=0x08004000

/*

* Clear the 16K level 1 swapper page table

*/ //将页表空间(stext-16K到stext)的16K空间清零

mov r0, r4 // r0 = r4 = stext-16K ,r4=0x08004000

mov r3, #0 // r3 = 0

add r2, r0, #0x4000 // r2 = r0+16K = stext,r2=0x08008000

1: str r3, [r0], #4 //循环清零页表空间

str r3, [r0], #4

str r3, [r0], #4

str r3, [r0], #4

teq r0, r2

bne 1b

//此时:r0 = r2 = stext ,r4 = stext-16K,r3 = 0

/*

* Create identity mapping for first MB of kernel to

* cater for the MMU enable. This identity mapping

* will be removed by paging_init()

*/

krnladr r2, r4, r5 @ start of kernel

add r3, r8, r2 @ flags + kernel base

str r3, [r4, r2, lsr #18] @ identity mapping

//建立恒等映射(为了开启MMU前后地址空间的一致性),即:

//映射虚拟空间:0x08000000-0x08100000

//到物理空间:0x08000000-0x08100000

// r2= stext&0xfff00fff =内核物理空间起始地址所在的段的起始地址

// r8 =页表一级描述符标志位,

// r3 = r2 + r8 =内核物理空间所在段的一级描述符

// r2 = 0x08000000

// r8= 0x00000c1e

// r3= 0x08000c1e

//将r2(内核空间起始物理地址)右移20位得到内核起始地址在页//表内的索引,再将它左移两位(即乘以4,页表内每个描述符占四//个字节)后加上页表基址就得到了内核空间起始地址所在段的一级//描述符的地址,r3存入此处

// [r4, r2, lsr #18]=0x08004200

/*

* Now setup the pagetables for our kernel direct

* mapped region. We round TEXTADDR down to the

* nearest megabyte boundary.

*/

//此时:

// r4=页表物理地址,r4=0x08004000

//r2=内核空间起始物理地址,r2 = 0x08000000

//r3=物理内核空间所在段的一级描述符,r3= 0x08000c1e

add r0, r4, #(TEXTADDR & 0xff000000) >> 18 @ start of kernel

bic r2, r3, #0x00f00000

str r2, [r0] @ PAGE_OFFSET + 0MB

//映射虚拟空间:0xC0000000-0xC0100000

//到物理空间:0x0800000-0x08100000

//由于 TEXTADDR=0xC0008000

// r0=0x08004000+0x00003000=0x08007000

// r2=0x08000c1e

建立此段映射的原因是某些内核空间起始地址非16M对齐的板子将一些数据(比如内核参数之类)存放在内核空间之前但在16M对齐之后的1MB内的位置,比如内核空间起始地址为0x00200000,而在0x0000000-0x00100000处有数据。这段映射的目的就是在启动MMU之后使内核能够找到这些数据,而在内核空间起始地址16M对齐的板子中,这段映射将在下一步映射内核空间第一个MB时被覆盖

 

add r0, r0, #(TEXTADDR & 0x00f00000) >> 18

str r3, [r0], #4 @ KERNEL + 0MB

// r0=0x08007000

// r3=0x08000c1e

//映射内核空间的第一个MB,即:

//映射虚拟空间:0xC0000000-0xC0100000

//到物理空间:0x0800000-0x08100000 (覆盖上一次映射)

add r3, r3, #1 << 20

str r3, [r0], #4 @ KERNEL + 1MB

// r0=0x08007004

// r3=0x08100c1e

//映射内核空间的第二个MB ,即:

//映射虚拟空间:0xC0100000-0xC0200000

//到物理空间:0x0810000-0x08200000

add r3, r3, #1 << 20

str r3, [r0], #4 @ KERNEL + 2MB

//映射内核空间的第三个MB

add r3, r3, #1 << 20

str r3, [r0], #4 @ KERNEL + 3MB

//映射内核空间的第三个MB

 

/*

* Ensure that the first section of RAM is present.

* we assume that:

* 1. the RAM is aligned to a 32MB boundary

* 2. the kernel is executing in the same 32MB chunk

* as the start of RAM.

*/

bic r0, r0, #0x01f00000 >> 18 @ round down

and r2, r5, #0xfe000000 @ round down

add r3, r8, r2 @ flags + rambase

str r3, [r0]

// r0=0x08007000

// r2=0x08000c1e

//映射虚拟空间:0xC0000000-0xC0100000

//到物理空间:0x08000000-0x08100000

//确认物理内存的第一个段存在

 

bic r8, r8, #0x0c @ turn off cacheable

@ and bufferable bits

//更改一级描述符的标志位,去除cacheble和bufferable位

#ifdef CONFIG_DEBUG_LL

//CONFIG_DEBUG_LL宏的定义表明low level debugging的开启

//开启了ll表明可以使用printch,printascii和printhexX等直接操作

//串口的调试函数,为此,必须在内核正式页表建立之前建立临时的

//IO空间映射以满足在MMU开启之后这些函数能正常的工作,这

//里的代码就是实现这个目标

/*

* Map in IO space for serial debugging.

* This allows debug messages to be output

* via a serial console before paging_init.

*/

add r0, r4, r7

rsb r3, r7, #0x4000 @ PTRS_PER_PGD*sizeof(long)

cmp r3, #0x0800

addge r2, r0, #0x0800

addlt r2, r0, r3

orr r3, r6, r8

1: str r3, [r0], #4

add r3, r3, #1 << 20

teq r0, r2

bne 1b

#endif

 

mov pc, lr //返回

 
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐