linux head.s 从第一条指令到start_kernel

/*

* 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/cp15.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/pgtable.h>

#if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_SEMIHOSTING)

#include CONFIG_DEBUG_LL_INCLUDE

#endif

/*

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

*/

#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)

#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000

#error KERNEL_RAM_VADDR must start at 0xXXXX8000

#endif

#ifdef CONFIG_ARM_LPAE

/* LPAE requires an additional page for the PGD */

#define PG_DIR_SIZE 0x5000

#define PMD_ORDER 3

#else

#define PG_DIR_SIZE 0x4000

#define PMD_ORDER 2

#endif

.globl swapper_pg_dir

.equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE

.macro pgtbl, rd, phys

add \rd, \phys, #TEXT_OFFSET

sub \rd, \rd, #PG_DIR_SIZE

.endm

/*

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

*/

.arm

__HEAD

ENTRY(stextxxx)

b stext

ARM_BE8(setend be ) @ ensure we are in BE8 mode

THUMB( badr r9, 1f ) @ Kernel is always entered in ARM.

THUMB( bx r9 ) @ If this is a Thumb-2 kernel,

THUMB( .thumb ) @ switch to Thumb now.

THUMB(1: )

#ifdef CONFIG_ARM_VIRT_EXT

bl __hyp_stub_install

#endif

@ ensure svc mode and all interrupts masked

safe_svcmode_maskall r9

mrc p15, 0, r9, c0, c0 @ get processor id

bl __lookup_processor_type @ r5=procinfo r9=cpuid

movs r10, r5 @ invalid processor (r5=0)?

THUMB( it eq ) @ force fixup-able long branch encoding

beq __error_p @ yes, error 'p'

bne __error_p

#ifdef CONFIG_ARM_LPAE

mrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0

and r3, r3, #0xf @ extract VMSA support

cmp r3, #5 @ long-descriptor translation table format?

THUMB( it lo ) @ force fixup-able long branch encoding

blo __error_lpae @ only classic page table format

#endif

#ifndef CONFIG_XIP_KERNEL

adr r3, 2f

ldmia r3, {r4, r8}

sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)

add r8, r8, r4 @ PHYS_OFFSET

#else

ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case

#endif

/*

* r1 = machine no, r2 = atags or dtb,

* r8 = phys_offset, r9 = cpuid, r10 = procinfo

*/

bl __vet_atags

#ifdef CONFIG_SMP_ON_UP

bl __fixup_smp

#endif

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

bl __fixup_pv_table

#endif

bl __create_page_tables

/*

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

* above.

*

* The processor init function will be called with:

* r1 - machine type

* r2 - boot data (atags/dt) pointer

* r4 - translation table base (low word)

* r5 - translation table base (high word, if LPAE)

* r8 - translation table base 1 (pfn if LPAE)

* r9 - cpuid

* r13 - virtual address for __enable_mmu -> __turn_mmu_on

*

* On return, the CPU will be ready for the MMU to be turned on,

* r0 will hold the CPU control register value, r1, r2, r4, and

* r9 will be preserved. r5 will also be preserved if LPAE.

*/

ldr r13, =__mmap_switched @ address to jump to after

@ mmu has been enabled

badr lr, 1f @ return (PIC) address

#ifdef CONFIG_ARM_LPAE

mov r5, #0 @ high TTBR0

mov r8, r4, lsr #12 @ TTBR1 is swapper_pg_dir pfn

#else

mov r8, r4 @ set TTBR1 to swapper_pg_dir

#endif

ldr r12, [r10, #PROCINFO_INITFUNC]

add r12, r12, r10

ret r12

1: b __enable_mmu

ENDPROC(stextxxx)

.align 2

out_num_str:

.asciz "reg: "

newline_str:

.asciz "\n"

ENTRY(print_out_num)

push {r0-r12, lr}

//ldr r0, =out_num_str

adr r0, out_num_str

bl printascii

pop {r0-r12, lr}

mov pc, lr

ENDPROC(print_out_num)

ENTRY(print_newline)

push {r0-r12, lr}

//ldr r0, =newline_str

adr r0, newline_str

bl printascii

pop {r0-r12, lr}

mov pc, lr

ENDPROC(print_newline)

/*

* 通用 16 进制打印函数

* 输入:r0 = 要打印的 32 位数值

* 作用:通过 DEBUG_LL 打印 16 进制数,不破坏任何寄存器

*/

ENTRY(print_hex)

push {r0-r12, lr} @ 保存所有寄存器 + LR

bl printhex8 @ 调用底层打印

pop {r0-r12, lr} @ 恢复现场

mov pc, lr @ 返回

ENDPROC(print_hex)

ENTRY(stext)

#ifdef CONFIG_ARM_VIRT_EXT

bl __hyp_stub_install

#endif

@ ensure svc mode and all interrupts masked

safe_svcmode_maskall r9

mrc p15, 0, r9, c0, c0 @ get processor id

bl __lookup_processor_type @ r5=procinfo r9=cpuid

movs r10, r5 @ invalid processor (r5=0)?

beq __error_p @ yes, error 'p'

bl print_out_num

mov r9, pc

mov r0, r9

bl print_hex

bl print_newline

bl print_out_num @ 打印 out_num:

ldr r0, =#0x12345678 @ 要打印的数字

bl print_hex @ 你之前的打印16进制

bl print_newline @ 换行

//push {r0-r12}

//@bl .

//adr r0, out_num_str

//bl printascii

//pop {r0-r12}

adr r3, 2f

//f = forward(向前 / 往代码后面找)

//b = backward(向后 / 往代码前面找)

//往后找到标号的绝对物理运行地址 与此处存储的虚拟编译链接地址相减 同一个位置

//以得到物理地址与虚拟地址的偏移大小

bl print_out_num

mov r0, r3

bl print_hex

bl print_newline

ldmia r3, {r4, r8}

bl print_out_num

mov r0, r4

bl print_hex

bl print_newline

bl print_out_num

mov r0, r8

bl print_hex

bl print_newline

//整个启动流程只允许使用r0,其他寄存器后面要用的 不能随意打乱!!!

sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)

