最近在内核的移植,先搞bootloader,网上找了篇不错的东东 #include #include #include //global声明一个符号可被其它文件引用,相当于声明了一个全局变量,.globl与.global相同。 //该部分为处理器的异常处理向量表。地址范围为0x0000 0000 ~ 0x0000 0020,刚好8条指令。 .globl _start _start: breset ldrpc, _undefined_instruction ldrpc, _software_interrupt ldrpc, _prefetch_abort ldrpc, _data_abort ldrpc, _not_used ldrpc, _irq ldrpc, _fiq // .word伪操作用于分配一段字内存单元(分配的单元都是字对齐的),并用伪操作中的expr初始化。.long与.int作用与之相同。 _undefined_instruction: .word undefined_instruction _software_interrupt:.word software_interrupt _prefetch_abort:.word prefetch_abort _data_abort:.word data_abort _not_used:.word not_used _irq:.word irq _fiq:.word fiq // .align伪操作用于表示对齐方式:通过添加填充字节使当前位置满足一定的对齐方式。.balign的作用同.align。 // .align {alignment} {,fill} {,max} //其中:alignment用于指定对齐方式,可能的取值为2的次幂,缺省为4。fill是填充内容,缺省用0填充。max是填充字节数最大值,如果填充字节数超过max, //就不进行对齐,例如: //.align 4/* 指定对齐方式为字对齐 */ .balignl 16,0xdeadbeef /* * Startup Code (reset vector) * * do important init only if we don't start from RAM! * - relocate armboot to ram * - setup stack * - jump to second stage */ // TEXT_BASE在开发板相关的目录中的config.mk文件中定义, 它定义了 // 代码在运行时所在的地址, 那么_TEXT_BASE中保存了这个地址 _TEXT_BASE: .wordTEXT_BASE // 声明 _armboot_start 并用 _start 来进行初始化,在board/u-boot.lds中定义。 .globl _armboot_start _armboot_start: .word _start /* * These are defined in the board-specific linker script. */ // 声明_bss_start并用__bss_start来初始化,其中__bss_start定义在与板相关的u-boot.lds中。 // _bss_start保存的是__bss_start这个标号所在的地址, 这里涉及到当前代码所在 // 的地址不是编译时的地址的情况, 这里直接取得该标号对应的地址, 不受编译时 // 地址的影响. _bss_end也是同样的道理. .globl _bss_start _bss_start: .word __bss_start // 同上 .globl _bss_end _bss_end: .word _end #ifdef CONFIG_USE_IRQ /* IRQ stack memory (calculated at run-time) */ .globl IRQ_STACK_START IRQ_STACK_START: .word0x0badc0de /* IRQ stack memory (calculated at run-time) */ .globl FIQ_STACK_START FIQ_STACK_START: .word 0x0badc0de #endif /****************************************************************************/ /**/ /* the actual reset code*/ /**/ /****************************************************************************/ //MRS {} Rd,CPSR|SPSR 将CPSR|SPSR传送到Rd //使用这两条指令将状态寄存器传送到一般寄存器,只修改必要的位,再将结果传送回状态寄存器,这样可以最好地完成对CRSP或者SPSR的修改 //MSR {} CPSR_|SPSR_,Rm 或者是 MSR {} CPSR_f|SPSR_f,# //MRS与MSR配合使用,作为更新PSR的“读取--修改--写回”序列的一部分 //bic r0,r1,r2;r0:=r1 and not r2 //orr ro,r1,r2;r0:=r1 or r2 //这几条指令执行完毕后,进入SVC模式,该模式主要用来处理软件中断(SWI) reset: mrsr0,cpsr/* set the cpu to SVC32 mode*/ bicr0,r0,#0x1f/* (superviser mode, M=10011)*/ orrr0,r0,#0x13 msrcpsr,r0 /* * we do sys-critical inits only at reboot, * not when booting from ram! */ // // B----转移指令,跳转到指令中指定的目的地址 // BL---带链接的转移指令,像B一样跳转并把转移后面紧接的一条指令地址保存到链接寄存器LR(R14)中,以此来完成子程序的调用 // 该语句首先调用cpu_init_crit进行CPU的初始化,并把下一条指令的地址保存在LR中,以使得执行完后能够正常返回。 // #ifndef CONFIG_SKIP_LOWLEVEL_INIT blcpu_init_crit/* we do sys-critical inits*/ #endif #ifndef CONFIG_SKIP_RELOCATE_UBOOT //调试阶段的代码是直接在RAM中运行的,而最后需要把这些代码固化到Flash中,因此U-Boot需要自己从Flash转移到 //RAM中运行,这也是重定向的目的所在。 //通过adr指令得到当前代码的地址信息:如果U-boot是从RAM开始运行,则从adr,r0,_start得到的地址信息为 //r0=_start=_TEXT_BASE=TEXT_BASE=0xa3000000;如果U-boot从Flash开始运行,即从处理器对应的地址运行, //则r0=0x0000,这时将会执行copy_loop标识的那段代码了。 // _TEXT_BASE 定义在board/pxa255_idp/config.mk中 relocate:/* relocate U-Boot to RAM*/ adrr0, _start/* r0 ldrr1, _TEXT_BASE/* test if we run from flash or RAM */ cmpr0, r1/* don't reloc during debug*/ beqstack_setup/* 如果是从RAM启动,则开始设置堆栈 */ // 如果不是从RAM运行的话,则将代码拷贝到_TEXT_BASE标识的RAM中。 ldrr2, _armboot_start ldrr3, _bss_start subr2, r3, r2/* r2 addr2, r0, r2/* r2 copy_loop: ldmiar0!, {r3-r10}/* copy from source address [r0]*/ stmiar1!, {r3-r10}/* copy totarget address [r1]*/ cmpr0, r2/* until source end addreee [r2]*/ blecopy_loop #endif/* CONFIG_SKIP_RELOCATE_UBOOT */ /* Set up the stack*/ stack_setup: ldrr0, _TEXT_BASE/* upper 128 KiB: relocated uboot*/ subr0, r0, #CFG_MALLOC_LEN/* malloc areaCFG_MALLOC_LEN----(CFG_ENV_SIZE(256K) + 128*1024)*/ subr0, r0, #CFG_GBL_DATA_SIZE/* bdinfo CFG_GBL_DATA_SIZE128---size in bytes reserved for initial data */ // 这里如果需要使用IRQ, 还有给IRQ保留堆栈空间, 一般不使用. #ifdef CONFIG_USE_IRQ subr0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)//二者均为4K #endif subsp, r0, #12/* leave 3 words for abort-stack*/ //该部分将未初始化数据段_bss_start----_bss_end中的数据清零。 clear_bss: ldrr0, _bss_start/* find start of bss segment */ ldrr1, _bss_end/* stop here*/ movr2, #0x00000000/* clear*/ clbss_l:strr2, [r0]/* clear loop...*/ addr0, r0, #4 cmpr0, r1 bleclbss_l // 通过该语句跳转到C代码执行,stage1的使命也算完成。 ldrpc, _start_armboot // start_armboot位于 lib_arm/board.c 中,是一个系统初始化的接口函数 _start_armboot: .word start_armboot /****************************************************************************/ /**/ /* CPU_init_critical registers*/ /**/ /* - setup important registers*/ /* - setup memory timing*/ /**/ /****************************************************************************/ /* mk@tbd: Fix this! */ #ifdef CONFIG_CPU_MONAHANS #undef ICMR #undef OSMR3 #undef OSCR #undef OWER #undef OIER #endif /* Interrupt-Controller base address */ IC_BASE:.word0x40d00000 #define ICMR0x04 /* Reset-Controller */ RST_BASE:.word0x40f00030 #define RCSR0x00 //OS定时器寄存器 // //地址名称描述 //0x40a0 0000OSMR0OS定时器符合寄存器0 //0x40a0 0004OSMR1OS定时器符合寄存器1 //0x40a0 0008OSMR2OS定时器符合寄存器2 //0x40a0 000COSMR3OS定时器符合寄存器3 //0x40a0 0010OSCROS定时器计数寄存器 //0x40a0 0014OSSROS定时器状态寄存器 //0x40a0 0018OWEROS定时器看门狗允许寄存器 //0x40a0 001COIEROS定时器中断寄存器 /* Operating System Timer */ OSTIMER_BASE:.word0x40a00000 #define OSMR30x0C #define OSCR0x10 #define OWER0x18 #define OIER0x1C /* Clock Manager Registers */ //地址名称描述 // 0x4130 0000CCCR核时钟配置寄存器 // 0x4130 0004CKEN时钟允许寄存器 // 0x4130 0008OSCC振荡配置寄存器 // #ifdef CONFIG_CPU_MONAHANS # ifndef CFG_MONAHANS_RUN_MODE_OSC_RATIO #error "You have to define CFG_MONAHANS_RUN_MODE_OSC_RATIO!!" # endif # ifndef CFG_MONAHANS_TURBO_RUN_MODE_RATIO #define CFG_MONAHANS_TURBO_RUN_MODE_RATIO 0x1 # endif #else /* ! CONFIG_CPU_MONAHANS */ #ifdef CFG_CPUSPEED CC_BASE:.word0x41300000 #define CCCR0x00 cpuspeed:.wordCFG_CPUSPEED #else #error "You have to define CFG_CPUSPEED!!" #endif #endif /* CONFIG_CPU_MONAHANS */ // .macro伪操作符标识宏定义的开始,.endm标识宏定义的结束。二者包含的一段代码,称为宏定义体,这样在程序中就可通过宏指令多次调用该代码段。 //格式: // .macro macroname {parameter{,parameter}...} //... // .endm // 宏的参数可直接使用斜线“/字符”来引用,如下“/reg”所示。 // MRC指令的格式为: // MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。 // MRC指令用于将协处理器寄存器中的数据传送到ARM处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处 // 理器操作码2为协处理器将要执行的操作,目的寄存器为ARM处理器的寄存器,源寄存器1和源寄存器2均为协处理器的寄存器。 //指令示例: //MRCP3,3,R0,C4,C5,6;该指令将协处理器P3的寄存器中的数据传送到ARM处理器寄存器中。 /* * Arm MMU采用二级页表结构:一级页表(L1)和二级页表(L2)。一级页表L1中的页表项包括:用于转换1MB页的页表项和指向二级页表起始地址的页表项。 * L1主页表将4GB的地址空间划分为多个1MB的段,它包含4096个页表项,占据16KB的空间。L2页表有两种:L2粗(Coarse)页表和L2细 (fine)页表。 * L2粗页表可映射1MB的空间,支持4KB和64KB的页表项,页表项个数为256个(64KB的页表项要重复16次),占据1kB的空间(在L1表中L2粗页表的目录 * 项应在1KB地址边缘)。L2细页表可映射1MB的地址空间,支持1KB、4KB和64KB的页表项,页表项个数为 1024个(4KB表项重复4次,64KB表项重复64次), * 占据4KB的空间(在L1表中L2细页表的目录项应在4KB地址边缘)。CP15的C2寄存器保存这转换表基地址,指向L1表的起始地址(位于16KB地址边缘) */ // 该宏的作用就是读取转换表基地址并将其存放在reg寄存器中。 /* takes care the CP15 update has taken place */ .macro CPWAIT reg mrcp15,0,/reg,c2,c0,0 mov/reg,/reg subpc,pc,#4 .endm cpu_init_crit: // 中断控制器共有6个寄存器 // //地 址名称描述 // 0x40D0 0000ICIP中断控制器IRQ Pending 寄存器 // 0x40D0 0004ICMR中断控制器屏蔽寄存器 // 0x40D0 0008ICLR中断控制器级别寄存器 // 0x40D0 000CICFR中断控制器FIQ Pending 寄存器 // 0x40D0 0010ICPR中断控制器 Pending 寄存器 // 0x40D0 0014ICCR中断控制器控制寄存器 // 一般的引导装入程序都不采用中断,即使采用中断也得在完成中断机制的初始化之前先关闭中断,以免干扰,所以这里的第一步就是屏蔽掉所有中断请求。 /* CONFIG_CPU_MONAHANS 定义在中,用来进行芯片选择,Monahans是XScale PXA27X 系列的新成员*/ /* mask all IRQs*/ #ifndef CONFIG_CPU_MONAHANS ldrr0, IC_BASE movr1, #0x00 strr1, [r0, #ICMR] #else /* Step 1 - Enable CP6 permission */ mrcp15, 0, r1, c15, c1, 0 @ read CPAR orrr1, r1, #0x40 mcrp15, 0, r1, c15, c1, 0 CPWAITr1 /* Step 2 - Mask ICMR & ICMR2 */ movr1, #0 mcrp6, 0, r1, c1, c0, 0@ ICMR mcrp6, 0, r1, c7, c0, 0@ ICMR2 /* turn off all clocks but the ones we will definitly require */ ldrr1, =CKENA ldrr2, =(CKENA_22_FFUART | CKENA_10_SRAM | CKENA_9_SMC | CKENA_8_DMC) strr2, [r1] ldrr1, =CKENB ldrr2, =(CKENB_6_IRQ) strr2, [r1] #endif /* set clock speed设置时钟*/ #ifdef CONFIG_CPU_MONAHANS ldrr0, =ACCR ldrr1, =(((CFG_MONAHANS_TURBO_RUN_MODE_RATIOstrr1, [r0] #else /* ! CONFIG_CPU_MONAHANS */ #ifdef CFG_CPUSPEED ldrr0, CC_BASE//0x41300000 ldrr1, cpuspeed//cpuspeed = CFG_CPUSPEED strr1, [r0, #CCCR]//CCCR = 0x00 movr0, #2 mcrp14, 0, r0, c6, c0, 0//进入频率变化顺序 setspeed_done: #endif /* CFG_CPUSPEED */ #endif /* CONFIG_CPU_MONAHANS */ /* * before relocating, we have to setup RAM timing * because memory timing is board-dependend, you will * find a lowlevel_init.S in your board directory. */ movip,lr bllowlevel_init movlr,ip /* Memory interfaces are working. Disable MMU and enable I-cache.*/ /* mk: hmm, this is not in the monahans docs, leave it now but *check here if it doesn't work :-) */ ldrr0, =0x2001/* enable access to all coproc.*/ mcrp15, 0, r0, c15, c1, 0 CPWAIT r0 mcrp15, 0, r0, c7, c10, 4/* drain the write & fill buffers*/ CPWAIT r0 mcrp15, 0, r0, c7, c7, 0/* flush Icache, Dcache and BTB*/ CPWAIT r0 mcrp15, 0, r0, c8, c7, 0/* flush instuction and data TLBs*/ CPWAIT r0 /* Enable the Icache*/ /* mrcp15, 0, r0, c1, c0, 0 orrr0, r0, #0x1800 mcrp15, 0, r0, c1, c0, 0 CPWAIT */ movpc, lr /****************************************************************************/ /**/ /* Interrupt handling */ /**/ /****************************************************************************/ /* IRQ stack frame*/ #define S_FRAME_SIZE72 #define S_OLD_R068 #define S_PSR64 #define S_PC60 #define S_LR56 #define S_SP52 #define S_IP48 #define S_FP44 #define S_R1040 #define S_R936 #define S_R832 #define S_R728 #define S_R624 #define S_R520 #define S_R416 #define S_R312 #define S_R28 #define S_R14 #define S_R00 #define MODE_SVC 0x13 /* use bad_save_user_regs for abort/prefetch/undef/swi ...*/ .macrobad_save_user_regs subsp, sp, #S_FRAME_SIZE stmiasp, {r0 - r12}/* Calling r0-r12*/ addr8, sp, #S_PC ldrr2, _armboot_start subr2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN) subr2, r2, #(CFG_GBL_DATA_SIZE+8)@ set base 2 words into abort stack ldmiar2, {r2 - r4}/* get pc, cpsr, old_r0*/ addr0, sp, #S_FRAME_SIZE/* restore sp_SVC*/ addr5, sp, #S_SP movr1, lr stmiar5, {r0 - r4}/* save sp_SVC, lr_SVC, pc, cpsr, old_r */ movr0, sp .endm /* use irq_save_user_regs / irq_restore_user_regs for*/ /* IRQ/FIQ handling */ .macroirq_save_user_regs subsp, sp, #S_FRAME_SIZE stmiasp, {r0 - r12}/* Calling r0-r12*/ addr8, sp, #S_PC stmdbr8, {sp, lr}^/* Calling SP, LR*/ strlr, [r8, #0]/* Save calling PC*/ mrsr6, spsr strr6, [r8, #4]/* Save CPSR*/ strr0, [r8, #8]/* Save OLD_R0*/ movr0, sp .endm .macroirq_restore_user_regs ldmiasp, {r0 - lr}^@ Calling r0 - lr movr0, r0 ldrlr, [sp, #S_PC]@ Get PC addsp, sp, #S_FRAME_SIZE subspc, lr, #4@ return & move spsr_svc into cpsr .endm .macro get_bad_stack ldrr13, _armboot_start@ setup our mode stack subr13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN) subr13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack strlr, [r13]@ save caller lr / spsr mrslr, spsr strlr, [r13, #4] movr13, #MODE_SVC@ prepare SVC-Mode msrspsr_c, r13 movlr, pc movspc, lr .endm .macro get_irq_stack@ setup IRQ stack ldrsp, IRQ_STACK_START .endm .macro get_fiq_stack@ setup FIQ stack ldrsp, FIQ_STACK_START .endm /****************************************************************************/ /**/ /* exception handlers*/ /**/ /****************************************************************************/ .align5 undefined_instruction: get_bad_stack bad_save_user_regs bldo_undefined_instruction .align5 software_interrupt: get_bad_stack bad_save_user_regs bldo_software_interrupt .align5 prefetch_abort: get_bad_stack bad_save_user_regs bldo_prefetch_abort .align5 data_abort: get_bad_stack bad_save_user_regs bldo_data_abort .align5 not_used: get_bad_stack bad_save_user_regs bldo_not_used #ifdef CONFIG_USE_IRQ .align5 irq: get_irq_stack irq_save_user_regs bldo_irq irq_restore_user_regs .align5 fiq: get_fiq_stack irq_save_user_regs/* someone ought to write a more*/ bldo_fiq/* effiction fiq_save_user_regs*/ irq_restore_user_regs #else .align5 irq: get_bad_stack bad_save_user_regs bldo_irq .align5 fiq: get_bad_stack bad_save_user_regs bldo_fiq #endif /****************************************************************************/ /**/ /* Reset function: the PXA250 doesn't have a reset function, so we have to*/ /* perform a watchdog timeout for a soft reset.*/ /* The processor contains a 32-bits OS timer that is clocked by the 3.864 MHZ oscillator.The Operating System Count register(OSCR) is a free running up-counter.The OS timer also contains four 32-bit match registers (OSMR3, OSMR2,OSMR1,OSMR0).Developers can read and write to each register. When the value in the OSCR is equal to the value within any of the match register,and the interrupt enable bit is set,the corresponding bit in the OSSR is set.These bits are also routed to the interrupt controller where they can be programmed to cause an interrupt. OSMR3 also serves as a watchdog match register that resets the processor when a match occurs provided the OS Timer Watchdog Match Enable Register(OWER) is set.You must initialize the OSCR and OSMR registers and clear any set status bits before the FIQ and IRQ interrupts are enabled within the CPU /****************************************************************************/ .align5 .globl reset_cpu /* FIXME: this code is PXA250 specific. How is this handled on*/ /*other XScale processors?*/ reset_cpu: /*************************************************************************** * This bit is set by writing a one to it and can only be cleared by * one of the reset functions such as, hardware reset,sleep reset, watchdog * reset,and GPIO reset * 只有最低位可进行设置<31:1>不能设置 * 0-----OSMR3 match will not cause a reset of the processor * 1-----OSMR3 match will cause a reset of the processor ****************************************************************************/ /* We set OWE:WME ( watchdog enable) and wait until timeout happens*/ ldrr0, OSTIMER_BASE//0x40a00000 ldrr1, [r0, #OWER]//OWER OS Timer Watchdog Enable Register orrr1, r1, #0x0001/* bit0: WME*/ strr1, [r0, #OWER] /* OS timer does only wrap every 1165 seconds, so we have to set*/ /* the match register as well.*/ ldrr1, [r0, #OSCR]/* read OS timer OSCR:OS Timer Counter Register */ addr1, r1, #0x800/* let OSMR3 match after*/ addr1, r1, #0x800/* 4096*(1/3.6864MHz)=1ms*/ strr1, [r0, #OSMR3] reset_endless: breset_endless |