Setup the kernel tagged list
Setup the device tree
linux-4.9.37/Documentation/arm/Booting
hi3519av100
设备原有的启动内核的方式是第一种ATAGS
的方式,由于目前hi3519av100
和hi3559av100
的u-boot
和kernel
源码都是共用的,且设备树比较方便兼容不同的板子,所有打算将hi3519av100
用第二种设备树的方式来启动内核。
问:
可以参考 蜗窝科技 的相关博文:(非广告,没收钱)
涉及文件:
test.dts
test.dtb
test-re.dts
fdtdump
打印出来的test.dump
涉及命令:
test.dts
:
/dts-v1/;
/ {
node@0 {
a-string-property = "A sttring";
a-string-list-property = "first string", "second string";
a-byte-data-property = [01 23 45 67 89 AB];
child-node@0 {
first-child-property;
second-child-property = <1>;
a-reference-to-something = <&node1>;
};
};
node1: node@1 {
an-empty-property;
a-string-property = "A new sttring";
a-cell-property = <1 2 3 4>;
a-reference-to-something = <&node2>;
child-node@0 {
};
};
node2: node@2 {
};
};
编译成test.dtb
:
dtc -I dts -O dtb -o test.dtb test.dts
反汇编test.dtb
为test-re.dts
文件:
dtc -I dtb -O dts -o test-re.dts test.dtb
test-re.dts
文件内容:
/dts-v1/;
/ {
node@0 {
a-string-property = "A sttring";
a-string-list-property = "first string", "second string";
a-byte-data-property = [01 23 45 67 89 ab];
child-node@0 {
first-child-property;
second-child-property = <0x1>;
a-reference-to-something = <0x1>;
};
};
node@1 {
an-empty-property;
a-string-property = "A new sttring";
a-cell-property = <0x1 0x2 0x3 0x4>;
a-reference-to-something = <0x2>;
linux,phandle = <0x1>;
phandle = <0x1>;
child-node@0 {
};
};
node@2 {
linux,phandle = <0x2>;
phandle = <0x2>;
};
};
可以看到上面源dts
文件与反编译出来的dts
文件还是存在差异的:
同时发现当使用[]
括号对4
个binary data
数据进行属性赋值时,反编译出来是一个u32
类型的值,使用其他值的时候还是会反编译成[]
的binary data
。(看文章后面的图)
使用fdtdump
打印test.dtb
:
fdtdump -ds test.dtb > test.dump
可以参照上面的test.dump
与编译出来的test.dtb
文件进行对比:
fdt_header
头的结构体如下所示:
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC */
fdt32_t totalsize; /* total size of DT block */
fdt32_t off_dt_struct; /* offset to structure */
fdt32_t off_dt_strings; /* offset to strings */
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
fdt32_t version; /* format version */
fdt32_t last_comp_version; /* last compatible version */
/* version 2 fields below */
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
booting on */
/* version 3 fields below */
fdt32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
fdt32_t size_dt_struct; /* size of the structure block */
};
struct fdt_reserve_entry {
fdt64_t address;
fdt64_t size;
};
struct fdt_node_header {
fdt32_t tag;
char name[];
};
struct fdt_property {
fdt32_t tag;
fdt32_t len;
fdt32_t nameoff;
char data[];
};
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
#define FDT_TAGSIZE sizeof(fdt32_t)
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
#define FDT_END_NODE 0x2 /* End node */
#define FDT_PROP 0x3 /* Property: name off,
size, content */
#define FDT_NOP 0x4 /* nop */
#define FDT_END 0x9
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
#define FDT_V16_SIZE FDT_V3_SIZE
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
二进制文件中以FDT_BEGIN_NODE (0x1)
表示一个节点的开始,FDT_END_NODE (0x2)
表示一个节点的结束,节点以struct fdt_node_header
描述;以FDT_PROP (0x3)
表示一个节点下的属性,属性以结构体struct fdt_property
描述。
fdtdump
出来的内容:
/dts-v1/;
// magic: 0xd00dfeed
// totalsize: 0x26e (622)
// off_dt_struct: 0x38
// off_dt_strings: 0x1bc
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0xb2
// size_dt_struct: 0x184
与编译出来的dtb
进行对比:
off_dt_struct
表明设备树的结构在dtb
中的偏移位置,这里是从0x38
开始,大小是size_dt_struct
,即0x184
字节。off_dt_strings
是属性名字字符串表在dtb
中开始的位置,大小是size_dt_strings
,这里是0xb2
字节。
那么从0x38
开始,就是根节点/
开始的位置。
根节点/
:以FDT_BEGIN_NODE (0x1)
开始,后面接u32
的0x0
值。
从fdtdump
出来的文件可以看到:
// 0038: tag: 0x00000001 (FDT_BEGIN_NODE)
/ {
/node@0
节点:以FDT_BEGIN_NODE (0x1)
开始,后面接node@0
的ASCII
码,并补全至4
字节对齐。
// 0040: tag: 0x00000001 (FDT_BEGIN_NODE)
node@0 {
/node@0
的a-string-property
属性:以FDT_PROP (0x3)
开始,属性值长度为0x9
,属性名字偏移为0x0
,接下来是属性的值"A string"(长度为8)。(长度会是字符串长度+1,即加0x00
的字符串结束符。并且以4
字节向上对齐。)(属性的名字存在size_dt_strings
里面,根据off_dt_strings
的地址0x1bc
加上偏移0x0
,就可以得到属性名字"a-string-property"。后面就不再赘述。)
// 004c: tag: 0x00000003 (FDT_PROP)
// 01bc: string: a-string-property
// 0058: value
a-string-property = "A string";
/node@0
的a-string-list-property
属性:属性值长度为0x1B
,属性名字偏移为0x12
,接下来是属性的值"first string second string"(长度为0x1A
)。(dtc编译与反编译过程中是如何区分是普通字符串还是字符串列表的?)
// 0064: tag: 0x00000003 (FDT_PROP)
// 01ce: string: a-string-list-property
// 0070: value
a-string-list-property = "first string", "second string";
/node@0
的a-byte-data-property
属性:属性值长度为0x6
,属性名字偏移为0x29
,接下来是属性的值:[01 23 45 67 89 AB]
,然后4
字节向上对齐。
// 008c: tag: 0x00000003 (FDT_PROP)
// 01e5: string: a-byte-data-property
// 0098: value
a-byte-data-property = [01 23 45 67 ffffff89 ffffffab];
实际dts
文件:
a-byte-data-property = [01 23 45 67 89 AB];
反编译出来的dts
文件:
a-byte-data-property = [01 23 45 67 89 ab];
/node@0/child-node@0
下的first-child-property
属性:属性值长度为0x0
,属性名字偏移为0x3E
,即"first-child-property"。
// 00b4: tag: 0x00000003 (FDT_PROP)
// 01fa: string: first-child-property
// 00c0: value
first-child-property;
// 00c0: tag: 0x00000003 (FDT_PROP)
// 020f: string: second-child-property
// 00cc: value
second-child-property = <0x00000001>;
// 00d0: tag: 0x00000003 (FDT_PROP)
// 0225: string: a-reference-to-something
// 00dc: value
a-reference-to-something = <0x00000001>;
实际dts
文件:
a-reference-to-something = <&node1>;
反编译出来的dts
文件:
linux,phandle = <0x1>;
phandle = <0x1>;
这个属性的名字跟/node@0
里面的属性的名字相同,所有属性名字的偏移通用指向了0x41
:(达到了属性名的服用,节省部分空间。)
fdtdump
出来的有错误:
// 011c: tag: 0x00000003 (FDT_PROP)
// 0250: string: a-cell-property
// 0128: value
a-cell-property = <0x00000001 0x00000003 0x00000003 0x00000003>;
实际dts
文件:
a-cell-property = <1 2 3 4>;
反编译出来的dts
文件:
a-cell-property = <0x1 0x2 0x3 0x4>;
// 0138: tag: 0x00000003 (FDT_PROP)
// 0225: string: a-reference-to-something
// 0144: value
a-reference-to-something = <0x00000002>;
实际dts
文件:
a-reference-to-something = <&node2>;
反编译出来的dts
文件:
a-reference-to-something = <0x2>;
被引用的节点会生成linux,phandle
和phandle
,并赋予不同的值:
linux,phandle = <0x1>;
phandle = <0x1>;
linux,phandle = <0x2>;
phandle = <0x2>;
从上面的分析可以知道,整个dtb
文件的结构大概如下所示:
查看hi3519av100
的内核编译过程,可以看到dtb
文件实际通过cat
命令是打到了zImage
的后面形成zImage-dtb
:
虽然说dtb
放在zImage
的后面,zImage-dtb
镜像最后生成uImage
,但uImage
的头实际是记录的ih_size
是zImage-dtb
的大小,而不是zImage
的大小,所有要怎么从uImage
里面获得dtb
的内容呢?
typedef struct image_header {
__be32 ih_magic; /* Image Header Magic Number */
__be32 ih_hcrc; /* Image Header CRC Checksum */
__be32 ih_time; /* Image Creation Timestamp */
__be32 ih_size; /* Image Data Size */
__be32 ih_load; /* Data Load Address */
__be32 ih_ep; /* Entry Point Address */
__be32 ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
从uImage
的生成过程来看,其实中间那个zImage
是很重要的,查询相关资料发现,zImage
也有一个自己的头,在u-boot
下的arch/arm/lib/zimage.c
里:
#define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818
struct arm_z_header {
uint32_t code[9];
uint32_t zi_magic;
uint32_t zi_start;
uint32_t zi_end;
} __attribute__ ((__packed__));
查看一下生成过程的zImage
镜像:
既然知道了这个arm_z_header
,并且头里面记录了zi_start
和zi_end
,对于zImage
,那么zi_end - zi_start
自然就是zImage
的大小了。
再看一下内核的arch/arm/boot/compressed/head.S
里面有一个奇怪的标记0x04030201
:
start:
.type start,#function
.rept 7
__nop
.endr
ARM( mov r0, r0 )
ARM( b 1f )
THUMB( badr r12, 1f )
THUMB( bx r12 )
.word _magic_sig @ Magic numbers to help the loader
.word _magic_start @ absolute load/run zImage address
.word _magic_end @ zImage end address
.word 0x04030201 @ endianness flag
再对比一下uImage
的前面部分:
首先,u-boot
在启动内核时会将生成uImage
时mkimage
加的64
字节头去掉,去掉了之后就剩下普通的zImage-dtb
了。紧接着是zImage
的头,共48
字节,接下来是生成zImaeg
时上面那个奇怪的标记endianness flag
,和代码也有对应,镜像位置也对应。基本上就可以获取到在uImage
里面的dtb
文件了。
去掉了uImage
的64
字节头后,启动内核的起始内核位置实际是zImage
在内存中的起始位置,假设为addr
,使用地址addr+0x2C
获得zi_end
,addr+0x28
获得zi_start
,由于zi_start
都是0
,那么zi_end
就是zImage
的长度了。addr+0x30
加就是endianness flag
的标记位了。
且知道dtb
就在zImage
后面,那么使用偏移addr+zi_start
就可以获得dtb
的头了。
#define FDT_SIZE_OFFSET (0x1) // 0x4
#define IMAGE_FDT_OFFSET (0xb) // 0x2C
#define IMAGE_FLAG_OFFSET (0xc) // 0x30
#define IMAGE_FLAG (0x04030201)
当使用4
字节长度的指针时,IMAGE_FLAG_OFFSET
偏移是endianness flag
标记,然后IMAGE_FDT_OFFSET
偏移是zi_end
(当zi_start
是0
时表示zImage
长度,也就是dtb
开始的位置了。)
if (head[IMAGE_FLAG_OFFSET] != IMAGE_FLAG)
...
fdt_head = (ulong *)(image_start+head[IMAGE_FDT_OFFSET]);
好,齐活了。
获取到设备树后,u-boot会在boot_jump_linux
函数中将r2
寄存器设置为设备树的地址(relocate之后的地址), 这里就将设备树传递给内核了。内核在setup_arch->setup_machine_fdt
的过程中解析设备树的内容,完成启动过程。
x2C
#define IMAGE_FLAG_OFFSET (0xc) // 0x30
#define IMAGE_FLAG (0x04030201)
当使用`4`字节长度的指针时,`IMAGE_FLAG_OFFSET`偏移是`endianness flag`标记,然后`IMAGE_FDT_OFFSET`偏移是`zi_end`(当`zi_start`是`0`时表示`zImage`长度,也就是`dtb`开始的位置了。)
```c
if (head[IMAGE_FLAG_OFFSET] != IMAGE_FLAG)
...
fdt_head = (ulong *)(image_start+head[IMAGE_FDT_OFFSET]);
好,齐活了。
获取到设备树后,u-boot会在boot_jump_linux
函数中将r2
寄存器设置为设备树的地址(relocate之后的地址), 这里就将设备树传递给内核了。内核在setup_arch->setup_machine_fdt
的过程中解析设备树的内容,完成启动过程。
文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大
文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码
文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版
文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗
文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程
文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0
文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader
文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型
文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写
文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录
文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点
文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文