首先要了解210板子的内存配置情况,我的板子是512M内存,DMC0上接了256M,DMC1接了256M,为了保证地址连续,内存地址只能是0x30000000 ~ 0x4FFFFFFF
第一步运行start.S:
_TEXT_BASE: .word TEXT_BASE//存放基址 /*由makefile内容: x210_nand_config : unconfig @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110 @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk 得知TXET_BASE = 0xc3e00000,为什么超出了0x30000000 ~ 0x4FFFFFFF,因为dnw烧写在0xd0000010处的启动代码初始化DDR时做了内存映射的工作,这个映射方式是简单的线性映射,只是在把内存起始地址增加至0xc0000000。后面会提到。 */ /* * 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 .globl _armboot_start _armboot_start: .word _start /* * These are defined in the board-specific linker script. */ .globl _bss_start _bss_start: .word __bss_start .globl _bss_end _bss_end: .word _end #if defined(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 */ reset: /* * set the cpu to SVC32 mode and IRQ & FIQ disable */ @;mrs r0,cpsr @;bic r0,r0,#0x1f @;orr r0,r0,#0xd3 @;msr cpsr,r0 msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC /* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ /* * we do sys-critical inits only at reboot, * not when booting from ram! */ cpu_init_crit: #ifndef CONFIG_EVT1 #if 0 bl v7_flush_dcache_all #else bl disable_l2cache mov r0, #0x0 @ mov r1, #0x0 @ i mov r3, #0x0 mov r4, #0x0 lp1: mov r2, #0x0 @ j lp2: mov r3, r1, LSL #29 @ r3 = r1(i) <<29 mov r4, r2, LSL #6 @ r4 = r2(j) <<6 orr r4, r4, #0x2 @ r3 = (i<<29)|(j<<6)|(1<<1) orr r3, r3, r4 mov r0, r3 @ r0 = r3 bl CoInvalidateDCacheIndex add r2, #0x1 @ r2(j)++ cmp r2, #1024 @ r2 < 1024 bne lp2 @ jump to lp2 add r1, #0x1 @ r1(i)++ cmp r1, #8 @ r1(i) < 8 bne lp1 @ jump to lp1 bl set_l2cache_auxctrl bl enable_l2cache #endif #endif bl disable_l2cache bl set_l2cache_auxctrl_cycle bl enable_l2cache /* * Invalidate L1 I/D */ mov r0, #0 @ set up for MCR mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs mcr p15, 0, r0, c7, c5, 0 @ invalidate icache /* * disable MMU stuff and caches */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002000 @ clear bits 13 (--V-) bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB mcr p15, 0, r0, c1, c0, 0 /* Read booting information */ ldr r0, =PRO_ID_BASE ldr r1, [r0,#OMR_OFFSET] bic r2, r1, #0xffffffc1 #ifdef CONFIG_VOGUES /* PS_HOLD(GPH0_0) set to output high */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x00000001 str r1, [r0, #GPH0CON_OFFSET] ldr r1, =0x5500 str r1, [r0, #GPH0PUD_OFFSET] ldr r1, =0x01 str r1, [r0, #GPH0DAT_OFFSET] #endif /* NAND BOOT */ cmp r2, #0x0 @ 512B 4-cycle moveq r3, #BOOT_NAND cmp r2, #0x2 @ 2KB 5-cycle moveq r3, #BOOT_NAND cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC moveq r3, #BOOT_NAND cmp r2, #0x6 @ 4KB 5-cycle 16-bit ECC moveq r3, #BOOT_NAND cmp r2, #0x8 @ OneNAND Mux moveq r3, #BOOT_ONENAND /* SD/MMC BOOT */ cmp r2, #0xc moveq r3, #BOOT_MMCSD /* NOR BOOT */ cmp r2, #0x14 moveq r3, #BOOT_NOR /*以上代码判断启动方式,保存到r3中*/ #if 0 /* Android C110 BSP uses OneNAND booting! */ /* For second device booting */ /* OneNAND BOOTONG failed */ cmp r2, #0x8 moveq r3, #BOOT_SEC_DEV #endif /* Uart BOOTONG failed */ cmp r2, #(0x1<<4) moveq r3, #BOOT_SEC_DEV ldr r0, =INF_REG_BASE str r3, [r0, #INF_REG3_OFFSET] /* * Go setup Memory and board specific bits prior to relocation. */ ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */ sub sp, sp, #12 /* set stack */ mov fp, #0 bl lowlevel_init /* go setup pll,mux,memory *//*进入 /* To hold max8698 output before releasing power on switch, * set PS_HOLD signal to high */ @ldr r0, =0xE010E81C /* PS_HOLD_CONTROL register */ @ldr r1, =0x00005301 /* PS_HOLD output high */ @str r1, [r0] /* get ready to call C functions */ ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */设置SP = 0x33e00000 sub sp, sp, #12 mov fp, #0 /* no previous frame, so fp=0 */ /* 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 */ 这里判断当前是否已经正常启动,如果r1 = r2,说明uboot已完成自拷贝,则跳转到after_copy,在after_copy中设置MMU映射表之后,开始正常运行初始化板子。 after_copy: #if defined(CONFIG_ENABLE_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 mcr p15, 0, r0, c1, c0, 0 nop nop nop nop #endif skip_hw_init: /* Set up the stack */ stack_setup: #if defined(CONFIG_MEMORY_UPPER_CODE) ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000) #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 */ #if defined(CONFIG_USE_IRQ) sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 /* leave 3 words for abort-stack */ #endif 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 /*进入启动程序*/ 回头看如果r1 != r2,那么需要进行自拷贝: #if defined(CONFIG_EVT1) /* If BL1 was copied from SD/MMC CH2 */ ldr r0, =0xD0037488 /*判断是否是SD卡启动*/ ldr r1, [r0] ldr r2, =0xEB200000 cmp r1, r2 beq mmcsd_boot #endif ldr r0, =INF_REG_BASE ldr r1, [r0, #INF_REG3_OFFSET] cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */ beq nand_boot cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand */ beq onenand_boot cmp r1, #BOOT_MMCSD beq mmcsd_boot cmp r1, #BOOT_NOR beq nor_boot cmp r1, #BOOT_SEC_DEV beq mmcsd_boot nand_boot: mov r0, #0x1000 bl copy_from_nand b after_copy onenand_boot: bl onenand_bl2_copy b after_copy mmcsd_boot: #if DELETE ldr sp, _TEXT_PHY_BASE /*这里不会执行*/ sub sp, sp, #12 mov fp, #0 #endif bl movi_bl2_copy b after_copy nor_boot: bl read_hword b after_copy
after_copy:
after_copy: #if defined(CONFIG_ENABLE_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 mcr p15, 0, r0, c1, c0, 0 nop nop nop nop #endif skip_hw_init: /* Set up the stack */ stack_setup: #if defined(CONFIG_MEMORY_UPPER_CODE) ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000) #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 */ #if defined(CONFIG_USE_IRQ) sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 /* leave 3 words for abort-stack */ #endif 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
我们直接看copy_from_nand
/* * 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 copy_from_nand: push {lr} /* save return address */ mov r9, r0 mov r9, #0x100 /* Compare about 8KB */ bl copy_uboot_to_ram tst r0, #0x0 bne copy_failed #if defined(CONFIG_EVT1) ldr r0, =0xd0020000 #else ldr r0, =0xd0030000 #endif ldr r1, _TEXT_PHY_BASE /* 0x23e00000 */ 1: ldr r3, [r0], #4 ldr r4, [r1], #4 teq r3, r4 bne compare_failed /* not matched */ subs r9, r9, #4 bne 1b pop {pc} /* all is OK */ copy_failed: nop /* copy from nand failed */ b copy_failed compare_failed: nop /* compare failed */ b compare_failed
看看copy_uboot_toram,因为已经设置栈,可以执行C程序了;
#define NAND_CONTROL_ENABLE() (NFCONT_REG |= (1 << 0)) static void nand_readpage (ulong col, ulong row, uchar* buf, int len) { int i; NAND_ENABLE_CE(); NFCMD_REG = NAND_CMD_READ0; /* Write Column Address */ NFADDR_REG = (col) & 0xff; NFADDR_REG = (col >> 8) & 0xff; /* Write Row Address */ NFADDR_REG = (row) & 0xff; NFADDR_REG = (row >> 8) & 0xff; NFADDR_REG = (row >> 16) & 0xff; NFCMD_REG = NAND_CMD_READSTART; NF_TRANSRnB(); for (i = 0; i < len; i++) { buf[i] = NFDATA8_REG; } NAND_DISABLE_CE(); } static int nand_isbad (ulong addr) { int i; int page_size = 2048; uchar oob[2]; if (addr == 0) return 0; nand_readpage(page_size, addr, oob, 2); if ((oob[0] == 0xFF) && (oob[1] == 0xFF)) return 0; else return 1; } /* * address format * 17 16 9 8 0 * -------------------------------------------- * | block(12bit) | page(5bit) | offset(9bit) | * -------------------------------------------- */ static int nandll_read_page (uchar *buf, ulong addr) { int i; int page_size = 2048; nand_readpage(0, addr, buf, page_size); return 0; } /* * Read data from NAND. */ static int nandll_read_blocks (ulong dst_addr, ulong size) { uchar *buf = (uchar *)dst_addr; int i; int skipped_page = 0; uint page_shift = 11; /* Read pages */ for (i = 0; i < (size>>page_shift); i++, buf+=(1< 0x80) large_block = 1; else return -1; // Do not support small page (512B) any more /* read NAND blocks */ return nandll_read_blocks(CFG_PHY_UBOOT_BASE, COPY_BL2_SIZE); }
回过头来分析after_copy,在这之前的代码应该都是位置无关码,甚至bl copy_fram_nand都是位置无关的因此都可以正确执行。
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 /*MMU表地址高位清零*/ orr r1, r0, r1/*uboot物理加载地址的 与MMU地址低字节相或,得到0x33exxxxx,及mmu_table的地址*/ mcr p15, 0, r1, c2, c0, 0 /*将r1存入CP15*/
这里将MMU映射表所在基址转换成物理地址,存入TTB寄存器中。考虑到之前dnw下载的那段初始程序(以下简写为usb_dnw_init),这里我也有点疑惑,当执行usb_dnw_init后,dnw就能在0x23e00000处下载uboot了,到底usb_dnw_init有没有做MMU的工作呢。
当我写到这里,发现已经有前辈分析过210的uboot了。可以看看:这里
但是网友没有对MMU机制进行分析,我这里说明一下,
可参考《S3C6410之uboot回炉再造(3)》这篇文章。
#ifdef CONFIG_ENABLE_MMU #ifdef CONFIG_MCP_SINGLE /* * MMU Table for SMDKC110 * 0x0000_0000 -- 0xBFFF_FFFF => Not Allowed * 0xB000_0000 -- 0xB7FF_FFFF => A:0xB000_0000 -- 0xB7FF_FFFF VA = PA * 0xC000_0000 -- 0xC7FF_FFFF => A:0x3000_0000 -- 0x37FF_FFFF 128M * 0xC800_0000 -- 0xDFFF_FFFF => Not Allowed * 0xE000_0000 -- 0xFFFF_FFFF => A:0xE000_0000 -- 0XFFFF_FFFF VA = PA */ /* form a first-level section entry */ .macro FL_SECTION_ENTRY base,ap,d,c,b .word (\base << 20) | (\ap << 10) | \ (\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1) .endm .section .mmudata, "a" .align 14 // the following alignment creates the mmu table at address 0x4000. .globl mmu_table mmu_table: .set __base,0 // Access for iRAM .rept 0x100 //对应虚拟地址0 ~ 256M FL_SECTION_ENTRY __base,3,0,0,0 .set __base,__base+1 .endr // Not Allowed .rept 0x200 - 0x100 //对应虚拟地址256M ~ 512M .word 0x00000000 .endr .set __base,0x200 // should be accessed .rept 0x600 - 0x200//对应虚拟地址512M ~ 1.5G FL_SECTION_ENTRY __base,3,0,1,1 .set __base,__base+1 .endr .rept 0x800 - 0x600//对应虚拟地址1.5G ~ 2G .word 0x00000000 .endr .set __base,0x800 // should be accessed .rept 0xb00 - 0x800//对应虚拟地址2 ~ 2.75G FL_SECTION_ENTRY __base,3,0,0,0 .set __base,__base+1 .endr /* .rept 0xc00 - 0xb00 .word 0x00000000 .endr */ .set __base,0xB00 .rept 0xc00 - 0xb00//对应虚拟地址2.75G ~ 3G 256MB FL_SECTION_ENTRY __base,3,0,0,0 .set __base,__base+1 .endr .set __base,0x300 // 256MB for SDRAM with cacheable .rept 0xD00 - 0xC00//对应虚拟地址3G ~ 3.25G, 256MB FL_SECTION_ENTRY __base,3,0,1,1 .set __base,__base+1 .endr // access is not allowed. @.rept 0xD00 - 0xC80//对应虚拟地址3G ~ 3.125G,128MB @.word 0x00000000 @.endr .set __base,0xD00 // 1:1 mapping for debugging with non-cacheable .rept 0x1000 - 0xD00//对应虚拟地址3.125G ~ 4G, 768MB FL_SECTION_ENTRY __base,3,0,0,0 .set __base,__base+1 .endr #else // CONFIG_MCP_AC, CONFIG_MCP_H, CONFIG_MCP_B
经过MMU映射后,0xc000 0000~0xcfff ffff映射到了物理内存0x3000 0000 ~ 0x4FFF FFFF中,这样uboot就能通过dnw下载运行了。