NanoPC-T3 64位裸机编程 —— 启动和运行状态切换
2019-05-27 23:43
2301 查看
参考:
https://github.com/metro94/s5p6818_spl
https://github.com/trebisky/Fire3/tree/master/Boot_NSIH
https://github.com/SamsungARTIK/bl1-artik710
https://github.com/SamsungARTIK/bl1-artik710/blob/artik/nsih-generator/PERIDOT_SYSINFO_Gen_ver03.xls
作者:彭东林
邮箱:pengdonglin137@163.com
Internal ROM: 0x3400_0000 ~ 0x3400_4FFF, 一共20KB Internal SRAM: 0xFFFF_0000 ~ 0xFFFF_FFFF,一共64KB
既然用户自己的Bootcode是被固化在芯片内部的bootrom程序加载的,所以用户自己的Bootcode在sdcard当中的存放就必须有一定的格式,否则bootrom不认,这个格式称之为Boot Header。从上面的图中,首先我们应该知道的是User Bootcode应该从sdcard的第1号扇区开始存放,对于sdcard来说,每个扇区的大小是512byte,其中第0号扇区保留出来给分区表使用,当然对于SDHCBOOT这种启动方式,不care在sdcard的第0号扇区里是否有分区表,因为bootrom是直接定位到第1号扇区开始读取的,读取56KB的大小,也就是112个扇区。存放位置清楚了,下面就是具体的Boot Header的数据结构,具体请参考S5P6818的芯片手册的3.4.9 Additional Information。 下面是我的理解:上面是关于Boot Header的说明:如果不是从uart启动的话,那么bootrom会检查第二级bootloader(也就是user boot code)的前512字节的Boot Header,bootrom会将第二级bootloader的前512字节的Boot Header存放到0xFFFF_0000地址上,这个是Internal SRAM的起始地址,然后检查signature是否为"NSIH",如果不是的话,就尝试下一个启动源。在Boot Header中LOADSIZE、LOADADDR以及LAUNCHADDR必须有效(16字节对齐),LOADSIZE表示第二级bootloader的大小(给bootrom看的),后两个分别表示第二级bootloader的加载地址和运行地址(加载地址表示bootrom把第二级bootloader从sdcard读取出来后,存放到Internal SRAM的哪个地址上,而运行地址的意思是,读到Internal SRAM之后,最后执行跳转操作时需要将PC指针设置为哪个地址),也就是0xFFFF_0000。如果是从SPI启动的话,bootrom还会检查CRC32(文档上说这部分校验码不包含Boot Header,意思是将前512B填充成0,然后计算CRC32,计算结果填充到对应的位置)。最后PC指针就会跳转到LAUNCHADDR表示的地址处开始执行, 也就是0xFFFF_0000,下面是从sdcard启动时的Boot Header的格式: 上面是Boot Header的基本格式,其中vector可以用于存放异常向量表(当然也可以不这么干),文档中给的例子看,异常向量表是按Aarch32组织的,说明S5P6818这款SoC的上电后bootrom运行在Aarch32状态。Device Addr表示第二级bootloader从sdcard的哪个地址(以字节为单位)上去读取第三级bootloder。从0x44~0x4C分别表示第二级bootloader的大小,加载地址和运行地址(这两个地址固定为0xFFFF_0000),这三个是给bootrom看的。Port Num表示第二级bootloader通过哪个sdhc port将第三级bootloader读取进来,CRC32是user bootcode的校验码(文档上说这部分校验码不包含Boot Header,意思是将前512B填充成0,然后计算CRC32,计算结果填充到对应的位置)。Stub区域也是留给第二级bootloader自己使用的,下面的excel表格只是一种用法,其中存放了一些时钟配置和ddr时序配置参数,在第二级bootloader里会解析这部分,这样的好处是,不需要修改代码,如果换了硬件,只需要修改一下Boot Header就行了。最后的signature非常重要。可以参考https://github.com/SamsungARTIK/bl1-artik710,这份代码实现了一个第二级bootloader,对理解上面的启动过程很具有参考意义。
第5行,设置复位向量基地址,也就是执行warm reset后,cluster0的core0会从这里设置的地址上开始运行 这里需要注意:上面写入的是0x3FFFC080,结合寄存器,这里设置的其实是地址的[33:2],所以最终的地址其实是(0x3FFC080<<2) = 0xFFFF0200。 第6行,0xC00102AC寄存器在手册里描述的是Reserved,这个寄存器的作用应该是设置warm reset标志,此时并没有执行reset操作 第7行,执行wfi操作,当执行完这条指令后,发现前面设置了warm reset标志,此时才会执行真正的warm reset操作。执行warm reset后,cluster的core0就会从0xFFFF0200地址上开始运行,并且此时的运行状态是Aarch64,这样就完成了对处理器运行状态的切换。 这里为什么不采用eret的方式进行处理器运行状态切换呢? 因为目前运行在Aarch32,而eret是Aarch64指令,所以只能通过warm reset的方式。 关于处理器执行状态的切换这部分,可以参考ARMv8参考手册D1.20: 关于warm reset可以参考ARMv8参考手册D1.9: 至此,我们已经知道了,在nsih64.bin的开始阶段完成了对处理器运行状态的切换,而且切换后会从0xFFFF0200开始运行。所以我们需要将裸机程序的入口放到这个地址上。 这里用到的裸机程序已经上传到了github上: https://github.com/pengdonglin137/s5p6818_bare_metal 下面重点关注如下几个文件:
邮箱:pengdonglin137@163.com
一、系统框图
可以看到S5P6818一共有两个cluster,每个cluster各有4个Cortex-A53架构的core。从官方手册中说,每个core都工作在不小于1.4GHz的频率上,每个core有一个属于自己的L1 Cache,其中I-Cache和D-Cache各32KB,每个cluster内部的4个core共享一个大小为512KB的L2 Cache,外部的CCI-400用于Cache一致性。此外,SoC内部还有一个64KB的Internal SRAM和一个20KB的Internal ROM,其中Internal ROM用于存放bootrom代码,Internal SRAM一部分给bootrom存放.data/.bss/stack,另一部分给留给二级bootloader运行,二级bootloader用于初始化DDR以及从Flash读取其他镜像到DDR中,比如uboot以及ATF镜像等。二、Memory Map
目前主要知道如下几个地址范围:Internal ROM: 0x3400_0000 ~ 0x3400_4FFF, 一共20KB Internal SRAM: 0xFFFF_0000 ~ 0xFFFF_FFFF,一共64KB
三、启动方式
- 原理图
- 从sdcard启动
既然用户自己的Bootcode是被固化在芯片内部的bootrom程序加载的,所以用户自己的Bootcode在sdcard当中的存放就必须有一定的格式,否则bootrom不认,这个格式称之为Boot Header。从上面的图中,首先我们应该知道的是User Bootcode应该从sdcard的第1号扇区开始存放,对于sdcard来说,每个扇区的大小是512byte,其中第0号扇区保留出来给分区表使用,当然对于SDHCBOOT这种启动方式,不care在sdcard的第0号扇区里是否有分区表,因为bootrom是直接定位到第1号扇区开始读取的,读取56KB的大小,也就是112个扇区。存放位置清楚了,下面就是具体的Boot Header的数据结构,具体请参考S5P6818的芯片手册的3.4.9 Additional Information。 下面是我的理解:上面是关于Boot Header的说明:如果不是从uart启动的话,那么bootrom会检查第二级bootloader(也就是user boot code)的前512字节的Boot Header,bootrom会将第二级bootloader的前512字节的Boot Header存放到0xFFFF_0000地址上,这个是Internal SRAM的起始地址,然后检查signature是否为"NSIH",如果不是的话,就尝试下一个启动源。在Boot Header中LOADSIZE、LOADADDR以及LAUNCHADDR必须有效(16字节对齐),LOADSIZE表示第二级bootloader的大小(给bootrom看的),后两个分别表示第二级bootloader的加载地址和运行地址(加载地址表示bootrom把第二级bootloader从sdcard读取出来后,存放到Internal SRAM的哪个地址上,而运行地址的意思是,读到Internal SRAM之后,最后执行跳转操作时需要将PC指针设置为哪个地址),也就是0xFFFF_0000。如果是从SPI启动的话,bootrom还会检查CRC32(文档上说这部分校验码不包含Boot Header,意思是将前512B填充成0,然后计算CRC32,计算结果填充到对应的位置)。最后PC指针就会跳转到LAUNCHADDR表示的地址处开始执行, 也就是0xFFFF_0000,下面是从sdcard启动时的Boot Header的格式: 上面是Boot Header的基本格式,其中vector可以用于存放异常向量表(当然也可以不这么干),文档中给的例子看,异常向量表是按Aarch32组织的,说明S5P6818这款SoC的上电后bootrom运行在Aarch32状态。Device Addr表示第二级bootloader从sdcard的哪个地址(以字节为单位)上去读取第三级bootloder。从0x44~0x4C分别表示第二级bootloader的大小,加载地址和运行地址(这两个地址固定为0xFFFF_0000),这三个是给bootrom看的。Port Num表示第二级bootloader通过哪个sdhc port将第三级bootloader读取进来,CRC32是user bootcode的校验码(文档上说这部分校验码不包含Boot Header,意思是将前512B填充成0,然后计算CRC32,计算结果填充到对应的位置)。Stub区域也是留给第二级bootloader自己使用的,下面的excel表格只是一种用法,其中存放了一些时钟配置和ddr时序配置参数,在第二级bootloader里会解析这部分,这样的好处是,不需要修改代码,如果换了硬件,只需要修改一下Boot Header就行了。最后的signature非常重要。可以参考https://github.com/SamsungARTIK/bl1-artik710,这份代码实现了一个第二级bootloader,对理解上面的启动过程很具有参考意义。
四、64位裸机程序
首先需要认识一下nsih.bin文件,也就是上面说的Boot Header,它占一个扇区(512B)大小。可以参考https://github.com/SamsungARTIK/bl1-artik710/blob/artik/nsih-generator/PERIDOT_SYSINFO_Gen_ver03.xls,这个文件用excel表格的方式表示了Boot Header,由于我们这里要折腾的是64位裸机程序,所以在nsih.bin里需要实现对处理器运行状态的切换操作,好在前面的excel表格里已经有这部分操作了,下图是这个excel表格的DDR3 NSIH64标签的内容:
我们重点关注上图中红框里的内容:
nsih64.bin: file format binary Disassembly of section .data: 00000000 <.data>: 0: e3a00103 mov r0, #-1073741824 ; 0xc0000000 4: e3800a11 orr r0, r0, #69632 ; 0x11000 8: e590113c ldr r1, [r0, #316] ; 0x13c c: e3811a0f orr r1, r1, #61440 ; 0xf000 10: e580013c str r0, [r0, #316] ; 0x13c 14: e3a025ff mov r2, #1069547520 ; 0x3fc00000 18: e38229ff orr r2, r2, #4177920 ; 0x3fc000 1c: e3822080 orr r2, r2, #128 ; 0x80 20: e5802140 str r2, [r0, #320] ; 0x140 24: e3a08103 mov r8, #-1073741824 ; 0xc0000000 28: e3888801 orr r8, r8, #65536 ; 0x10000 2c: e59892ac ldr r9, [r8, #684] ; 0x2ac 30: e3899001 orr r9, r9, #1 34: e58892ac str r9, [r8, #684] ; 0x2ac 38: e320f003 wfi 3c: eafffffe b 0x3c ... 48: ffff0000 ; <UNDEFINED> instruction: 0xffff0000 4c: ffff0000 ; <UNDEFINED> instruction: 0xffff0000 ... 1fc: 4849534e stmdami r9, {r1, r2, r3, r6, r8, r9, ip, lr}^
将上面的代码转成C语言就容易理解了:
{ #define REG32(addr) (*((volatile uint32 *)addr)) REG32(0xC001113c) |= 0xF000; REG32(0xC0011140) = 0x3FFC080; REG32(0xC00102AC) |= 0x1; wfi(); while(1); }
结合6818的寄存器手册分析一下:
第4行,将0xC001113C的[15:12]写成0xF, 表示将cluster0的四个core都设置为Aarch64,此时并没有生效。这个寄存器的默认值是0,对应的是Aarch32,所以对于S5P6818来说,上电后,cpu默认处于Aarch32模式第5行,设置复位向量基地址,也就是执行warm reset后,cluster0的core0会从这里设置的地址上开始运行 这里需要注意:上面写入的是0x3FFFC080,结合寄存器,这里设置的其实是地址的[33:2],所以最终的地址其实是(0x3FFC080<<2) = 0xFFFF0200。 第6行,0xC00102AC寄存器在手册里描述的是Reserved,这个寄存器的作用应该是设置warm reset标志,此时并没有执行reset操作 第7行,执行wfi操作,当执行完这条指令后,发现前面设置了warm reset标志,此时才会执行真正的warm reset操作。执行warm reset后,cluster的core0就会从0xFFFF0200地址上开始运行,并且此时的运行状态是Aarch64,这样就完成了对处理器运行状态的切换。 这里为什么不采用eret的方式进行处理器运行状态切换呢? 因为目前运行在Aarch32,而eret是Aarch64指令,所以只能通过warm reset的方式。 关于处理器执行状态的切换这部分,可以参考ARMv8参考手册D1.20: 关于warm reset可以参考ARMv8参考手册D1.9: 至此,我们已经知道了,在nsih64.bin的开始阶段完成了对处理器运行状态的切换,而且切换后会从0xFFFF0200开始运行。所以我们需要将裸机程序的入口放到这个地址上。 这里用到的裸机程序已经上传到了github上: https://github.com/pengdonglin137/s5p6818_bare_metal 下面重点关注如下几个文件:
- 链接脚本spl.lds
- start.S
- boot.c
void boot_master(void) { int i, d = 0; clrsetbits32(0xc001b020, 3 << 24, 2 << 24); setbits32(0xc001b004, 1 << 12); clrsetbits32(0xc001b020, 3 << 22, 2 << 22); setbits32(0xc001b004, 1 << 11); tglbits32(0xc001b000, 1 << 11); while (1) { for (i = 0; i < 200000; ++i) d ^= i; tglbits32(0xc001b000, 1 << 12); tglbits32(0xc001b000, 1 << 11); } }
这个裸机程序运行的效果是,板子上的两个LED灯交替闪烁,下面是原理图: 完。
相关文章推荐
- Android通过包名或类名启动APP或者一个Activity 以及 判断APP的运行状态
- Linux查看服务的运行状态,并且启动和停止服务的方法
- 重新想象 Windows 8 Store Apps (69) - 其它: 自定义启动屏幕, 程序的运行位置, 保持屏幕的点亮状态, MessageDialog, PopupMenu
- 除非Windows Activation Service (WAS)和万维网发布服务(W3SVC)均处于运行状态,否则无法启动网站。
- LINUX下如何查看tomcat运行状态,判断其是否启动
- Android 编程下帧动画在 Activity 启动时自动运行的几种方式
- 在Oracle各启动模式间切换及受限状态、只读状态详解
- 如何用SC获得Windows Service的启动类型,运行状态, 修改启动类型
- 【Redis实现运行状态下切换RDB备份至AOF备份】
- Hadoop,Hbase启动及用web查看hadoop运行状态
- 如何切换bash中程序的前台运行状态
- 转载:用oralce连接.net客户端出现问题:“数据连接不成功,请检查该数据库是否已启动尝试加载oracle客户端时引发BadImageFormatException.如果在安装32位Oracle客户端组件的情况下以64位模式运行,”的解
- 最简单的ARM裸机程序,帮你理解程序的运行启动(mini2440开发板)
- nanopc-T3开发板内核应用程序的编译及运行(使用adb方式上传)
- 切换IIS到64位模式运行
- 指定eclipse启动时使用的jdk版本(在64位操作系统中同时运行64位eclipse和32位eclipse)
- 关于android 线程切换运行和停止状态
- vc编程判断运行时os是32位还是64位
- centos6.9 上docker 的安装 及启动 和运行状态查看
- uboot启动后在内存中运行裸机程序hello