- 浏览: 686174 次
文章分类
仿照linux下的uboot来DIY自己的arm11(6410)的bootloader
转载请注明出处:http://blog.csdn.net/hongkangwl/article/details/16353709
U-Boot是用于多种嵌入式CPU( MIPS、x86、ARM等)的bootloader程序,U-Boot不仅支持嵌入式Linux系统的引导,还支持VxWorks, QNX等多种嵌入式操作系统。
查看S3C6410_Internal_ROM_Booting.pdf可看到系统启动的原理图如下:
linux的Uboot分析
对于.lds文件,它定义了整个程序编译之后的连接过程,决定了一个可执行程序的各个段的存储位置。
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
secname和contents是必须的,其他的都是可选的。下面挑几个常用的看看:
1、secname:段名
2、contents:决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等)
3、start:本段连接(运行)的地址,如果没有使用AT(ldadr),本段存储的地址也是start。GNU网站上说start可以用任意一种描述地址的符号来描述。
4、AT(ldadr):定义本段存储(加载)的地址。
结合u-boot.lds进行分析:
OUTPUT_FORMAT("elf32littlearm", "elf32littlearm", "elf32littlearm") //指定输出可执行文件是elf格式,32位ARM指令,小端 OUTPUT_ARCH(arm) //指定输出可执行文件的平台为ARM ENTRY(_start) //指定输出可执行文件的起始代码段为_start. SECTIONS { . = 0x00000000 ; 从0x0位置开始 . = ALIGN(4) ; 代码以4字节对齐 .text : ;指定代码段 { cpu/s3c64xx/start.o (.text) //代码的第一个代码部分 cpu/s3c64xx/s3c6410/cpu_init.o// (.text)//初始化CPU cpu/s3c64xx/onenand_cp.o // (.text) cpu/s3c64xx/nand_cp.o //(.text)//拷贝nandflash 8K至stepstone cpu/s3c64xx/movi.o //(.text)//把nandflash剩余部分拷贝至DRAM中运行 *(.text)//代码剩余部分 lib_arm/div0.o } . = ALIGN(4) .rodata : { *(.rodata) }//指定只读数据段 . = ALIGN(4) .data : { *(.data) } //指定读/写数据段 . = ALIGN(4) .got : { *(.got) } //指定got段, got段式是uboot自定义的一个段, 非标准段 __u_boot_cmd_start = . //把__u_boot_cmd_start赋值为当前位置, 即起始位置 .u_boot_cmd : { *(.u_boot_cmd) } //指定u_boot_cmd段, uboot把所有的uboot命令放在该段. __u_boot_cmd_end = .//把__u_boot_cmd_end赋值为当前位置,即结束位置 . = ALIGN(4) __bss_start = .// 把__bss_start赋值为当前位置,即bss段的开始位置 .bss : { *(.bss) }// 指定bss段 _end = .//把_end赋值为当前位置,即bss段的结束位置 }
在链接器脚本中可以看出,程序入口时start.s,下面开始分析start.s/* * armboot - Startup Code for S3C6400/ARM1176 CPU-core * * Copyright (c) 2007 Samsung Electronics * * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * 2007-09-21 - Restructured codes by jsgood (jsgood.yang@samsung.com) * 2007-09-21 - Added moviNAND and OneNAND boot codes by jsgood (jsgood.yang@samsung.com) * Base codes by scsuh (sc.suh) */ #include <config.h> #include <version.h> #ifdef CONFIG_ENABLE_MMU #include <asm/proc/domain.h> #endif #include <regs.h> #ifndef CONFIG_ENABLE_MMU #ifndef CFG_PHY_UBOOT_BASE #define CFG_PHY_UBOOT_BASE CFG_UBOOT_BASE #endif #endif /* ************************************************************************* * * Jump vector table as in table 3.1 in [1] * ************************************************************************* */ //global声明一个符号可被其它文件引用,相当于声明了一个全局变量,.globl与.global相同。 //该部分为处理器的异常处理向量表。地址范围为0x0000 0000 ~ 0x0000 0020,刚好8条指令。 (1) .globl _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _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 _pad: .word 0x12345678 /* now 16*4=64 */ .global _end_vect _end_vect: .balignl 16,0xdeadbeef /* ************************************************************************* * * Startup Code (reset vector) * * do important init only if we don't start from memory! * setup Memory and board specific bits prior to relocation. * relocate armboot to ram * setup stack * ************************************************************************* */ // TEXT_BASE在开发板相关的目录中的config.mk文件中定义, 它定义了 // 代码在运行时所在的地址, 那么_TEXT_BASE中保存了这个地址 _TEXT_BASE: .word TEXT_BASE /* * Below variable is very important because we use MMU in U-Boot. * Without it, we cannot run code correctly before MMU is ON. * by scsuh. */ _TEXT_PHY_BASE: .word CFG_PHY_UBOOT_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: .word 0x0badc0de /* IRQ stack memory (calculated at run-time) */ .globl FIQ_STACK_START FIQ_STACK_START: .word 0x0badc0de #endif /* * the actual reset code */ // MRS {<cond>} Rd,CPSR|SPSR 将CPSR|SPSR传送到Rd // 使用这两条指令将状态寄存器传送到一般寄存器,只修改必要的位,再将结果传送回状态寄存器,这样可以最好地完成对CRSP或者SPSR的修改 // MSR {<cond>} CPSR_<field>|SPSR_<field>,Rm 或者是 MSR {<cond>} CPSR_f|SPSR_f,#<32-bit immediate> // MRS与MSR配合使用,作为更新PSR的“读取--修改--写回”序列的一部分 // bic r0,r1,r2 ;r0:=r1 and not r2 // orr ro,r1,r2 ;r0:=r1 or r2 // 这几条指令执行完毕后,进入SVC模式,该模式主要用来处理软件中断(SWI) reset: /* * set the cpu to SVC32 mode */ mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0 /* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ /* * we do sys-critical inits only at reboot, * not when booting from ram! */ //cpu初始化 cpu_init_crit: /* * flush v4 I/D caches */ //关闭I/Dcaches mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ /* * disable MMU stuff and caches */ //关闭MMU mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) orr r0, r0, #0x00000002 @ set bit 2 (A) Align orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache mcr p15, 0, r0, c1, c0, 0 /* Peri port setup */ //外设基地址设置 ldr r0, =0x70000000 orr r0, r0, #0x13 mcr p15,0,r0,c15,c2,4 @ 256M(0x70000000-0x7fffffff) //因为是只定义了6410,别的都忽略 #ifdef CONFIG_BOOT_ONENAND ldr r0, =0x70000000 @ onenand controller setup orr r0, r0, #0x100000 ldr r1, =0x4000 orr r1, r1, #0xe0 str r1, [r0] //关闭6410的看门狗 #if defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430) orr r0, r0, #300 @ disable watchdog mov r1, #1 str r1, [r0] mov r1, #0x23000000 @ start buffer register orr r1, r1, #0x30000 orr r1, r1, #0xc800 #else //不执行 mov r1, =0x20000000 @ start buffer register orr r1, r1, #0xc30000 orr r1, r1, #0xc800 #endif sub r0, r1, #0x0400 @ start address1 register ldr r2, [r1, #0x84] @ ecc bypass orr r2, r2, #0x100 str r2, [r1, #0x84] mov r3, #0x0 @ DFS, FBA str r3, [r0, #0x00] str r3, [r0, #0x04] @ select dataram for DDP as 0 mov r4, #0x104 @ interrupt register mov r5, #0x0002 @ FPA, FSA mov r6, #0x0800 @ BSA onenand_bl1_load: str r5, [r0, #0x1c] @ save FPA, FSA orr r6, r6, #0x02 @ BSC str r6, [r1, #0x00] @ save BSA, BSC str r3, [r1, r4] @ clear interrupt str r3, [r1, #0x80] @ write load command mov r7, #0x100 @ need small delay onenand_wait_loop1: subs r7, r7, #0x1 bne onenand_wait_loop1 add r5, r5, #0x2 @ next FPA, FSA sub r6, r6, #0x2 add r6, r6, #0x200 @ next BSA cmp r5, #0x8 bne onenand_bl1_load #endif /* * Go setup Memory and board specific bits prior to relocation. */ bl lowlevel_init /* go setup pll,mux,memory */ /* when we already run in ram, we don't need to relocate U-Boot. * and actually, memory controller must be configured before U-Boot * is running in ram. */ ldr r0, =0xff000fff bic r1, pc, r0 /* r0 <- current base addr of code */ ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */ bic r2, r2, r0 /* r0 <- current base addr of code */ cmp r1, r2 /* compare r0, r1 */ beq after_copy /* r0 == r1 then skip flash copy */ //使用nandflash,此处忽略 #ifdef CONFIG_BOOT_NOR /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_PHY_BASE /* r1 <- destination */ ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 <- size of armboot */ add r2, r0, r2 /* r2 <- source end address */ nor_copy_loop: ldmia r0!, {r3-r10} /* copy from source address [r0] */ stmia r1!, {r3-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end addreee [r2] */ ble nor_copy_loop b after_copy #endif //从Nandflash启动,执行此处代码 #ifdef CONFIG_BOOT_NAND mov r0, #0x1000 bl copy_from_nand #endif #ifdef CONFIG_BOOT_MOVINAND ldr sp, _TEXT_PHY_BASE bl movi_bl2_copy b after_copy #endif //使用nandflash,此处忽略 #ifdef CONFIG_BOOT_ONENAND ldr sp, =0x50000000 @ temporary stack //设置地址并分配空间 #ifdef CONFIG_S3C6400 mov r1, =0x20000000 @ start buffer register orr r1, r1, #0xc30000 orr r1, r1, #0xc800 #else mov r1, #0x23000000 @ start buffer register orr r1, r1, #0x30000 orr r1, r1, #0xc800 #endif ldr r2, [r1, #0x84] @ ecc bypass orr r2, r2, #0x100 str r2, [r1, #0x84] sub r0, r1, #0x0400 @ start address1 register str r3, [r0, #0x00] str r3, [r0, #0x04] @ select dataram for DDP as 0 mov r4, #0x104 @ interrupt register mov r6, #0x0c00 @ fixed dataram1 sector number str r6, [r1, #0x00] mov r3, #0x0 @ DFS, FBA mov r5, #0x0000 @ FPA, FSA ldr r9, =CFG_PHY_UBOOT_BASE @ destination onenand_bl2_load: str r3, [r0, #0x00] @ save DFS, FBA str r5, [r0, #0x1c] @ save FPA, FSA mov r7, #0x0 @ clear interrupt str r7, [r1, r4] str r7, [r1, #0x80] @ write load command mov r8, #0x1000 onenand_wait_loop2: subs r8, r8, #0x1 bne onenand_wait_loop2 onenand_wait_int: @ wait INT and RI ldr r7, [r1, r4] mov r8, #0x8000 orr r8, r8, #0x80 tst r7, r8 beq onenand_wait_int mov r7, #0x0 @ clear interrupt str r7, [r1, r4] mov r8, #0xc00 @ source address (dataram1) mov r10, #0x40 @ copy loop count (64 = 2048 / 32) stmia sp, {r0-r7} @ backup //把代码拷贝至DRAM onenand_copy_to_ram: ldmia r8!, {r0-r7} stmia r9!, {r0-r7} subs r10, r10, #0x1 bne onenand_copy_to_ram ldmia sp, {r0-r7} @ restore add r5, r5, #0x4 @ next FPA cmp r5, #0x100 @ last FPA? bne onenand_bl2_load /* next block */ mov r5, #0x0 @ reset FPA add r3, r3, #0x1 @ next FBA cmp r3, #0x2 @ last FBA? bne onenand_bl2_load b after_copy #endif #ifdef CONFIG_BOOT_ONENAND_IROM ldr sp, _TEXT_PHY_BASE bl onenand_bl2_copy b after_copy #endif //进入DRAM执行 after_copy: #ifdef CONFIG_ENABLE_MMU 开MMU enable_mmu: /* enable domain access */ ldr r5, =0x0000ffff mcr p15, 0, r5, c3, c0, 0 @ load domain access register /* Set the TTB register */ ldr r0, _mmu_table_base ldr r1, =CFG_PHY_UBOOT_BASE ldr r2, =0xfff00000 bic r0, r0, r2 orr r1, r0, r1 mcr p15, 0, r1, c2, c0, 0 /* Enable the MMU */ mmu_on: mrc p15, 0, r0, c1, c0, 0 orr r0, r0, #1 /* Set CR_M to enable MMU */ mcr p15, 0, r0, c1, c0, 0 nop nop nop nop #endif skip_hw_init: /* Set up the stack */ stack_setup: //分配堆栈 #ifdef CONFIG_MEMORY_UPPER_CODE ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0xc) #else ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 /* leave 3 words for abort-stack */ #endif //将未初始化数据段_bss_start----_bss_end中的数据清零 clear_bss: ldr r0, _bss_start /* find start of bss segment */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000 /* clear */ clbss_l: str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 ble clbss_l ldr pc, _start_armboot _start_armboot: .word start_armboot #ifdef CONFIG_ENABLE_MMU _mmu_table_base: .word mmu_table #endif /* * copy U-Boot to SDRAM and jump to ram (from NAND or OneNAND) * r0: size to be compared * Load 1'st 2blocks to RAM because U-boot's size is larger than 1block(128k) size */ .globl copy_from_nand //从NANDflash中拷贝8K以后的代码值DRAM copy_from_nand: mov r10, lr /* save return address */ mov r9, r0 /* get ready to call C functions */ ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */ sub sp, sp, #12 mov fp, #0 /* no previous frame, so fp=0 */ mov r9, #0x1000 bl copy_uboot_to_ram 3: tst r0, #0x0 bne copy_failed ldr r0, =0x0c000000 ldr r1, _TEXT_PHY_BASE 1: ldr r3, [r0], #4 ldr r4, [r1], #4 teq r3, r4 bne compare_failed /* not matched */ subs r9, r9, #4 bne 1b 4: mov lr, r10 /* all is OK */ mov pc, lr copy_failed: nop /* copy from nand failed */ b copy_failed compare_failed: nop /* compare failed */ b compare_failed /* * we assume that cache operation is done before. (eg. cleanup_before_linux()) * actually, we don't need to do anything about cache if not use d-cache in U-Boot * So, in this function we clean only MMU. by scsuh * * void theLastJump(void *kernel, int arch_num, uint boot_params); */ #ifdef CONFIG_ENABLE_MMU .globl theLastJump theLastJump: mov r9, r0 ldr r3, =0xfff00000 ldr r4, _TEXT_PHY_BASE adr r5, phy_last_jump bic r5, r5, r3 orr r5, r5, r4 mov pc, r5 phy_last_jump: /* * disable MMU stuff */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */ bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */ orr r0, r0, #0x00000002 /* set bit 2 (A) Align */ orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */ mcr p15, 0, r0, c1, c0, 0 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ mov r0, #0 mov pc, r9 #endif /* ************************************************************************* * * Interrupt handling * ************************************************************************* */ //中断处理 @ @ IRQ stack frame. @ #define S_FRAME_SIZE 72 #define S_OLD_R0 68 #define S_PSR 64 #define S_PC 60 #define S_LR 56 #define S_SP 52 #define S_IP 48 #define S_FP 44 #define S_R10 40 #define S_R9 36 #define S_R8 32 #define S_R7 28 #define S_R6 24 #define S_R5 20 #define S_R4 16 #define S_R3 12 #define S_R2 8 #define S_R1 4 #define S_R0 0 #define MODE_SVC 0x13 #define I_BIT 0x80 /* * use bad_save_user_regs for abort/prefetch/undef/swi ... * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling */ .macro bad_save_user_regs sub sp, sp, #S_FRAME_SIZE @ carve out a frame on current user stack stmia sp, {r0 - r12} @ Save user registers (now in svc mode) r0-r12 ldr r2, _armboot_start sub r2, r2, #(CFG_MALLOC_LEN) sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack ldmia r2, {r2 - r3} @ get values for "aborted" pc and cpsr (into parm regs) add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack add r5, sp, #S_SP mov r1, lr stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr mov r0, sp @ save current stack into r0 (param register) .endm .macro irq_save_user_regs sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0-r12 add r8, sp, #S_PC @ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good. stmdb r8, {sp, lr}^ @ Calling SP, LR str lr, [r8, #0] @ Save calling PC mrs r6, spsr str r6, [r8, #4] @ Save CPSR str r0, [r8, #8] @ Save OLD_R0 mov r0, sp .endm .macro irq_restore_user_regs ldmia sp, {r0 - lr}^ @ Calling r0 - lr mov r0, r0 ldr lr, [sp, #S_PC] @ Get PC add sp, sp, #S_FRAME_SIZE subs pc, lr, #4 @ return & move spsr_svc into cpsr .endm .macro get_bad_stack ldr r13, _armboot_start @ setup our mode stack (enter in banked mode) sub r13, r13, #(CFG_MALLOC_LEN) @ move past malloc pool sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ move to reserved a couple spots for abort stack str lr, [r13] @ save caller lr in position 0 of saved stack mrs lr, spsr @ get the spsr str lr, [r13, #4] @ save spsr in position 1 of saved stack mov r13, #MODE_SVC @ prepare SVC-Mode @ msr spsr_c, r13 msr spsr, r13 @ switch modes, make sure moves will execute mov lr, pc @ capture return pc movs pc, lr @ jump to next instruction & switch modes. .endm .macro get_bad_stack_swi sub r13, r13, #4 @ space on current stack for scratch reg. str r0, [r13] @ save R0's value. ldr r0, _armboot_start @ get data regions start sub r0, r0, #(CFG_MALLOC_LEN) @ move past malloc pool sub r0, r0, #(CFG_GBL_DATA_SIZE+8) @ move past gbl and a couple spots for abort stack str lr, [r0] @ save caller lr in position 0 of saved stack mrs r0, spsr @ get the spsr str lr, [r0, #4] @ save spsr in position 1 of saved stack ldr r0, [r13] @ restore r0 add r13, r13, #4 @ pop stack entry .endm .macro get_irq_stack @ setup IRQ stack ldr sp, IRQ_STACK_START .endm .macro get_fiq_stack @ setup FIQ stack ldr sp, FIQ_STACK_START .endm /* * exception handlers */ //几种工作模式 .align 5 undefined_instruction: get_bad_stack bad_save_user_regs bl do_undefined_instruction .align 5 software_interrupt: get_bad_stack_swi bad_save_user_regs bl do_software_interrupt .align 5 prefetch_abort: get_bad_stack bad_save_user_regs bl do_prefetch_abort .align 5 data_abort: get_bad_stack bad_save_user_regs bl do_data_abort .align 5 not_used: get_bad_stack bad_save_user_regs bl do_not_used #ifdef CONFIG_USE_IRQ .align 5 irq: get_irq_stack irq_save_user_regs bl do_irq irq_restore_user_regs .align 5 fiq: get_fiq_stack /* someone ought to write a more effiction fiq_save_user_regs */ irq_save_user_regs bl do_fiq irq_restore_user_regs #else .align 5 irq: get_bad_stack bad_save_user_regs bl do_irq .align 5 fiq: get_bad_stack bad_save_user_regs bl do_fiq #endif .align 5 .global arm1136_cache_flush arm1136_cache_flush: mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache mov pc, lr @ back to caller #if defined(CONFIG_INTEGRATOR) && defined(CONFIG_ARCH_CINTEGRATOR) /* Use the IntegratorCP function from board/integratorcp/platform.S */ #elif defined(CONFIG_S3C64XX) /* For future usage of S3C64XX*/ #else .align 5 .globl reset_cpu reset_cpu: ldr r1, rstctl /* get addr for global reset reg */ mov r3, #0x2 /* full reset pll+mpu */ str r3, [r1] /* force reset */ mov r0, r0 _loop_forever: b _loop_forever rstctl: .word PM_RSTCTRL_WKUP #endif
现我们仿照着uboot对ARM11所做的初始化操作,编写裸机bootloader。
首先编写makefile代码
all: start.o arm-linux-ld -Tgboot.lds -o gboot.elf $^ arm-linux-objcopy -O binary gboot.elf gboot.bin %.o : %.S arm-linux-gcc -g -c $^ %.o : %.c arm-linux-gcc -g -c $^ .PHONY: clean clean: rm *.o *.elf *.bin
然后编写链接器脚本diyboot.lds
OUTPUT_ARCH(arm)//arm内核 ENTRY(_start)//代码入口start SECTIONS { . = 0x50008000; . = ALIGN(4); .text : //代码段 { start.o (.text) //start.s是初始化代码 *(.text) } . = ALIGN(4); //数据段,4字节对齐 .data : { *(.data) } . = ALIGN(4); bss_start = .; //当前代码所在地址 .bss : { *(.bss) } bss_end = .; //当前代码结束地址 }
之后开始编写我们自己的start.S1. 添加向量表
查看三星官方的ARM Architecture Reference Manual.pdf文件,可以看到ARM11共有七种模式
设计代码如下:
.text .global _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _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 undefined_instruction: nop software_interrupt: nop prefetch_abort: nop data_abort: nop not_used: nop irq: nop fiq: nop reset: nop
2. 设置SVC模式如手册上表所示
要把CSRP的后五位M[4:0]设置为10011
另外要屏蔽中断和快速中断,他们的屏蔽室I为和F位
要把I位和F位设置为1 因此CSRP的后8为设置为11010011即为0XD3
因此修改代码如下:
.text .global _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _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 undefined_instruction: nop software_interrupt: nop prefetch_abort: nop data_abort: nop not_used: nop irq: nop fiq: nop reset: bl set_svc set_svc: mrs r0, cpsr //把CPSR导入到r0寄存器 bic r0, r0, #0x1f //把CPSR的后五位清零 orr r0, r0, #0xd3//把CPSR的后八位设置为0XD3 msr cpsr, r0//把r0值导入到CPSR mov pc, lr//退出CPSR寄存器
3.关闭看门狗
在嵌入式领域,有些系统需要长期运行在无人看守的环境。在运行过程中,难免不出现系统死机的情况,这时就需要系统自身带有一种自动重启的功能。watchdog一般是一个硬件模块,其作用就是在系统死机时,帮助系统实现自动重启。Watchdog在硬件上实现了计时功能,启动计时后,用户(软件)必须在计时结束前重新开始计时,俗称“喂狗”,如果到超时的时候还没有重新开始计时,那么它就认为系统是死机了,就自动重启系统。
关闭看门狗和看门狗中断就是设置WTCON的0位和2位为0,修改代码如下:
.text .global _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _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 undefined_instruction: nop software_interrupt: nop prefetch_abort: nop data_abort: nop not_used: nop irq: nop fiq: nop reset: bl set_svc bl disable_watchdog set_svc: mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr, r0 mov pc, lr #define pWTCON 0x7e004000 //定义WTCON的地址 disable_watchdog: ldr r0, =pWTCON //把地址装入r0 mov r1, #0x0 //r1清零 str r1, [r0] //把r1的0装入ro装入的WTCON所在的地址 mov pc, lr //返回
4.关闭中断中断使能寄存器如图所示:
很简单只要把VIC0INTENABLE和VIC1INTENABLE全部设置为0即可,修改代码如下:
.text .global _start _start: b reset ldr pc, _undifined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _undifined_instruction: .word undifined_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 reset undifined_instruction: nop software_interrupt: nop prefetch_abort: nop data_abort: nop not_used: nop irq: nop fiq: nop reset: bl set_svc bl disable_watchdog bl disable_interrupt set_svc: mrs r0, cpsr bic r0, r0,#0x1f orr r0, r0,#0xd3 msr cpsr, r0 mov pc, lr #define pWTCON 0x7e004000 disable_watchdog: ldr r0, =pWTCON mov r1, #0x0 str r1, [r0] mov pc, lr disable_interrupt: //把VIC0INTENABLE和VIC1INTENABLE全部设置为0 mvn r1,#0x0 ldr r0,=0x71200014 str r1,[r0] ldr r0,=0x71300014 str r1,[r0] mov pc, lr
5.关闭MMUMMU作用就是把虚拟地址映射到物理地址上去
初始化完成前必须先关掉。
关闭mmu按照CP15的寄存器说明进行:
设计代码如下:
.text .global _start _start: b reset ldr pc, _undifined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _undifined_instruction: .word undifined_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 reset undifined_instruction: nop software_interrupt: nop prefetch_abort: nop data_abort: nop not_used: nop irq: nop fiq: nop reset: bl set_svc bl disable_watchdog bl disable_interrupt bl disable_mmu set_svc: mrs r0, cpsr bic r0, r0,#0x1f orr r0, r0,#0xd3 msr cpsr, r0 mov pc, lr #define pWTCON 0x7e004000 disable_watchdog: ldr r0, =pWTCON mov r1, #0x0 str r1, [r0] mov pc, lr disable_interrupt: mvn r1,#0x0 ldr r0,=0x71200014 str r1,[r0] ldr r0,=0x71300014 str r1,[r0] mov pc, lr disable_mmu: mcr p15,0,r0,c7,c7,0 mrc p15,0,r0,c1,c0,0 bic r0, r0, #0x00000007 mcr p15,0,r0,c1,c0,0 mov pc, lr
相关推荐
uboot分析嵌入式Linux系统开发技术详解基于ARM
uboot ARM bootloader 源代码
arm linux启动资料,关于uboot等arm linux启动资料,关于uboot等arm linux启动资料,关于uboot等arm linux启动资料,关于uboot等arm linux启动资料,关于uboot等
arm linux uboot bootloader 详解
Rockchip Linux SDK uboot logo 开发指南
linux uboot 详细分析过程 uboot 分析详解
ARM11 S3C6410 UBOOT和linux内核部分
基于Uboot构建嵌入式Linux的Bootloader基于Uboot构建嵌入式Linux的Bootloader
Uboot启动过程分析,学习,需要移植uboot的可以看下
本资源为我的博客《实验1后篇——引导linux与uboot命令分析》的资源与代码。其中主要包含了关于两个实验的源码:引导linux,uboot命令模拟的实现,另外是博客中所描述的linux启动流程与编译流程的相关源码与文档。...
uboot start_armboot分析
一本培训机构出的uboot移植操作书,非常重视实践,很有使用价值,对于对uboot有大概印象的很有使用价值
uboot学习之bootloader,感兴趣的朋友下载去研究研究
《创龙ARM Linux通用驱动视频》 视频简介:本视频主要以uboot-2016.05、Linux-4.4.12为蓝本,讲解uboot的基础知识、启动流程和常用命令,达到易学、易懂、易上手的目的。并且讲解Linux驱动的框架以及经常使用到的...
介绍了如何实现在线升级linux系统,即uboot,kernel,rootfs, 以及相关的前提知识和准备工作 目录 1. 正文之前 3 1.1. 此文目的 3 1.2. 一点说明 3 2. 嵌入式系统中,如何在Linux运行的时候去升级Linux系统 4 2.1. ...
linux-uboot和套结字多线程linux-uboot和套结字多线程linux-uboot和套结字多线程linux-uboot和套结字多线程
Linux uboot 自引导内核 Linux uboot 自引导内核 Linux uboot 自引导内核
Uboot_for_Tiny6410_移植步骤详解
arm linux bootloader uboot 分析
UBOOT源码内核镜像 arm9 一直好的