参考:
 
作者:彭东林
邮箱: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

三、启动方式

  • 原理图
   
  上图是S5P6818的启动源,目前NanoPC T3上支持eMMC、sdcard的启动方式,如果启动失败的话,会从USB启动,对应的就是上面的 "5=SDMMC",然后通过SD3和VID1[3]来控制使用哪个port,一个port对应的就是一个SDMMC控制器,这款SoC一共有3个SDMMC控制器,eMMC接在SDMMC2上,sdcard接在SDMMC0上,wifi和bt接在SDMMC1上,使用的是SDIO接口。在原理图上:
 
        
  图中,CAM1_D3就是VID1[3]引脚,当按下图中的BOOT按键是,就是从sdcard启动,抬起就是从eMMC启动。
  • 从sdcard启动
       
  如果从sdcard启动的话,上电后首先执行的是Internal ROM中的程序(称之为iROMBOOT),硬件会自动把Internal ROM重新映射到物理地址0x0000_0000上,然后bootrom中的程序通过检查bootconfig的配置得知是从SDMMC0启动,然后将用户自己的bootcode(第二级bootloader)从sdcard当中读取出来放入Internal SRAM中相应的位置(0xFFFF_0000)执行,称之为SDHCBOOT。
 
        
  既然用户自己的Bootcode是被固化在芯片内部的bootrom程序加载的,所以用户自己的Bootcode在sdcard当中的存放就必须有一定的格式,否则bootrom不认,这个格式称之为Boot Header。从上面的图中,首先我们应该知道的是User Bootcode应该从sdcard的第1号扇区开始存放,对于sdcard来说,每个扇区的大小是512byte,其中第0号扇区保留出来给分区表使用,当然对于SDHCBOOT这种启动方式,不care在sdcard的第0号扇区里是否有分区表,因为bootrom会直接定位到第1号扇区开始读取的,读取最大56KB的大小(实际大小应该是Boot Header中的LOADSIZE,需要后续验证)到Internal SRAM中。存放位置清楚了,下面就是具体的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的哪个地址开始存放,而运行地址的意思是,将第二级bootloader全部读到Internal SRAM后,最后跳转执行第二级bootloader时需要将PC指针设置为哪个地址开始执行),这里是0xFFFF_0000。如果是从SPI启动的话,bootrom还会检查CRC32(文档上说这部分校验码不包含Boot Header,意思是将前512B填充成0,然后计算CRC32,计算结果填充到对应的位置,前面填充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标签的内容:

我们重点关注上图中红框里的内容:

 
图中第一列是机器码,第二列表示的是偏移地址,最后是对应的反汇编代码,这段反汇编实现了从Aarch32切到Aarch64。根据上面的内容我手动填写了一个可用的nsih64.bin文件,内容如下:
 
 
然后使用下面的命令对其进行反汇编:
 
arm-none-linux-gnueabi-objdump -D -b binary -m arm nsih64.bin > nsih64.S
 
nsih64.bin:     file format binary

Disassembly of section .data:

 <.data>:
: e3a00103 mov r0, #- ; 0xc0000000
: e3800a11 orr r0, r0, # ; 0x11000
: e590113c ldr r1, [r0, #] ; 0x13c
c: e3811a0f orr r1, r1, # ; 0xf000
: e580013c str r0, [r0, #] ; 0x13c
: e3a025ff mov r2, # ; 0x3fc00000
: e38229ff orr r2, r2, # ; 0x3fc000
1c: e3822080 orr r2, r2, # ; 0x80
: e5802140 str r2, [r0, #] ; 0x140
: e3a08103 mov r8, #- ; 0xc0000000
: e3888801 orr r8, r8, # ; 0x10000
2c: e59892ac ldr r9, [r8, #] ; 0x2ac
: e3899001 orr r9, r9, #
: e58892ac str r9, [r8, #] ; 0x2ac
: e320f003 wfi
3c: eafffffe b 0x3c
...
: 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) = 0x3FFFC080;
REG32(0xC00102AC) |= 0x1;
wfi();
while();
}

结合6818的寄存器手册分析一下:

第4行,将0xC001113C的[15:12]写成0xF, 表示将cluster0的四个core都设置为Aarch64,此时并没有生效。这个寄存器的默认值是0,对应的是Aarch32,所以对于S5P6818来说,上电后,cpu默认处于Aarch32模式
 
 
第5行,设置复位向量基地址,也就是执行warm reset后,cluster0的core0会从这里设置的地址上开始运行
 
 
这里需要注意:上面写入的是0x3FFFC080,结合寄存器,这里设置的其实是地址的[33:2],所以最终的地址其实是(0x3FFFC080<<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上:
 
下面重点关注如下几个文件:
  • 链接脚本spl.lds
链接地址设置的是0xFFFF0000。
  • start.S
 
上面第23行,表示跳过前512字节,也就是将最终可执行程序的前512字节填充为0,将来这部分会用nsih64.bin填充,并更新LOADSIZE和CRC32字段(前面填充0不会影响CRC32的校验值)。这样的话,第27行的b reset指令正好就位于0xFFFF0200.
  • boot.c
void boot_master(void)
{
int i, d = ; clrsetbits32(0xc001b020, << , << );
setbits32(0xc001b004, << ); clrsetbits32(0xc001b020, << , << );
setbits32(0xc001b004, << );
tglbits32(0xc001b000, << ); while () {
for (i = ; i < ; ++i)
d ^= i;
tglbits32(0xc001b000, << );
tglbits32(0xc001b000, << );
}
}

从github上下载后,进入工程目录执行make,就会在out目录下生成如下几个文件:

可以阅读Makefile看看这几个镜像都是怎么来的。 这里大概说明如下:NanoPC-T3.elf文件表示最后编译生成的elf格式的可执行文件,NanoPC-T3.map文件是NanoPC-T3.elf的地址空间map表,对于分析链接脚本以及每个成员的空间占用情况很有帮助,NanoPC-T3_nonsih.img是将NanoPC-T3.elf文件用objcopy处理得到的bin文件,NanoPC-T3.img是用build工具将nsih64.bin跟NanoPC-T3_nonsih.img组装起来的,同时会更新LOADSIZE和CRC32字段(可以用beyondcompare比较一下):

  其中,NanoPi_M3.img就是我们需要烧写到sdcard中的,烧写命令如下:

dd if=./out/NanoPC-T3.img of=/dev/sdh bs= seek= conv=fdatasync

注意: 上面的/dev/sdh对应的就是sdcard的节点,sdh后面不太任何数字,表示的是整块sdcard,从0扇区开始。“bs=512 seek=1”表示跳过第一个512字节,也就是跳过第0个扇区,从第1个扇区开始烧写。

  烧写完毕后,在板子上电或者reset时按住BOOT按键,此时就会从sdcard启动,这个裸机程序运行的效果是,板子上的两个LED灯交替闪烁,下面是原理图:

 
 
完。
 
 
 
 
 

NanoPC-T3 64位裸机编程 —— 启动和运行状态切换的更多相关文章

  1. 64位电脑上启动程序出现丢失MSVCR110.dll的解决办法

    启动程序报错如下: 无法启动此程序,因为计算机中丢失MSVCR110.dll.尝试重新安装该程序以解决此问题. 应该很容易就搜索到,缺少这样的dll文件,是没有安装Visual C++ Redistr ...

  2. 新建虚拟机_WIN8 64位系统_启动报错Directory "EZBOOT" not found

    准备工作:下载win8 64 镜像文件 1.虚拟机安装win8 64位操作系统,新建虚拟机步骤同XP系统 2.BIOS设置CD/ROM启动,但启动报错,如下,由于镜像文件超过4G,无法从虚拟机安装,需 ...

  3. 用VC进行64位编程

    用VC进行64位编程 分类: C/C++2014-04-30 15:14 532人阅读 评论(0) 收藏 举报 本文转自:http://www.usidcbbs.com/read-htm-tid-52 ...

  4. 【转】win32,win64编程永恒;语言编程需要注意的64位和32机器的区别

    原文网址:http://www.cnblogs.com/kex1n/archive/2010/10/06/1844737.html 一.数据类型特别是int相关的类型在不同位数机器的平台下长度不同.C ...

  5. C# winIO32位,64位的使用(运行时要用管理员身份)

    下载地址: http://www.internals.com/utilities/WinIo.zip 一个按键的消息产生流程如下: 1)硬件中断/硬件端口数据 WinIO能模拟,或者修改IDT是在这一 ...

  6. 64位win系统上面tomcat6启动不了 window不能再本地计算机启动

    64位的jdk装完之后,jre的bin目录下面没有client文件夹, 而tomcat6.0.20的默认配置启动在client文件夹下面. 所以打开tomcat6w,在java选项界面,取消Use d ...

  7. 安装64位版Oracle11gR2后无法启动SQLDeveloper的解决方案

    安装64位版Oracle11gR2后发现启动SQL Developer时弹出配置java.exe的路径,找到Oracle自带java.exe后产生的路径“C:\app\用户名\product\11.2 ...

  8. 安装64位版Oracle11gR2后无法启动SQLDeveloper的解决方案(原创) (2016-10-29 下午01:56)

    安装64位版Oracle11gR2后发现启动SQL Developer时弹出配置java.exe的路径,找到Oracle自带java.exe后产生的路径"C:\app\用户名\product ...

  9. dll文件32位64位检测工具以及Windows文件夹SysWow64的坑(很详细,还有自动动手编程探测dll)

    阅读目录 dll文件不匹配导致数据库无法启动 究竟是System32还是SysWow64 区分dll文件32位64位的程序让我倍感迷惑 再次判断究竟是System32还是SysWow64——意想不到的 ...

随机推荐

  1. layer父页面调用子页面的方法

    由于不知道如何在子页面获取到layer定义的确定按钮,于是就在子页面上定义了一个方法,然后在由父页面在点确定按钮时调用子页面所定义的这个方法,从而执行子页面方法里面的内容: 子页面代码: functi ...

  2. Proguard returned with error code 1. See console

    满世界的bug. 微信支付,Windows的远程桌面. Android的 , Proguard returned with error code 1. See console解决办法" 真的 ...

  3. luogu3375 【模板】KMP字符串匹配

    nxt数组下标从1开始而a,b数组下标从0开始. #include <iostream> #include <cstring> #include <cstdio> ...

  4. Careercup - Microsoft面试题 - 5917873302142976

    2014-05-12 06:56 题目链接 原题: A link list contains following elements struct node{ int data; node* next; ...

  5. Python学习之正则表达式初探

    正则表达式 正则表达式 (或 regexes ) 是通用的文本模式匹配的方法. Django URLconfs 允许你 使用任意的正则表达式来做强有力的URL映射,不过通常你实际上可能只需要使用很少的 ...

  6. 【Luogu P1637】 三元上升子序列

    对于每个数$a_i$,易得它对答案的贡献为 它左边比它小的数的个数$\times$它右边比它大的数的个数. 可以离散化后再处理也可以使用动态开点的线段树. 我使用了动态开点的线段树,只有需要用到这个节 ...

  7. bugku 普通的二维码

    记录下对进制转换实现的重新思考. 扫描二维码扫到了一句垃圾话. 拖到winhex里面. 一开始以为是十进制直接转ascii,发现错误. 后来发现,最大数是7,八进制转换吧. 我是打算用python的l ...

  8. nginx的进程模型

    nginx采用的也是大部分http服务器的做法,就是master,worker模型,一个master进程管理站个或者多个worker进程,基本的事件处理都是放在woker中,master负责一些全局初 ...

  9. Lua 语法要点

    table 默认键值都是从1开始 table array = { "A", "B" } array2 = array array[] = "D&quo ...

  10. FreeBSD 用kgdb调试kernel dump文件

    FreeBSD 用kgdb调试kernel dump文件 来自: http://blog.csdn.net/ztz0223/article/details/8600052 kgdb貌似和ddb一样属于 ...