内核版本:2.6.32.2(mini2440光盘源码)
github地址:https://github.com/guanglun/mini2440_uboot_linux (for_len分支 https://github.com/guanglun/mini2440_uboot_linux/tree/for_learn)
1 /* 2 * linux/arch/arm/kernel/head.S 3 * 4 * Copyright (C) 1994-2002 Russell King 5 * Copyright (c) 2003 ARM Limited 6 * All Rights Reserved 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 * Kernel startup code for all 32-bit CPUs 13 */ 14 #include <linux/linkage.h> 15 #include <linux/init.h> 16 17 #include <asm/assembler.h> 18 #include <asm/domain.h> 19 #include <asm/ptrace.h> 20 #include <asm/asm-offsets.h> 21 #include <asm/memory.h> 22 #include <asm/thread_info.h> 23 #include <asm/system.h> 24 25 #if (PHYS_OFFSET & 0x001fffff) 26 #error "PHYS_OFFSET must be at an even 2MiB boundary!" 27 #endif 28 29 /* 30 PAGE_OFFSET 0xC0000000 页表偏移地址 31 PHYS_OFFSET 0x30000000 物理内存偏移地址 32 TEXT_OFFSET 0x00008000 33 KERNEL_RAM_VADDR 0xC0008000 内核虚拟映射地址 34 KERNEL_RAM_PADDR 0x30008000 内核物理(RAM)地址 35 */ 36 37 #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET) 38 #define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET) 39 40 41 /* 42 * swapper_pg_dir is the virtual address of the initial page table. 43 * We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must 44 * make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect 45 * the least significant 16 bits to be 0x8000, but we could probably 46 * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000. 47 */ 48 49 /* 50 swapper_pg_dir是初始化页表的虚拟地址 51 我们把页表放在KERNEL_RAM_VADDR下面,因此我们必须保证KERNEL_RAM_VADDR设置得正确。 52 当前我们要求最低有效的16位数值位0x8000, 53 但我们可能放宽此限制为KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000 54 */ 55 56 #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 57 #error KERNEL_RAM_VADDR must start at 0xXXXX8000 58 #endif 59 60 /* 61 62 .equ 虽然数据段主要用于定义变量数据,但是也可以在这里声明静态数据符号。 63 .equ 命令用于把常量值设置为可以在文本段中使用的符号 64 */ 65 .globl swapper_pg_dir 66 .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000 67 //定义rgtbl 宏 功能是将KERNEL_RAM_PADDR - 0x4000复制给rd寄存器 68 .macro pgtbl, rd 69 ldr \rd, =(KERNEL_RAM_PADDR - 0x4000) 70 .endm 71 72 /*CONFIG_XIP_KERNEL未设置 73 *#define KERNEL_START KERNEL_RAM_VADDR 74 *#define KERNEL_END _end 75 */ 76 77 //XIP片上执行 78 #ifdef CONFIG_XIP_KERNEL 79 #define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR) 80 #define KERNEL_END _edata_loc 81 #else 82 #define KERNEL_START KERNEL_RAM_VADDR 83 #define KERNEL_END _end 84 #endif 85 86 /* 87 * Kernel startup entry point. 88 * --------------------------- 89 * 90 * This is normally called from the decompressor code. The requirements 91 * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0, 92 93 * 0xc0008000, you call this at __pa(0xc0008000). 94 * 95 * See linux/arch/arm/tools/mach-types for the complete list of machine 96 * numbers for r1. 97 * 98 * We‘re trying to keep crap to a minimum; DO NOT add any machine specific 99 * crap here - that‘s what the boot loader (or in extreme, well justified 100 * circumstances, zImage) is for. 101 */ 102 /** 103 内核运行入口 104 MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。 105 MRC指令将协处理器的寄存器中数值传送到ARM处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。 106 **/ 107 .section ".text.head", "ax" 108 ENTRY(stext) 109 setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode 110 @ and irqs disabled 111 mrc p15, 0, r9, c0, c0 @ get processor id //获取处理器ID r9 = cpuid 112 bl __lookup_processor_type @ r5=procinfo r9=cpuid //处理器类型是否支持 113 114 beq __error_p @ yes, error ‘p‘ 115 bl __lookup_machine_type @ r5=machinfo //机器(板子)类型是否支持 116 movs r8, r5 @ invalid machine (r5=0)? r8 = machinfo 117 beq __error_a @ yes, error ‘a‘ 118 bl __vet_atags //确定r2 atags指针的有效性 无参数??? 119 120 //r8 = machinfo 121 //r9 = cpuid 122 //r10 = procinfo 123 bl __create_page_tables 124 125 /* 126 * The following calls CPU specific code in a position independent 127 * manner. See arch/arm/mm/proc-*.S for details. r10 = base of 128 * xxx_proc_info structure selected by __lookup_machine_type 129 * above. On return, the CPU will be ready for the MMU to be 130 * turned on, and r0 will hold the CPU control register value. 131 */ 132 ldr r13, __switch_data @ address to jump to after r13 = __mmap_switched(虚拟地址) 133 @ mmu has been enabled 134 adr lr, BSYM(__enable_mmu) @ return (PIC) address 135 ARM( add pc, r10, #PROCINFO_INITFUNC ) 136 THUMB( add r12, r10, #PROCINFO_INITFUNC ) 137 THUMB( mov pc, r12 ) 138 ENDPROC(stext) 139 140 #if defined(CONFIG_SMP) 141 ENTRY(secondary_startup) 142 /* 143 * Common entry point for secondary CPUs. 144 * 145 * Ensure that we‘re in SVC mode, and IRQs are disabled. Lookup 146 * the processor type - there is no need to check the machine type 147 * as it has already been validated by the primary processor. 148 */ 149 setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 150 mrc p15, 0, r9, c0, c0 @ get processor id 151 bl __lookup_processor_type 152 movs r10, r5 @ invalid processor? 153 moveq r0, #‘p‘ @ yes, error ‘p‘ 154 beq __error 155 156 /* 157 * Use the page tables supplied from __cpu_up. 158 */ 159 adr r4, __secondary_data 160 ldmia r4, {r5, r7, r12} @ address to jump to after 161 sub r4, r4, r5 @ mmu has been enabled 162 ldr r4, [r7, r4] @ get secondary_data.pgdir 163 adr lr, BSYM(__enable_mmu) @ return address 164 mov r13, r12 @ __secondary_switched address 165 ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor 166 @ (return control reg) 167 THUMB( add r12, r10, #PROCINFO_INITFUNC ) 168 THUMB( mov pc, r12 ) 169 ENDPROC(secondary_startup) 170 171 /* 172 * r6 = &secondary_data 173 */ 174 ENTRY(__secondary_switched) 175 ldr sp, [r7, #4] @ get secondary_data.stack 176 mov fp, #0 177 b secondary_start_kernel 178 ENDPROC(__secondary_switched) 179 180 .type __secondary_data, %object 181 __secondary_data: 182 .long . 183 .long secondary_data 184 .long __secondary_switched 185 #endif /* defined(CONFIG_SMP) */ 186 187 188 189 /* 190 * Setup common bits before finally enabling the MMU. Essentially 191 * this is just loading the page table pointer and domain access 192 * registers. 193 */ 194 __enable_mmu: 195 #ifdef CONFIG_ALIGNMENT_TRAP 196 orr r0, r0, #CR_A //执行 197 #else 198 bic r0, r0, #CR_A 199 #endif 200 #ifdef CONFIG_CPU_DCACHE_DISABLE 201 bic r0, r0, #CR_C //未执行 202 #endif 203 #ifdef CONFIG_CPU_BPREDICT_DISABLE 204 bic r0, r0, #CR_Z //未执行 205 #endif 206 #ifdef CONFIG_CPU_ICACHE_DISABLE 207 bic r0, r0, #CR_I //未执行 208 #endif 209 mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | 210 domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | 211 domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | 212 domain_val(DOMAIN_IO, DOMAIN_CLIENT)) 213 214 //MRC:协处理器寄存器到ARM处理器寄存器的数据传送指令(读出协处理器寄存器)。 215 //MCR:ARM处理器寄存器到协处理器寄存器的数据传送指令(写入协处理器寄存器)。 216 217 //c3 DOMAIN ACCESS CONTROL REGISTER 218 mcr p15, 0, r5, c3, c0, 0 @ load domain access register 219 //r4 = 30004000 放进协处理器c2 220 mcr p15, 0, r4, c2, c0, 0 @ load page table pointer 221 222 b __turn_mmu_on 223 ENDPROC(__enable_mmu) 224 225 /* 226 * Enable the MMU. This completely changes the structure of the visible 227 * memory space. You will not be able to trace execution through this. 228 * If you have an enquiry about this, *please* check the linux-arm-kernel 229 * mailing list archives BEFORE sending another post to the list. 230 * 231 * r0 = cp#15 control register 232 * r13 = *virtual* address to jump to upon completion 233 * 234 * other registers depend on the function called upon completion 235 */ 236 .align 5 237 __turn_mmu_on: 238 mov r0, r0 239 mcr p15, 0, r0, c1, c0, 0 @ write control reg 240 mrc p15, 0, r3, c0, c0, 0 @ read id reg 241 mov r3, r3 242 mov r3, r13 243 mov pc, r3 244 ENDPROC(__turn_mmu_on) 245 246 247 /* 248 * Setup the initial page tables. We only setup the barest 249 * amount which are required to get the kernel running, which 250 * generally means mapping in the kernel code. 251 * 252 * r8 = machinfo 253 * r9 = cpuid 254 * r10 = procinfo 255 * 256 * Returns: 257 * r0, r3, r6, r7 corrupted 258 * r4 = physical page table address 259 */ 260 //创建页表 261 __create_page_tables: 262 /* 263 *宏定义 *.macro pgtbl, rd *ldr \rd, =(KERNEL_RAM_PADDR - 0x4000) *.endm * 264 *分析:内存为 4G = 4*1024 MB;需要4096个表单表示,每一页表单为4bytes;因此需要16K内存,即0x4000; 265 *根据 =(KERNEL_RAM_PADDR - 0x4000)得,页表存放于-内核在内存中的物理地址之前。 266 */ 267 268 //定义rgtbl 宏 功能是将KERNEL_RAM_PADDR - 0x4000复制给rd寄存器 269 pgtbl r4 @ page table address //r4 = 30004000 270 271 /* 272 * Clear the 16K level 1 swapper page table 273 */ 274 /* * 按16个bytes一次,将页表清空 */ 275 mov r0, r4 //r0 = 30004000 276 mov r3, #0 //r3 = 0 277 add r6, r0, #0x4000 //r6 = 30008000 278 //将30004000-30008000 的空间置0 279 1: str r3, [r0], #4 280 str r3, [r0], #4 281 str r3, [r0], #4 282 str r3, [r0], #4 283 teq r0, r6 284 bne 1b 285 286 /* *r10 = proc_info_list类型结构体的基地址 *PROCINFO_MM_MMUFLAGS 8 /* offsetof(struct proc_info_list, __cpu_mm_mmu_flags) @ */ 287 ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags r7 = 00000C1E __cpu_mm_mmu_flags 288 289 /* 290 * Create identity mapping for first MB of kernel to 291 * cater for the MMU enable. This identity mapping 292 * will be removed by paging_init(). We use our current program 293 * counter to determine corresponding section base address. 294 */ 295 296 /* 297 298 为第一个MB内核创建标识映射,以满足MMU启用。 此标识映射将通过paging_init()删除。 我们使用当前的程序计数器来确定相应的部分基址。 299 300 */ 301 302 /* 303 *下面代码建立kernel对应的section页表项。 * 304 *1. 通过PC值的高12位(右移20位),得到kernel的section,并存储在r6中。 305 *2. 获取32bit的页表表单值 306 *3. 将页表表单值存放在页表内存区中。 * 307 *注意点: *a. lsr 20 因为虚拟地址分区中,后20位为相对地址,前12位为段地址 308 *b. lsl 2 因为每一个页表项为4字节,所以需要左移2位 309 */ 310 311 /*页表将4GB的地址空间分成若干个1MB的段(section),因此页表包含4096个页表项(section entry)。 312 每个页表项是32bits(4 bytes),因而页表占用4096*4=16k的内存空间。下面的代码是将这16k的页表清0。 313 */ 314 315 mov r6, pc //r6= 3000xxxx 通过pc值的高12位(右移20位),得到kernel的section,并存储到r6中.因为当前是通过运行时地址得到的kernel的section,因而是物理地址. 316 mov r6, r6, lsr #20 @ start of kernel section r6 = 00000300 317 orr r3, r7, r6, lsl #20 @ flags + kernel base r3 = 30000C1E = r7 | (r6 << 20); flags + kernel base,得到页表中需要设置的值. 318 str r3, [r4, r6, lsl #2] @ identity mapping //设置页表: mem[r4 + r6 * 4] = r3 r4 = 30004000 319 // mem(30004000 + 300 << 2) = 30000C1E 320 // mem(30004000 + 300 * 4) = 30000C1E 321 // mem(30004C00) = 30000C1E 322 //这里,因为页表的每一项是32 bits(4 bytes),所以要乘以4(<<2). 323 324 /* 325 * Now setup the pagetables for our kernel direct 326 * mapped region. 327 */ 328 /* 329 * 下面的 add r0, r4, #(KERNEL_START & 0xff000000) >> 18 涉及到一个立即数的概念: 330 * 关于arm汇编立即数,可以参考下面网站:http://blog.csdn.net/a99778800/article/details/6759825 * 331 * 下面这段代码就是存储kernel物理地址。建立页表,虚拟地址和物理地址之间建立连接。 332 * 即:将内核中所有的物理地址(1M为单位)都存放到了页表中,与虚拟地址一一对应。 */ 333 334 //KERNEL_START = 0xC0008000 335 //这样分开写是由于arm的立即数只能是8位表示。 336 add r0, r4, #(KERNEL_START & 0xff000000) >> 18 //r0 = 30007000 R4 =30004000 337 str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! 338 ldr r6, =(KERNEL_END - 1) 339 add r0, r0, #4 340 add r6, r4, r6, lsr #18 341 1: cmp r0, r6 342 add r3, r3, #1 << 20 343 strls r3, [r0], #4 344 bls 1b 345 346 /* 347 运行到此 段表在内存中如下: 348 349 J-Link>mem32 30007000 10 350 30007000 = 30000C1E 30100C1E 30200C1E 30300C1E 351 30007010 = 30400C1E 00000000 00000000 00000000 352 30007020 = 00000000 00000000 00000000 00000000 353 30007030 = 00000000 00000000 00000000 00000000 354 J-Link>h 355 PC: (R15) = 300080D8, CPSR = 200000D3 (SVC mode, ARM FIQ dis. IRQ dis.) 356 Current: 357 R0 =30007014, R1 =000007CF, R2 =00000000, R3 =30500C1E 358 R4 =30004000, R5 =00000000, R6 =30007013, R7 =00000C1E 359 R8 =3001EF70, R9 =41129200, R10=3001EF3C, R11=00000020, R12=306EFD84 360 R13=3049D990, R14=30008028, SPSR=00000010 361 USR: R8 =3001EF70, R9 =41129200, R10=3001EF3C, R11=00000020, R12=306EFD84 362 R13=00000000, R14=00000000 363 FIQ: R8 =00000000, R9 =00000000, R10=00000000, R11=00000000, R12=00000000 364 R13=00000000, R14=00000000, SPSR=00000010 365 IRQ: R13=00000000, R14=00000000, SPSR=00000010 366 SVC: R13=3049D990, R14=30008028, SPSR=00000010 367 ABT: R13=00000000, R14=00000000, SPSR=00000010 368 UND: R13=00000000, R14=00000000, SPSR=00000010 369 370 371 */ 372 373 374 /* 375 * XIP介绍: 376 * XIP是指 (EXECUTE IN PLACE) 是指直接从存放代码的位置上启动运行。 377 * 非XIP方式是指在运行之前需对代码进行重定位。该类型的内核以非压缩方式存放在Flash中,启动时由Bootloader加载到内存后运行。 * 378 * 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分 379 * 这里我们再映射一些RAM来作为.data and .bss空间。 380 */ 381 382 #ifdef CONFIG_XIP_KERNEL XIP 未定义 383 /* 384 * Map some ram to cover our .data and .bss areas. 385 */ 386 orr r3, r7, #(KERNEL_RAM_PADDR & 0xff000000) 387 .if (KERNEL_RAM_PADDR & 0x00f00000) 388 orr r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000) 389 .endif 390 //这样分开写是由于arm的立即数只能是8位表示。 391 add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18 392 str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]! 393 ldr r6, =(_end - 1) 394 add r0, r0, #4 395 add r6, r4, r6, lsr #18 396 1: cmp r0, r6 397 add r3, r3, #1 << 20 398 strls r3, [r0], #4 399 bls 1b 400 #endif 401 402 /* 403 * Then map first 1MB of ram in case it contains our boot params. 404 * 然后映射第一个1MB的RAM,以防它包含我们的引导参数。 405 */ 406 /* 407 * 下面的代码用来设置RAM中大小为1M虚拟地址的页表。之所以要设置这个页表项的原因是该区域存储着boot params。 408 * 因此需要为它建立map,这样开启MMU后就可以访问 409 */ 410 411 //这样分开写是由于arm的立即数只能是8位表示。 412 413 /* 414 *下面的代码用来设置RAM中起始地址为0x30000000、大小为1M虚拟地址的页表,之所以要设置这个页表项的原因是该区域起始地址为0x30000100存 415 *储着boot params。因此需要为它建立map,这样开启MMU后就可以访问这些参数了。 416 */ 417 add r0, r4, #PAGE_OFFSET >> 18 418 orr r6, r7, #(PHYS_OFFSET & 0xff000000) 419 .if (PHYS_OFFSET & 0x00f00000) 420 orr r6, r6, #(PHYS_OFFSET & 0x00f00000) 421 .endif 422 str r6, [r0] 423 424 425 #ifdef CONFIG_DEBUG_LL 426 ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags 427 /* 428 * Map in IO space for serial debugging. 429 * This allows debug messages to be output 430 * via a serial console before paging_init. 431 */ 432 ldr r3, [r8, #MACHINFO_PGOFFIO] 433 add r0, r4, r3 434 rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long) 435 cmp r3, #0x0800 @ limit to 512MB 436 movhi r3, #0x0800 437 add r6, r0, r3 438 ldr r3, [r8, #MACHINFO_PHYSIO] 439 orr r3, r3, r7 440 1: str r3, [r0], #4 441 add r3, r3, #1 << 20 442 teq r0, r6 443 bne 1b 444 #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS) 445 /* 446 * If we‘re using the NetWinder or CATS, we also need to map 447 * in the 16550-type serial port for the debug messages 448 */ 449 add r0, r4, #0xff000000 >> 18 450 orr r3, r7, #0x7c000000 451 str r3, [r0] 452 #endif 453 #ifdef CONFIG_ARCH_RPC 454 /* 455 * Map in screen at 0x02000000 & SCREEN2_BASE 456 * Similar reasons here - for debug. This is 457 * only for Acorn RiscPC architectures. 458 */ 459 add r0, r4, #0x02000000 >> 18 460 orr r3, r7, #0x02000000 461 str r3, [r0] 462 add r0, r4, #0xd8000000 >> 18 463 str r3, [r0] 464 #endif 465 #endif 466 mov pc, lr 467 ENDPROC(__create_page_tables) 468 .ltorg 469 470 #include "head-common.S"
1 /* 2 * linux/arch/arm/kernel/head-common.S 3 * 4 * Copyright (C) 1994-2002 Russell King 5 * Copyright (c) 2003 ARM Limited 6 * All Rights Reserved 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 14 #define ATAG_CORE 0x54410001 15 #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2) 16 #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2) 17 18 .align 2 19 .type __switch_data, %object 20 __switch_data: 21 .long __mmap_switched 22 .long __data_loc @ r4 23 .long _data @ r5 24 .long __bss_start @ r6 25 .long _end @ r7 26 .long processor_id @ r4 27 .long __machine_arch_type @ r5 28 .long __atags_pointer @ r6 29 .long cr_alignment @ r7 30 .long init_thread_union + THREAD_START_SP @ sp 31 32 /* 33 * The following fragment of code is executed with the MMU on in MMU mode, 34 * and uses absolute addresses; this is not position independent. 35 * 36 * r0 = cp#15 control register 37 * r1 = machine ID 38 * r2 = atags pointer 39 * r9 = processor ID 40 */ 41 /* 42 R0~R15 和 r0~r15 (16 个通用寄存器); 43 a1~a4(参数,结果或临时寄存器,同 R0~R3); 44 v1~v8(变量寄存器,同 R4~R11); 45 SB 和 sb(静态基址,同 R9); 46 SL 和 sl(堆栈限制,同 R10); 47 FP 和 fp(帧指针); 48 IP 和 ip(过程调用中间临时寄存器,同 R12); 49 SP 和 sp(堆栈指针,同 R13); 50 LR 和 lr(链接寄存器,同 R14); 51 PC 和 pc(程序计数器,同 R15). 52 */ 53 __mmap_switched: 54 adr r3, __switch_data + 4 55 56 //R4 =C0460000, R5 =C0460000, R6 =C049D8E0, R7 =C04D1D38 57 ldmia r3!, {r4, r5, r6, r7} 58 cmp r4, r5 @ Copy data segment if needed 59 //以下ne都不会执行 60 1: cmpne r5, r6 61 ldrne fp, [r4], #4 62 strne fp, [r5], #4 63 bne 1b 64 65 mov fp, #0 @ Clear BSS (and zero fp) 66 1: cmp r6, r7 67 strcc fp, [r6],#4 68 bcc 1b 69 70 ARM( ldmia r3, {r4, r5, r6, r7, sp}) 71 THUMB( ldmia r3, {r4, r5, r6, r7} ) 72 THUMB( ldr sp, [r3, #16] ) 73 str r9, [r4] @ Save processor ID 74 str r1, [r5] @ Save machine type 75 str r2, [r6] @ Save atags pointer 76 bic r4, r0, #CR_A @ Clear ‘A‘ bit 77 stmia r7, {r0, r4} @ Save control register values 78 b start_kernel 79 ENDPROC(__mmap_switched) 80 81 /* 82 * Exception handling. Something went wrong and we can‘t proceed. We 83 * ought to tell the user, but since we don‘t have any guarantee that 84 * we‘re even running on the right architecture, we do virtually nothing. 85 * 86 * If CONFIG_DEBUG_LL is set we try to print out something about the error 87 * and hope for the best (useful if bootloader fails to pass a proper 88 * machine ID for example). 89 */ 90 __error_p: 91 #ifdef CONFIG_DEBUG_LL 92 adr r0, str_p1 93 bl printascii 94 mov r0, r9 95 bl printhex8 96 adr r0, str_p2 97 bl printascii 98 b __error 99 str_p1: .asciz "\nError: unrecognized/unsupported processor variant (0x" 100 str_p2: .asciz ").\n" 101 .align 102 #endif 103 ENDPROC(__error_p) 104 105 __error_a: 106 #ifdef CONFIG_DEBUG_LL 107 mov r4, r1 @ preserve machine ID 108 adr r0, str_a1 109 bl printascii 110 mov r0, r4 111 bl printhex8 112 adr r0, str_a2 113 bl printascii 114 adr r3, 4f 115 ldmia r3, {r4, r5, r6} @ get machine desc list 116 sub r4, r3, r4 @ get offset between virt&phys 117 add r5, r5, r4 @ convert virt addresses to 118 add r6, r6, r4 @ physical address space 119 1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type 120 bl printhex8 121 mov r0, #‘\t‘ 122 bl printch 123 ldr r0, [r5, #MACHINFO_NAME] @ get machine name 124 add r0, r0, r4 125 bl printascii 126 mov r0, #‘\n‘ 127 bl printch 128 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc 129 cmp r5, r6 130 blo 1b 131 adr r0, str_a3 132 bl printascii 133 b __error 134 ENDPROC(__error_a) 135 136 str_a1: .asciz "\nError: unrecognized/unsupported machine ID (r1 = 0x" 137 str_a2: .asciz ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n" 138 str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n" 139 .align 140 #endif 141 142 __error: 143 #ifdef CONFIG_ARCH_RPC 144 /* 145 * Turn the screen red on a error - RiscPC only. 146 */ 147 mov r0, #0x02000000 148 mov r3, #0x11 149 orr r3, r3, r3, lsl #8 150 orr r3, r3, r3, lsl #16 151 str r3, [r0], #4 152 str r3, [r0], #4 153 str r3, [r0], #4 154 str r3, [r0], #4 155 #endif 156 1: mov r0, r0 157 b 1b //1b b指back 1为标号 158 ENDPROC(__error) 159 160 161 /* 162 * Read processor ID register (CP#15, CR0), and look up in the linker-built 163 * supported processor list. Note that we can‘t use the absolute addresses 164 * for the __proc_info lists since we aren‘t running with the MMU on 165 * (and therefore, we are not in the correct address space). We have to 166 * calculate the offset. 167 * 168 * r9 = cpuid 169 * Returns: 170 * r3, r4, r6 corrupted 171 * r5 = proc_info pointer in physical address space 物理内存中的proc_info指针 172 * r9 = cpuid (preserved) 173 */ 174 //ADR : 小范围的地址读取伪指令.ADR 指令将基于 PC 相对偏移的地址值读取到寄存器中. 175 //LDMIA 和 STMIA 批量加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据 176 __lookup_processor_type: 177 adr r3, 3f //3f f指forward 3为标号 178 ldmia r3, {r5 - r7} 179 /* 180 r5 __proc_info_begin 地址 181 r6 __proc_info_end 地址 182 r7 . 当前地址 183 184 185 R0 =00000000, R1 =000007CF, R2 =016F2818, R3 =300081B8 186 R4 =30008000, R5 =C001EF3C, R6 =C001EF70, R7 =C00081C0 187 R8 =016F2818, R9 =41129200, R10=00000004, R11=00000020, R12=306EFD84 188 R13=3049D990, R14=3000800C, SPSR=00000010 189 190 */ 191 192 add r3, r3, #8 193 sub r3, r3, r7 @ get offset between virt&phys R3 = 70000000 = 300081C0 - C00081C0 194 add r5, r5, r3 @ convert virt addresses to 195 add r6, r6, r3 @ physical address space 196 1: ldmia r5, {r3, r4} @ value, mask 197 and r4, r4, r9 @ mask wanted bits 198 teq r3, r4 199 beq 2f //ID正确,跳转回去 200 add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) ID不正确的话,寻找下一个list对比 201 cmp r5, r6 //比较是否小于__proc_info_end的地址,如果小于则说明还有list可以比较,否则就已经没有了 202 blo 1b 203 mov r5, #0 @ unknown processor 返回r5 = 0表示没有对应的处理器 204 2: mov pc, lr 205 ENDPROC(__lookup_processor_type) 206 207 /* 208 * This provides a C-API version of the above function. 209 */ 210 ENTRY(lookup_processor_type) 211 stmfd sp!, {r4 - r7, r9, lr} 212 mov r9, r0 213 bl __lookup_processor_type 214 mov r0, r5 215 ldmfd sp!, {r4 - r7, r9, pc} 216 ENDPROC(lookup_processor_type) 217 218 /* 219 * Look in <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for 220 * more information about the __proc_info and __arch_info structures. 221 */ 222 223 224 //proc_info和arch_info位置 225 .align 2 226 3: .long __proc_info_begin 227 .long __proc_info_end 228 4: .long . 229 .long __arch_info_begin 230 .long __arch_info_end 231 232 /* 233 * Lookup machine architecture in the linker-build list of architectures. 234 * Note that we can‘t use the absolute addresses for the __arch_info 235 * lists since we aren‘t running with the MMU on (and therefore, we are 236 * not in the correct address space). We have to calculate the offset. 237 * 238 * r1 = machine architecture number 239 * Returns: 240 * r3, r4, r6 corrupted 241 * r5 = mach_info pointer in physical address space 242 */ 243 __lookup_machine_type: 244 adr r3, 4b //将 .long . 的物理地址加载至r3寄存器 R3 =300081C0 245 ldmia r3, {r4, r5, r6} //R4 =C00081C0, R5 =C001EF70, R6 =C001EFA4, 246 247 //将r5 r6的__arch_info_begin和__arch_info_end地址由虚拟地址转换成物理地址 248 sub r3, r3, r4 @ get offset between virt&phys 249 add r5, r5, r3 @ convert virt addresses to 250 add r6, r6, r3 @ physical address space 251 252 1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type 253 teq r3, r1 @ matches loader number? 254 beq 2f @ found 255 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc 256 cmp r5, r6 257 blo 1b 258 mov r5, #0 @ unknown machine 259 2: mov pc, lr 260 ENDPROC(__lookup_machine_type) 261 262 /* 263 * This provides a C-API version of the above function. 264 */ 265 ENTRY(lookup_machine_type) 266 stmfd sp!, {r4 - r6, lr} 267 mov r1, r0 268 bl __lookup_machine_type 269 mov r0, r5 270 ldmfd sp!, {r4 - r6, pc} 271 ENDPROC(lookup_machine_type) 272 273 /* Determine validity of the r2 atags pointer. The heuristic requires 274 * that the pointer be aligned, in the first 16k of physical RAM and 275 * that the ATAG_CORE marker is first and present. Future revisions 276 * of this function may be more lenient with the physical address and 277 * may also be able to move the ATAGS block if necessary. 278 * 279 * r8 = machinfo 280 * 281 * Returns: 282 * r2 either valid atags pointer, or zero 283 * r5, r6 corrupted 284 */ 285 __vet_atags: 286 tst r2, #0x3 @ aligned? 287 bne 1f 288 289 ldr r5, [r2, #0] @ is first tag ATAG_CORE? 290 cmp r5, #ATAG_CORE_SIZE 291 cmpne r5, #ATAG_CORE_SIZE_EMPTY 292 bne 1f 293 ldr r5, [r2, #4] 294 ldr r6, =ATAG_CORE 295 cmp r5, r6 296 bne 1f 297 298 mov pc, lr @ atag pointer is ok 299 300 1: mov r2, #0 301 mov pc, lr 302 ENDPROC(__vet_atags)
linux kernel mini2440 start.S head-common.S 部分注释
原文:https://www.cnblogs.com/guanglun/p/10730971.html