//整个注释不大对 应该是 物理地址 - 虚拟地址,得到物理地址与虚拟地址的偏移

//就是说是 标号2的物理地址[680081a4] 与 .[虚拟地址][800081a4] 相减 (同一个位置的)

//得到的是一个负数 0xE8000000 说明物理地址与虚拟地址的偏差是offset[0xE8000000]

add r8, r8, r4 @ PHYS_OFFSET

//r8 存的是 0x80000000 即PAGE_OFFSET 虚拟地址的基地址

//前面已经算出物理地址与虚拟基地址的偏差是 0xE8000000

//此处 物理基地址 - 虚拟基地址 = 0xE8000000

//物理基地址 = 虚拟基地址[高] + 0xE8000000[负数] = 0x68000000

/*

* r1 = machine no, r2 = atags or dtb,

* r8 = phys_offset, r9 = cpuid, r10 = procinfo

*/

bl __vet_atags

#ifdef CONFIG_SMP_ON_UP

bl __fixup_smp

#endif

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

bl __fixup_pv_table

#endif

bl print_out_num

ldr r0, =#0x5555aaaa

bl print_hex

bl print_newline

bl __create_page_tables

/*

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

* above.

*

* The processor init function will be called with:

* r1 - machine type

* r2 - boot data (atags/dt) pointer

* r4 - translation table base (low word)

* r5 - translation table base (high word, if LPAE)

* r8 - translation table base 1 (pfn if LPAE)

* r9 - cpuid

* r13 - virtual address for __enable_mmu -> __turn_mmu_on

*

* On return, the CPU will be ready for the MMU to be turned on,

* r0 will hold the CPU control register value, r1, r2, r4, and

* r9 will be preserved. r5 will also be preserved if LPAE.

*/

ldr r13, =__mmap_switched @ address to jump to after

@ mmu has been enabled

badr lr, 1f @ return (PIC) address

mov r8, r4 @ set TTBR1 to swapper_pg_dir

ldr r12, [r10, #PROCINFO_INITFUNC]

add r12, r12, r10

ret r12

1: b __enable_mmu

ENDPROC(stext)

.ltorg

#ifndef CONFIG_XIP_KERNEL

2: .long .

.long PAGE_OFFSET

#endif

/*

* 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 = phys_offset, r9 = cpuid, r10 = procinfo

*

* Returns:

* r0, r3, r5-r7 corrupted

* r4 = physical page table address

*/

__create_page_tables:

push {lr}

bl print_out_num

mov r0, r8

bl print_hex

bl print_newline

//pgtbl r4, r8 @ page table address

//直接展开吧,看得蛋疼

//.macro pgtbl, rd, phys 0x68000000

add r4, r8, #TEXT_OFFSET //0x00008000

sub r4, r4, #PG_DIR_SIZE //0x00004000

//.endm

//内核页表存储的位置 r8物理基地址 + 文本偏移之后的下面16k

//即 0x68004000-0x68008000 区间

//bl print_out_num

//mov r0, r8

//bl print_hex

//bl print_newline

//bl print_out_num

//mov r0, #TEXT_OFFSET

//bl print_hex

//bl print_newline

//bl print_out_num

//mov r0, #PG_DIR_SIZE

//bl print_hex

//bl print_newline

mov r0, r4

bl print_out_num

bl print_hex

bl print_newline

bl print_out_num

ldr r0, =#0x12345678

bl print_hex

bl print_newline

bl print_newline

bl print_out_num

ldr r0, =#0xaaaaaaaa

bl print_hex

bl print_newline

//pop { lr}

/*

* Clear the swapper page table

*/

mov r0, r4

mov r3, #0

add r6, r0, #PG_DIR_SIZE

1: str r3, [r0], #4

str r3, [r0], #4

str r3, [r0], #4

str r3, [r0], #4

teq r0, r6

bne 1b

//标准的清除代码,清除16K的页表区间

#if 0

#ifdef CONFIG_ARM_LPAE

/*

* Build the PGD table (first level) to point to the PMD table. A PGD

* entry is 64-bit wide.

*/

mov r0vvvv, r4

//没有使用 最高兴看到这个

kkkkkkkkkkkkkk

add r3, r4, #0x1000 @ first PMD table address

orr r3, r3, #3 @ PGD block type

mov r6, #4 @ PTRS_PER_PGD

mov r7, #1 << (55 - 32) @ L_PGD_SWAPPER

1:

#ifdef CONFIG_CPU_ENDIAN_BE8

str r7, [r0], #4 @ set top PGD entry bits

str r3, [r0], #4 @ set bottom PGD entry bits

#else

str r3, [r0], #4 @ set bottom PGD entry bits

str r7, [r0], #4 @ set top PGD entry bits

#endif

add r3, r3, #0x1000 @ next PMD table

subs r6, r6, #1

bne 1b

add r4, r4, #0x1000 @ point to the PMD tables

#ifdef CONFIG_CPU_ENDIAN_BE8

add r4, r4, #4 @ we only write the bottom word

#endif

#endif

#endif

ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags

//0x00000c0e

/*

* Create identity mapping to cater for __enable_mmu.

* This identity mapping will be removed by paging_init().

*/

adr r0, __turn_mmu_on_loc

ldmia r0, {r3, r5, r6}

sub r0, r0, r3 @ virt->phys offset

//这个注释反了,应该是物理地址到虚拟地址的偏移

add r5, r5, r0 @ phys __turn_mmu_on __turn_mmu_on 的 物理地址 68008480

add r6, r6, r0 @ phys __turn_mmu_on_end 结束物理地址 680084a0

#if 0

bl print_out_num

mov r0, #SECTION_SHIFT

bl print_hex

bl print_newline

bl print_out_num

mov r0, #PMD_ORDER

bl print_hex

bl print_newline

bl print_out_num

mov r0, r7

bl print_hex

bl print_newline

bl print_out_num

mov r0, r5

bl print_hex

bl print_newline

bl print_out_num

mov r0, r6

bl print_hex

bl print_newline

#endif

pop { lr }

mov r5, r5, lsr #SECTION_SHIFT

//SECTION_SHIFT = 20

mov r6, r6, lsr #SECTION_SHIFT

1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base

//r3 = 段表项 (有效、可访问、带缓存的段映射)

str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping

//PMD_ORDER = 2

cmp r5, r6

addlo r5, r5, #1 @ next section

blo 1b

//Map our RAM from the start to the end of the kernel .bss section.

//r4 0x68004000 页表的基地址

// 0x80000000 即PAGE_OFFSET 虚拟地址的基地址

add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)

//先定位到0x80000000地址的页表项地址 0x68004000 + 0x2000 = 0x68006000 2G位置 刚好占一半页表

ldr r6, =(_end - 1) //内核结束位置

orr r3, r8, r7

//构造第一段映射: 0x80000000 → 0x68000000 r8=0x68000000

add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)

//定位到内核结束位置的虚拟地址的页表项位置

1: str r3, [r0], #1 << PMD_ORDER

add r3, r3, #1 << SECTION_SHIFT

//地址加了1M, 属性还没有变

cmp r0, r6

bls 1b

//这一段就是映射内核区 从0x80000000一直到_end

//映射到0x68000000 -- end

//这样的话,内核区就都开启了段映射

//物理内存只有一份:0x68000000

//恒等映射:0x6800xxxx → 0x6800xxxx(保命跳板)

//内核映射:0x8000xxxx → 0x6800xxxx(正式运行)

//两个映射互补、不重复、缺一不可

#if 0

#ifdef CONFIG_XIP_KERNEL

/*

* Map the kernel image separately as it is not located in RAM.

*/

#define XIP_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)

mov r3, pcccccc

//没有使用 最高兴看到这个

mov r3, r3, lsr #SECTION_SHIFT

orr r3, r7, r3, lsl #SECTION_SHIFT

add r0, r4, #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)

str r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!

ldr r6, =(_edata_loc - 1)

add r0, r0, #1 << PMD_ORDER

add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)

1: cmp r0, r6

add r3, r3, #1 << SECTION_SHIFT

strls r3, [r0], #1 << PMD_ORDER

bls 1bccccc

#endif

#endif

/*

* Then map boot params address in r2 if specified.

* We map 2 sections in case the ATAGs/DTB crosses a section boundary.

*/

#if 0

mov r0, r2, lsr #SECTION_SHIFT

movs r0vvvvvvvvvv, r0, lsl #SECTION_SHIFT

subne r3, r0, r8

addne r3, r3, #PAGE_OFFSET

addne r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)

orrne r6, r7, r0

strne r6, [r3], #1 << PMD_ORDER

addne r6, r6, #1 << SECTION_SHIFT

strne r6, [r3]

#endif

/*

* Then map boot params address in r2 if specified.

* We map 2 sections in case the ATAGs/DTB crosses a section boundary.

*

* 功能:映射启动参数(ATAGs / DTB 设备树)到内核虚拟地址空间

* 原因:内核启动后必须能读取硬件信息,所以必须做虚拟地址映射

* 一次性映射 2 个 1MB 段:防止 DTB 刚好跨在段边界上导致访问异常

*

* 输入:

* r2 = DTB/ATAGs 物理地址

* r8 = 内存物理基地址 0x68000000

* r4 = 页表基地址 0x68004000

* r7 = 段权限 0xC0E

* PAGE_OFFSET = 0x80000000

*/

mov r0, r2, lsr #SECTION_SHIFT @r0 = DTB物理地址 >> 20,取出 [段号]

movs r0, r0, lsl #SECTION_SHIFT @r0 = 段号 << 20,得到 [1MB对齐的物理段基址]

@movs 会更新标志位:如果 r2=0(无DTB),则 Z=1,后面不执行

@ ================= 下面所有指令都带 ne:只有 r2 != 0 才执行 =================

subne r3, r0, r8 @ r3 = DTB物理段基址 - 内存物理基地址 = [偏移量]

addne r3, r3, #PAGE_OFFSET @ r3 = 偏移量 + 0x80000000 = DTB对应的 [虚拟地址]

addne r3, r4, r3, lsr #18 @ r3 = 页表基地址 + (虚拟地址 >>18)

@ 得到:DTB虚拟地址对应的 [页表项地址]

orrne r6, r7, r0 @ r6 = 物理段基地址 + 权限(0xC0E) = 完整段表项

strne r6, [r3], #1 << PMD_ORDER @ 把段表项写入页表,映射第一个1MB段,r3 +=4

addne r6, r6, #1 << SECTION_SHIFT @ 下一个1MB物理段 (地址+1M,权限不变)

strne r6, [r3] @ 写入页表,映射第二个1MB段

#if 0

#if defined(CONFIG_ARM_LPAE) && defined(CONFIG_CPU_ENDIAN_BE8)

sub r4, r4xxxx, #4 @ Fixup page table pointer

@ for 64-bit descriptors

#endif

#endif

#ifdef CONFIG_DEBUG_LL

#if !defined(CONFIG_DEBUG_ICEDCC) && !defined(CONFIG_DEBUG_SEMIHOSTING)

/*

* Map in IO space for serial debugging.

* This allows debug messages to be output

* via a serial console before paging_init.

*/

#if 0

addruart r7, r3, r0

mov r3, r3, lsr #SECTION_SHIFT

mov r3, r3, lsl #PMD_ORDER

add r0, r4, r3

mov r3, r7, lsr #SECTION_SHIFT

ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags

orr r3, r7, r3, lsl #SECTION_SHIFT

#endif

#ifdef CONFIG_ARM_LPAE

mov r7, #1 << (54 - 32) @ XN

#ifdef CONFIG_CPU_ENDIAN_BE8

str r7, [r0], #4

str r3, [r0], #4

#else

str r3, [r0], #4

str r7cccc, [r0], #4

#endif

#else

#if 0

orr r3, r3, #PMD_SECT_XN

str r3, [r0], #4

#endif

#endif

#else /* CONFIG_DEBUG_ICEDCC || CONFIG_DEBUG_SEMIHOSTING */

/* we don't need any serial debugging mappings */

ldr r7bbbbb, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags

#endif

#if 0

#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

*/

add r0xxxxxcv, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ORDER)

orr r3, r7, #0x7c000000

str r3, [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.

*/

add r0xxxxx, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ORDER)

orr r3, r7, #0x02000000

str r3, [r0]

add r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER)

str r3, [r0]

#endif

#endif

#endif

/*

* 功能:映射串口 UART 物理地址到虚拟地址

* 用途:内核启动后可以用虚拟地址打印串口日志(early printk)

* 特点:IO 外设,必须:禁用缓存 + 禁止执行(XN)

*/

addruart r7, r3, r0 @ 宏定义:获取 UART 物理地址

@ r7 = UART 物理基地址

@ r3 = UART 物理基地址(备用)

mov r3, r3, lsr #SECTION_SHIFT @ r3 = UART物理地址 >> 20 → 取段号

mov r3, r3, lsl #PMD_ORDER @ r3 = 段号 << 2 → 页表内偏移(×4)

add r0, r4, r3 @ r0 = 页表基地址(r4) + 偏移

@ 得到:UART 虚拟地址对应的【页表项指针】

mov r3, r7, lsr #SECTION_SHIFT @ r3 = UART物理地址 >>20 → 段号

ldr r7, [r10, #PROCINFO_IO_MMUFLAGS]

@ r7 = IO 外设权限标志

@ 特点:无缓存、不可执行、特权访问

orr r3, r7, r3, lsl #SECTION_SHIFT

@ 拼接段表项:

@ r3 = UART物理段基地址 + IO权限标志

#ifdef CONFIG_ARM_LPAE

@ 32位 + LPAE 大物理地址扩展(特殊处理,不影响核心理解)

mov r7, #1 << (54 - 32) @ XN (eXecute Never 禁止执行)

#ifdef CONFIG_CPU_ENDIAN_BE8

str r7, [r0], #4

str r3, [r0], #4

#else

str r3, [r0], #4

str r7, [r0], #4

#endif

#else

@ 普通 ARM 32位 段映射

orr r3, r3, #PMD_SECT_XN @ 加上【禁止执行】位 (XN=1),外设绝对不能执行!

str r3, [r0], #4 @ 把段表项写入页表 → UART 映射完成!

#endif

#if 0

#ifdef CONFIG_ARM_LPAE

sub r4, r4, #0x1000 @ point to the PGD table

#endif

#endif

ret lr

ENDPROC(__create_page_tables)

.ltorg

.align

__turn_mmu_on_loc:

.long .

.long __turn_mmu_on

.long __turn_mmu_on_end

#if defined(CONFIG_SMP)

.text

.arm

ENTRY(secondary_startup_arm)

THUMB( badr r9, 1f ) @ Kernel is entered in ARM.

THUMB( bx r9 ) @ If this is a Thumb-2 kernel,

THUMB( .thumb ) @ switch to Thumb now.

THUMB(1: )

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.

*/

ARM_BE8(setend be) @ ensure we are in BE8 mode

#ifdef CONFIG_ARM_VIRT_EXT

bl __hyp_stub_install_secondary

#endif

safe_svcmode_maskall r9

mrc p15, 0, r9, c0, c0 @ get processor id

bl __lookup_processor_type

movs r10, r5 @ invalid processor?

moveq r0, #'p' @ yes, error 'p'

THUMB( it eq ) @ force fixup-able long branch encoding

beq __error_p

/*

* Use the page tables supplied from __cpu_up.

*/

adr r4, __secondary_data

ldmia r4, {r5, r7, r12} @ address to jump to after

sub lr, r4, r5 @ mmu has been enabled

add r3, r7, lr

ldrd r4, [r3, #0] @ get secondary_data.pgdir

ARM_BE8(eor r4, r4, r5) @ Swap r5 and r4 in BE:

ARM_BE8(eor r5, r4, r5) @ it can be done in 3 steps

ARM_BE8(eor r4, r4, r5) @ without using a temp reg.

ldr r8, [r3, #8] @ get secondary_data.swapper_pg_dir

badr lr, __enable_mmu @ return address

mov r13, r12 @ __secondary_switched address

ldr r12, [r10, #PROCINFO_INITFUNC]

add r12, r12, r10 @ initialise processor

@ (return control reg)

ret r12

ENDPROC(secondary_startup)

ENDPROC(secondary_startup_arm)

/*

* r6 = &secondary_data

*/

ENTRY(__secondary_switched)

ldr sp, [r7, #12] @ get secondary_data.stack

mov fp, #0

b secondary_start_kernel

ENDPROC(__secondary_switched)

.align

.type __secondary_data, %object

__secondary_data:

.long .

.long secondary_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. All these registers need to be preserved by the

* processor setup function (or set in the case of r0)

*

* r0 = cp#15 control register

* r1 = machine ID

* r2 = atags or dtb pointer

* r4 = TTBR pointer (low word)

* r5 = TTBR pointer (high word if LPAE)

* r9 = processor ID

* r13 = *virtual* address to jump to upon completion

*/

__enable_mmu:

#if defined(CONFIG_ALIGNMENT_TRAP) && LINUX_ARM_ARCH < 6

orr r0, r0, #CR_A

#else

bic r0, r0, #CR_A

#endif

#ifdef CONFIG_CPU_DCACHE_DISABLE

bic r0, r0, #CR_C

#endif

#ifdef CONFIG_CPU_BPREDICT_DISABLE

bic r0, r0, #CR_Z

#endif

#ifdef CONFIG_CPU_ICACHE_DISABLE

bic r0, r0, #CR_I

#endif

#ifdef CONFIG_ARM_LPAE

mcrr p15, 0, r4, r5, c2 @ load TTBR0

#else

mov r5, #DACR_INIT

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

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

#endif

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 (已经配置好的MMU控制寄存器值)

* r1 = machine ID 机器ID

* r2 = atags or dtb pointer 设备树/启动参数物理地址

* r9 = processor ID CPU型号ID

* r13 = *virtual* address to jump to upon completion 开启MMU后要跳去的【虚拟地址】

*

* other registers depend on the function called upon completion

*/

.align 5 @ 32字节对齐,保证函数不跨段

.pushsection .idmap.text, "ax" @ 放入恒等映射段,物理==虚拟,保证开启MMU不崩溃

ENTRY(__turn_mmu_on)

mov r0, r0 @流水线等待指令,防止前序指令未完成

instr_sync @ 指令同步屏障,确保前面指令全部执行完毕

mcr p15, 0, r0, c1, c0, 0 @ write control reg

@致命一行: 真正打开MMU硬件

@ 执行前:MMU=关闭,CPU运行在【物理地址】

@ 执行后:MMU=开启,CPU所有地址自动视为【虚拟地址】

@ PC当前值 = 物理地址 0x68008480

@ MMU开启后,CPU把这个值当成【虚拟地址】去查表

@ 依靠【恒等映射】:虚拟0x68008480 → 物理0x68008480

@ 指令无缝执行,不会跑飞,不会死机

//如果没有前面建的恒等映射,这里直接死机!

mrc p15, 0, r3, c0, c0, 0 @ read id reg

@ 读取CPU ID寄存器(延时/同步作用,保证MMU生效)

instr_sync @ 再次保证MMU完全开启,指令流水线清空

mov r3, r3 @ 延时操作,确保MMU状态稳定

mov r3, r13 @ r13 = __mmap_switched 的虚拟地址 0x8000xxxx

ret r3 @ 跳去虚拟空间!

@ 终极一跃 跳转到内核虚拟地址空间

@ 跳转地址是虚拟地址 0x8000xxxx

@ MMU查表:虚拟0x8000xxxx → 物理0x6800xxxx

@ 成功进入内核高端虚拟地址,正式开始运行Linux内核

__turn_mmu_on_end:

ENDPROC(__turn_mmu_on)

.popsection @ 退出恒等映射段

#ifdef CONFIG_SMP_ON_UP

__HEAD

__fixup_smp:

and r3, r9, #0x000f0000 @ architecture version

teq r3, #0x000f0000 @ CPU ID supported?

bne __fixup_smp_on_up @ no, assume UP

bic r3, r9, #0x00ff0000

bic r3, r3, #0x0000000f @ mask 0xff00fff0

mov r4, #0x41000000

orr r4, r4, #0x0000b000

orr r4, r4, #0x00000020 @ val 0x4100b020

teq r3, r4 @ ARM 11MPCore?

reteq lr @ yes, assume SMP

mrc p15, 0, r0, c0, c0, 5 @ read MPIDR

and r0, r0, #0xc0000000 @ multiprocessing extensions and

teq r0, #0x80000000 @ not part of a uniprocessor system?

bne __fixup_smp_on_up @ no, assume UP

@ Core indicates it is SMP. Check for Aegis SOC where a single

@ Cortex-A9 CPU is present but SMP operations fault.

mov r4, #0x41000000

orr r4, r4, #0x0000c000

orr r4, r4, #0x00000090

teq r3, r4 @ Check for ARM Cortex-A9

retne lr @ Not ARM Cortex-A9,

@ If a future SoC *does* use 0x0 as the PERIPH_BASE, then the

@ below address check will need to be #ifdefed or equivalent

@ for the Aegis platform.

mrc p15, 4, r0, c15, c0 @ get SCU base address

teq r0, #0x0 @ '0' on actual UP A9 hardware

beq __fixup_smp_on_up @ So its an A9 UP

ldr r0, [r0, #4] @ read SCU Config

ARM_BE8(rev r0, r0) @ byteswap if big endian

and r0, r0, #0x3 @ number of CPUs

teq r0, #0x0 @ is 1?

retne lr

__fixup_smp_on_up:

adr r0, 1f

ldmia r0, {r3 - r5}

sub r3, r0, r3

add r4, r4, r3

add r5, r5, r3

b __do_fixup_smp_on_up

ENDPROC(__fixup_smp)

.align

1: .word .

.word __smpalt_begin

.word __smpalt_end

.pushsection .data

.globl smp_on_up

smp_on_up:

ALT_SMP(.long 1)

ALT_UP(.long 0)

.popsection

#endif

.text

__do_fixup_smp_on_up:

cmp r4, r5

reths lr

ldmia r4!, {r0, r6}

ARM( str r6, [r0, r3] )

THUMB( add r0, r0, r3 )

#ifdef ARMEB

THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian.

#endif

THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords

THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3.

THUMB( strh r6, [r0] )

b __do_fixup_smp_on_up

ENDPROC(__do_fixup_smp_on_up)

ENTRY(fixup_smp)

stmfd sp!, {r4 - r6, lr}

mov r4, r0

add r5, r0, r1

mov r3, #0

bl __do_fixup_smp_on_up

ldmfd sp!, {r4 - r6, pc}

ENDPROC(fixup_smp)

#ifdef ARMEB

#define LOW_OFFSET 0x4

#define HIGH_OFFSET 0x0

#else

#define LOW_OFFSET 0x0

#define HIGH_OFFSET 0x4

#endif

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

/* __fixup_pv_table - patch the stub instructions with the delta between

* PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and

* can be expressed by an immediate shifter operand. The stub instruction

* has a form of '(add|sub) rd, rn, #imm'.

*/

__HEAD

__fixup_pv_table:

adr r0, 1f

ldmia r0, {r3-r7}

mvn ip, #0

subs r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET

add r4, r4, r3 @ adjust table start address

add r5, r5, r3 @ adjust table end address

add r6, r6, r3 @ adjust __pv_phys_pfn_offset address

add r7, r7, r3 @ adjust __pv_offset address

mov r0, r8, lsr #PAGE_SHIFT @ convert to PFN

str r0, [r6] @ save computed PHYS_OFFSET to __pv_phys_pfn_offset

strcc ip, [r7, #HIGH_OFFSET] @ save to __pv_offset high bits

mov r6, r3, lsr #24 @ constant for add/sub instructions

teq r3, r6, lsl #24 @ must be 16MiB aligned

THUMB( it ne @ cross section branch )

bne __error

str r3, [r7, #LOW_OFFSET] @ save to __pv_offset low bits

b __fixup_a_pv_table

ENDPROC(__fixup_pv_table)

.align

1: .long .

.long __pv_table_begin

.long __pv_table_end

2: .long __pv_phys_pfn_offset

.long __pv_offset

.text

__fixup_a_pv_table:

adr r0, 3f

ldr r6, [r0]

add r6, r6, r3

ldr r0, [r6, #HIGH_OFFSET] @ pv_offset high word

ldr r6, [r6, #LOW_OFFSET] @ pv_offset low word

mov r6, r6, lsr #24

cmn r0, #1

#ifdef CONFIG_THUMB2_KERNEL

moveq r0, #0x200000 @ set bit 21, mov to mvn instruction

lsls r6, #24

beq 2f

clz r7, r6

lsr r6, #24

lsl r6, r7

bic r6, #0x0080

lsrs r7, #1

orrcs r6, #0x0080

orr r6, r6, r7, lsl #12

orr r6, #0x4000

b 2f

1: add r7, r3

ldrh ip, [r7, #2]

ARM_BE8(rev16 ip, ip)

tst ip, #0x4000

and ip, #0x8f00

orrne ip, r6 @ mask in offset bits 31-24

orreq ip, r0 @ mask in offset bits 7-0

ARM_BE8(rev16 ip, ip)

strh ip, [r7, #2]

bne 2f

ldrh ip, [r7]

ARM_BE8(rev16 ip, ip)

bic ip, #0x20

orr ip, ip, r0, lsr #16

ARM_BE8(rev16 ip, ip)

strh ip, [r7]

2: cmp r4, r5

ldrcc r7, [r4], #4 @ use branch for delay slot

bcc 1b

bx lr

#else

#ifdef CONFIG_CPU_ENDIAN_BE8

moveq r0, #0x00004000 @ set bit 22, mov to mvn instruction

#else

moveq r0, #0x400000 @ set bit 22, mov to mvn instruction

#endif

b 2f

1: ldr ip, [r7, r3]

#ifdef CONFIG_CPU_ENDIAN_BE8

@ in BE8, we load data in BE, but instructions still in LE

bic ip, ip, #0xff000000

tst ip, #0x000f0000 @ check the rotation field

orrne ip, ip, r6, lsl #24 @ mask in offset bits 31-24

biceq ip, ip, #0x00004000 @ clear bit 22

orreq ip, ip, r0 @ mask in offset bits 7-0

#else

bic ip, ip, #0x000000ff

tst ip, #0xf00 @ check the rotation field

orrne ip, ip, r6 @ mask in offset bits 31-24

biceq ip, ip, #0x400000 @ clear bit 22

orreq ip, ip, r0 @ mask in offset bits 7-0

#endif

str ip, [r7, r3]

2: cmp r4, r5

ldrcc r7, [r4], #4 @ use branch for delay slot

bcc 1b

ret lr

#endif

ENDPROC(__fixup_a_pv_table)

.align

3: .long __pv_offset

ENTRY(fixup_pv_table)

stmfd sp!, {r4 - r7, lr}

mov r3, #0 @ no offset

mov r4, r0 @ r0 = table start

add r5, r0, r1 @ r1 = table size

bl __fixup_a_pv_table

ldmfd sp!, {r4 - r7, pc}

ENDPROC(fixup_pv_table)

.data

.globl __pv_phys_pfn_offset

.type __pv_phys_pfn_offset, %object

__pv_phys_pfn_offset:

.word 0

.size __pv_phys_pfn_offset, . -__pv_phys_pfn_offset

.globl __pv_offset

.type __pv_offset, %object

__pv_offset:

.quad 0

.size __pv_offset, . -__pv_offset

#endif

//#include "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.

*

*/

#include <asm/assembler.h>

#define ATAG_CORE 0x54410001

#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)

#define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)

#ifdef CONFIG_CPU_BIG_ENDIAN

#define OF_DT_MAGIC 0xd00dfeed

#else

#define OF_DT_MAGIC 0xedfe0dd0 /* 0xd00dfeed in big-endian */

#endif

/*

* 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

/* 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. If CONFIG_OF_FLATTREE

* is selected, then it will also accept a dtb pointer. 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.

*

* Returns:

* r2 either valid atags pointer, valid dtb pointer, or zero

* r5, r6 corrupted

*/

__vet_atags:

tst r2, #0x3 @ aligned?

bne 1f

ldr r5, [r2, #0]

#ifdef CONFIG_OF_FLATTREE

ldr r6, =OF_DT_MAGIC @ is it a DTB?

cmp r5, r6

beq 2f

#endif

cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE?

cmpne r5, #ATAG_CORE_SIZE_EMPTY

bne 1f

ldr r5, [r2, #4]

ldr r6, =ATAG_CORE

cmp r5, r6

bne 1f

2: ret lr @ atag/dtb pointer is ok

1: mov r2, #0

ret 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/dtb pointer

* r9 = processor ID

*/

__INIT

__mmap_switched:

adr r3, __mmap_switched_data

ldmia r3!, {r4, r5, r6, r7}

cmp r4, r5 @ Copy data segment if needed

1: cmpne r5, r6

ldrne fp, [r4], #4

strne fp, [r5], #4

bne 1b

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

1: cmp r6, r7

strcc fp, [r6],#4

bcc 1b

ARM( ldmia r3, {r4, r5, r6, r7, sp})

THUMB( ldmia r3, {r4, r5, r6, r7} )

THUMB( ldr sp, [r3, #16] )

str r9, [r4] @ Save processor ID

str r1, [r5] @ Save machine type

str r2, [r6] @ Save atags pointer

cmp r7, #0

strne r0, [r7] @ Save control register values

b start_kernel

ENDPROC(__mmap_switched)

.align 2

.type __mmap_switched_data, %object

__mmap_switched_data:

.long __data_loc @ r4

.long _sdata @ r5

.long __bss_start @ r6

.long _end @ r7

.long processor_id @ r4

.long __machine_arch_type @ r5

.long __atags_pointer @ r6

#ifdef CONFIG_CPU_CP15

.long cr_alignment @ r7

#else

.long 0 @ r7

#endif

.long init_thread_union + THREAD_START_SP @ sp

.size __mmap_switched_data, . - __mmap_switched_data

__FINIT

.text

/*

* This provides a C-API version of __lookup_processor_type

*/

ENTRY(lookup_processor_type)

stmfd sp!, {r4 - r6, r9, lr}

mov r9, r0

bl __lookup_processor_type

mov r0, r5

ldmfd sp!, {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)

*/

__lookup_processor_type:

adr r3, __lookup_processor_type_data

ldmia r3, {r4 - r6}

sub r3, r3, r4 @ get offset between virt&phys

add r5, r5, r3 @ convert virt addresses to

add r6, r6, r3 @ physical address space

1: ldmia r5, {r3, r4} @ value, mask

and r4, r4, r9 @ mask wanted bits

teq r3, r4

beq 2f

add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)

cmp r5, r6

blo 1b

mov r5, #0 @ unknown processor

2: ret lr

ENDPROC(__lookup_processor_type)

/*

* Look in <asm/procinfo.h> for information about the __proc_info structure.

*/

.align 2

.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_lpae:

#ifdef CONFIG_DEBUG_LL

adr r0, str_lpae

bl printascii

b __error

str_lpae: .asciz "\nError: Kernel with LPAE support, but CPU does not support LPAE.\n"

#else

b __error

#endif

.align

ENDPROC(__error_lpae)

__error_p:

#ifdef CONFIG_DEBUG_LL

adr r0, str_p1

bl printascii

mov r0, r9

bl printhex8

adr r0, str_p2

bl printascii

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.

*/

mov r0, #0x02000000

mov r3, #0x11

orr r3, r3, r3, lsl #8

orr r3, r3, r3, lsl #16

str r3, [r0], #4

str r3, [r0], #4

str r3, [r0], #4

str r3, [r0], #4

#endif

1: mov r0, r0

b 1b

ENDPROC(__error)

相关推荐
饼瑶2 小时前
Isaac Sim 5.0.0 Docker 部署手册(实验室服务器)
服务器·docker·容器
BioRunYiXue2 小时前
AlphaGenome:DeepMind 新作,基因组学迎来 Alpha 时刻
java·linux·运维·网络·数据库·人工智能·eclipse
十五年专注C++开发2 小时前
windows和linux使用system启动进程是一样的吗?
linux·c++·windows·system
Huanzhi_Lin2 小时前
Nginx本地资源服务器-常用脚本
服务器·前端·nginx·batch·静态资源服务器
此刻觐神2 小时前
IMX6ULL开发板学习-04(Linux磁盘管理相关命令)
linux·运维·学习
wb1892 小时前
docker-ce容器技术重习
运维·笔记·docker·容器·云计算
jiayong232 小时前
第 4 课:怎么把一个大页面拆成多个组件
运维·服务器·前端
qq_8573058192 小时前
ubuntu 22 源码安装bochs
linux·运维·ubuntu
A-刘晨阳2 小时前
麒麟v10桌面版2403版本运行程序提示权限不足(KYSEC)
运维·云计算·操作系统·银河麒麟·麒麟桌面系统