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
的过程中解析设备树的内容,完成启动过程。
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland