这段时间因为疫情在家,失业了也没有什么其他的事情,想着好好学习一下u-boot相关内容。
正好手头上有之前买了然后吃灰的OK6410,根据网上的相关资料着手移植到u-boot 2018版本,折腾了快半个月的时间还是没有成功,每次bl _main
后就会挂掉,不知道具体原因很是困惑,也找了一些调试方法,比如通过Jlink直接调试,通过Jlink-gdb-server,最后没怎么搞明白如何调试,但是发现运行后进入了data_abort
异常位置。分析应该是relocate出现问题,也找了很久无果,然后想着好好分析一些飞凌提供的旧版本的u-boot。
开发板硬件资源 1 2 3 4 5 6 7 8 飞凌OK6410开发板 DDR: 256MB 型号:K4X1G163PC (64Mx16bit=128MB) * 2 NAND: 1GB 型号:MT29F16G08ABACAWP, SLC, PageSize=4K SD: SD/MMC Host Controller 使用channel 0 LCD: WXCAT43-TG3#001, 4.3寸 屏 Ethernet: DM9000A
如何编译 根据根目录下的Makefile文件
编译方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 # 使用的工具链是飞凌提供的,gcc version 4.3.2 # 指定交叉工具链 export CROSS_COMPILE=arm-linux- # 配置u-boot #从sd卡启动 make forlinx_sd_ram256_config #从nand启动 make forlinx_nand_ram256_config # 编译 make -j8
mkconfig
shell脚本主要的配置:
从SD卡启动 u-boot怎么放入SD卡 根据S3C6410_Internal_ROM_Booting.pdf
的启动流程说明:
s3c6410启动时,iROM中的BL0 会加载sd卡中的u-boot前8K到iRAM
怎么将u-boot分成两部分(BL1,BL2) sd卡的分区方式(u-boot,kernel,rootfs等如何存放): 本人使用的时SHDC sd卡,所以分区格式为
有点疑惑的是这个Signature
分区,作用是什么??
所以想看看sd卡的u-boot如何烧写Nand Flash中的 u-boot
如何烧写:
BL1流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 reset #cpu/s3c64xx/start.S cpu_init_crit bl lowlevel_init #board/samsung/smdk6410/lowlevel_init.S #判断运行的代码是否已经relocate; start.S #BL1运行在iRAM,所以没有relocate,执行movi_bl2_copy #搬迁BL2到SDRAM bl movi_bl2_copy #cpu/s3c64xx/movi.c b after_copy #cpu/s3c64xx/start.S skip_hw_init stack_setup clear_bss #跳转到BL2 ldr pc, _start_armboot #cpu/s3c64xx/start.S
BL1如何搬迁BL2到SDRAM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #SD卡中的代码 如何搬运 确定了 第二部分在哪里 #如何搬运的代码 #ifdef CONFIG_BOOT_MOVINAND // u-boot-1.1.6/cpu/s3c64xx/start.S ldr sp, _TEXT_PHY_BASE bl movi_bl2_copy b after_copy #endif #搬运代码核心 CopyMovitoMem(HSMMC_CHANNEL, MOVI_BL2_POS, MOVI_BL2_BLKCNT, (uint *)BL2_BASE, MOVI_INIT_REQUIRED); #define CopyMovitoMem(a,b,c,d,e) (((int(*)(int, uint, ushort, uint *, int))(*((uint *)(TCM_BASE + 0x8))))(a,b,c,d,e)) // 这个代码是 定义在 iTCM 中的 HSMMC_CHANNEL 0 用的哪一个channel : 0 MOVI_BL2_POS #define MOVI_BL2_POS (MOVI_LAST_BLKPOS - MOVI_BL1_BLKCNT - MOVI_BL2_BLKCNT - MOVI_ENV_BLKCNT) 从哪个位置搬移 : 全部的块大小(由iROM中的代码算出来,位于signature处) - BL大小(16个sector,由手册决定) - BL2大小(512个,由u-boot决定,所以第二部分最大为256KB) - 环境所占大小(32个,由u-boot决定) MOVI_BL2_BLKCNT #define MOVI_BL2_BLKCNT (PART_SIZE_BL / MOVI_BLKSIZE) 搬移多少个块 : 512个块,256KB BL2_BASE #define BL2_BASE (CFG_PHY_UBOOT_BASE) 搬到哪里: 0x5FE00000,位于sdram MOVI_INIT_REQUIRED 0 是否重新初始化:否
By: _pop , OK6410A 开发板 (三) u-boot-1.1.6 boot 解析
BL0中厂商固化的拷贝函数(S3C6410_Internal_ROM_Booting.pdf
):
BL2流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #BL2 是 从_start_armboot开始的: ldr pc, _start_armboot #cpu/s3c64xx/start.S _start_armboot: .word start_armboot #cpu/s3c64xx/start.S start_armboot() #lib_arm/board.c #gd_t 结构初始化 #初始化: 函数首地址存放在 init_sequence[] 指针数组 cpu_init() #cpu/s3c64xx/cpu.c board_init() #board/samsung/smdk6410.c interrupt_init() #cpu/s3c64xx/interrupts.c env_init() #common/env_movi.c (函数所在文件和启动方式相关) init_baudrate() #lib_arm/board.c serial_init() #cpu/s3c64xx/serial.c console_init_f() #common/console.c display_banner() #lib_arm/board.c print_cpuinfo() #cpu/s3c64xx/speed.c checkboard() #board/samsung/smdk6410.c dram_init() #board/samsung/smdk6410.c display_dram_config()#lib_arm/board.c flash_init() #board/samsung/flash.c lcd_setmem() #common/lcd.c mem_malloc_init() #lib_arm/board.c nand_init() #board/samsung/smdk6410.c #sd/mmc控制器初始化 (和启动方式相关) movi_set_capacity() movi_set_ofs() movi_init() #cpu/s3c64xx/movi.c env_relocate() #common/env_common.c #设备相关驱动初始化 devices_init() #common/devices.c jumptable_init() #common/exports.c console_init_r() #common/console.c enable_interrupts() #cpu/s3c64xx/interrupts.c board_late_init() #board/samsung/smdk6410.c eth_initialize() #net/eth.c #main循环 main_loop() #common/main.c #还没有超时或被中断 if (bootdelay >= 0 && s && !abortboot (bootdelay)) { #解析bootcmd命令 parse_string_outer() #common/hush.c run_list #common/hush.c run_list_real #common/hush.c run_pipe_real #common/hush.c #查找&执行命令 cmdtp = find_cmd(child->argv[i]); #common/command.c #cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start; #命令列表首地址为__u_boot_cmd_start ,链接器脚本board/samsung/smdk6410/u-boot.lds中定义 __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; if (cmdtp == NULL) { #没有找到命令 } else { #执行命令 rcode = (cmdtp->cmd)(cmdtp, flag,child->argc-i,&child->argv[i]); } } #被中断 ARMMenu() #common/main.c #相关菜单选择……
**.u_boot_cmd
**定义的位置为:include/command.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd" ))) #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \ cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help} U_BOOT_CMD(nand, 5 , 1 , do_nand, "nand - NAND sub-system\n" , "info - show available NAND devices\n" "nand device [dev] - show or set current device\n" "nand read[.jffs2] - addr off|partition size\n" "nand write[.jffs2] - addr off|partiton size - read/write `size' bytes starting\n" " at offset `off' to/from memory address `addr'\n" #ifdef CFG_NAND_YAFFS_WRITE "nand write[.yaffs[1]] - addr off|partition size - write `size' byte yaffs image\n" " starting at offset `off' from memory address `addr' (.yaffs1 for 512+16 NAND)\n" #endif "nand write[.uboot] - addr off|partition size\n" "nand write[.ok] - sound beep ok\n" "nand erase [clean] [off size] - erase `size' bytes from\n" " offset `off' (entire device if not specified)\n" "nand bad - show bad blocks\n" "nand dump[.oob] off - dump page\n" "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" "nand markbad off - mark bad block at offset (UNSAFE)\n" "nand biterr off - make a bit error at offset (UNSAFE)\n" "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section\n" );
对于飞凌提供的从sd卡启动的固件,上电默认自动烧写所有固件,那么bootcmd
是什么呢?
从common/env_movi.c
中定义的env_init()
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int env_init (void ) { gd->env_addr = (ulong)&default_environment[0 ]; gd->env_valid = 1 ; return (0 ); } uchar default_environment[] = { #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif #ifdef CONFIG_BOOTCOMMAND "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif } #define CONFIG_BOOTCOMMAND "nand led-start;nand erase;fatload mmc 0:1 0x50008000 u-boot.bin;nand write.uboot 0x50008000 0 0x200000;fatload mmc 0:1 0x50008000 zImage;nand write.e 0x50008000 0x500000 0x500000; fatload mmc 0:1 0x50008000 rootfs.yaffs2; nand write.yaffs2 0x50008000 0x01e00000 $filesize; nand beep; nand led-end"
启动信息:
BL2如何将sd卡中u-boot烧写到nand 从bootcmd
启动命令,知道执行的是nand write.uboot 0x50008000 0 0x200000
命令。
从前面.u_boot_cmd
的分析,知道nand write.uboot
是在common/cmd_nand.c中定义, 命令函数是do_nand()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 typedef struct mtd_info nand_info_t ; nand_info_t *nand;nand = &nand_info[nand_curr_device]; if (strncmp (cmd, "read" , 4 ) == 0 || strncmp (cmd, "write" , 5 ) == 0 ) nand_write_opts(nand, &opts); else if (!read && s != NULL && (!strcmp (s, ".uboot" )) && nand->writesize == 4096 ) { size=4096 ; nand_write(nand, off, &size, (u_char *)addr); off+=4096 ; addr+=2048 ; nand_write(nand, off, &size, (u_char *)addr); off+=4096 ; addr+=2048 ; nand_write(nand, off, &size, (u_char *)addr); off+=4096 ; addr+=2048 ; nand_write(nand, off, &size, (u_char *)addr); off+=4096 ; addr+=2048 ; size=1024 *1024 -4 *4096 ; ret = nand_write(nand, off, &size, (u_char *)addr); } static inline int nand_write (nand_info_t *info, ulong ofs, ulong *len, u_char *buf) { return info->write(info, ofs, *len, (size_t *)len, buf); } static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd->priv; int ret; if ((to + len) > mtd->size) return -EINVAL; if (!len) return 0 ; nand_get_device(chip, mtd, FL_WRITING); chip->ops.len = len; chip->ops.datbuf = (uint8_t *)buf; chip->ops.oobbuf = NULL ; ret = nand_do_write_ops(mtd, to, &chip->ops); *retlen = chip->ops.retlen; nand_release_device(mtd); return ret; } int nand_curr_device = -1 ;nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; static struct nand_chip nand_chip [CFG_MAX_NAND_DEVICE ];static ulong base_address[CFG_MAX_NAND_DEVICE] = CFG_NAND_BASE_LIST; nand_init() nand_init_chip() board_nand_init() if (nand_scan(mtd, 1 ) == 0 ) { if (!mtd->name) mtd->name = (char *)default_nand_name; }
所以可以看出手上的这份文档S3C6410_Internal_ROM_Booting.pdf
中关于nand的分区是有问题的 ,
有可能后续修改了而我们没有最新文档。
按照代码,及上面所述BL0的拷贝方式,这里BL1应该占4Page , 关于Signature
这个段,有些不理解,看了SPV210的 手册S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf
相关的描述,是在secure boot 的时候才需要的,按照个人理解这里也是一样,所以不需要。
从Nand Flash启动 BL1流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #和从sd卡启动的流程大体相同,只是搬迁BL2不一样 reset #cpu/s3c64xx/start.S cpu_init_crit bl lowlevel_init #board/samsung/smdk6410/lowlevel_init.S #判断运行的代码是否已经relocate; start.S #BL1运行在iRAM,所以没有relocate,执行copy_from_nand #搬迁BL2到SDRAM bl copy_from_nand #cpu/s3c64xx/start.S copy_uboot_to_ram #cpu/s3c64xx/nand_cp.c skip_hw_init stack_setup clear_bss #跳转到BL2 ldr pc, _start_armboot #cpu/s3c64xx/start.S
BL1如何搬迁BL2到SDRAM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #cpu/s3c64xx/start.S mov r0, #0x1000 bl 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: 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 copy_uboot_to_ram() large_block = 2 ; nandll_read_blocks(CFG_PHY_UBOOT_BASE, 0x3c000 , large_block); if (large_block == 2 ) { for (i = 0 ; i < 4 ; i++, buf+=(1 <<(page_shift-1 ))) { nandll_read_page(buf, i, large_block); } for (i = 4 ; i < (0x3c000 >>page_shift); i++, buf+=(1 <<page_shift)) { nandll_read_page(buf, i, large_block); } }
看代码的时候一直很困惑,不知道为啥第一次读4page的数据,偏移量是page_shift-1
, 这样每次会覆盖2K数据。
然后在网上看到了资料:
从nandflash启动时,BL0是将nandflash的块0的前4页中,每页头2K组合成8K,拷贝到steppingstone中的 。
BL0是厂家固化好的代码,我们改不了 。如果我们要自己一些启动代码就需要这点。无论是写入,还是读取这8K的启动代码,也要遵循这样的约定 。
所以也解决了前面[BL2如何将sd卡中u-boot烧写到nand](# BL2如何将sd卡中u-boot烧写到nand) “sd卡中的u-boot程序,烧录u-boot到nand时,addr的偏移为啥是2048”的困惑。
这里要感谢drsonxu 的这篇博客:关于OK6410的NandFlash启动的一些事实 .
BL2流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #和从sd卡启动大致相同,只有 #BL2 是 从_start_armboot开始的: ldr pc, _start_armboot #cpu/s3c64xx/start.S _start_armboot: .word start_armboot #cpu/s3c64xx/start.S start_armboot() #lib_arm/board.c #初始化: 函数首地址存放在 init_sequence[] 指针数组 #....参考从sd卡启动 #nand控制器初始化 nand_init(); #....参考从sd卡启动
TODO:待解决问题 nand_write()
实际调用的是mtd->write()
, 那么nand驱动是如何和mtd驱动关联的,暂时还没看懂???
参考
OK6410A 开发板 (三) u-boot-1.1.6 boot 解析
关于OK6410的NandFlash启动的一些事实
s3c6410初始化2G nand flash的一些注意点