U-Boot-fdt-GPT

  1. 一、介绍
    1. dtb在U-boot中的位置
    2. dtb解析接口
    3. fdt 命令
  2. 二、u-boot 获取GPT分区表
    1. 3588-android-uboot

U-Boot官网:https://docs.u-boot.org/en/latest/

u-boot启动流程

  • 板子上电以后,首先执行的是ROM中的一段启动代码。启动代码根据寄存器/外部管脚配置,确定是进入下载模式,还是从某介质(Flash/EMMC/SD卡等存储设备)启动u-boot

ROM中的代码是固化的,无法修改

一、介绍

FDT,flatted device tree,扁平设备树,简单来说,就是将部分设备信息结构存放到device tree文件中。

uboot最终将其编译成dtb文件,使用过程中通过解析该dtb来获取板级设备信息。

U-boot的dtb和kernel中的dtb是一致的,有关fdt的详细介绍,参考doc/README.fdt-control

dtb在U-boot中的位置

  • dtb能够以两种形式编译到U-boot的镜像中
  1. dtb和u-boot的bin文件分离(imx6q中使用的这种方式,在.config文件中可以查看到)

通过CONFIG_OF_SEPARATE宏定义使能,dtb最后会追加到u-boot的bin文件的最后面,通过u-boot的结束地址符号_end符号来获取dtb的地址

  1. dtb集成到u-boot的bin文件内部

通过CONFIG_OF_EMBED宏定义使能,dtb会位于u-boot的.dtb.init.rodata段中,通过__dtb_dt_begin符号来获取dtb

  1. 获取dts文件的地址gd->fdt_blob
1
2
3
4
5
6
7
8
9
10
11
// 宏用来表示是否把dtb文件放在uboot.bin的文件中
CONFIG_OF_EMBED

// 单独编译dtb文件
CONFIG_OF_SEPARATE,编译出来的dtb放在uboot.bin的最后面,就是dtb追加到uboot的bin文件后面时,通过_end符号来获取dtb地址

gd->fdt_blob = (ulong *)&_end;

// 可以通过fdtcontroladdr环境变量来指定fdt的地址
gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
(uintptr_t)gd->fdt_blob);

dtb解析接口

  • 定义在lib/fdtdec.c文件中,节点变量node中存放的是偏移地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 获得dtb下某个节点的路径path的偏移,偏移就代表这个节点
int fdt_path_offset(const void *fdt, const char *path)
eg:node = fdt_path_offset(gd->fdt_blob, “/aliases”);

// 获得节点node的某个字符串属性值
const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp)
eg: mac = fdt_getprop(gd->fdt_blob, node, “mac-address”, &len);

// 获得节点node的某个整形数组属性值
int fdtdec_get_int_array(const void *blob, int node, const char *prop_name, u32 *array, int count)
eg: ret = fdtdec_get_int_array(blob, node, “interrupts”, cell, ARRAY_SIZE(cell));

// 获得节点node的地址属性值
fdt_addr_t fdtdec_get_addr(const void *blob, int node, const char *prop_name)
eg:fdtdec_get_addr(blob, node, “reg”);

// 获得config节点下的整形属性、bool属性、字符串等等
fdtdec_get_config_int、fdtdec_get_config_bool、fdtdec_get_config_string

// 获得chosen下的name节点的偏移
int fdtdec_get_chosen_node(const void *blob, const char *name)

// 获得chosen下name属性的值
const char *fdtdec_get_chosen_prop(const void *blob, const char *name)
  • 定义在lib/fdtdec_common.c文件中
1
2
3
4
5
6
// 获得节点node的某个整形属性值
int fdtdec_get_int(const void *blob, int node, const char *prop_name, int default_val)
eg: bus->udelay = fdtdec_get_int(blob, node, “i2c-gpio,delay-us”, DEFAULT_UDELAY);

// 获得节点node的某个无符号整形属性值
fdtdec_get_uint

fdt 命令

对于u-boot提供了fdt的相关命令

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
fdt - flattened device tree utility commands

Usage:
fdt addr [-c] <addr> [<length>] - Set the [control] fdt location to <addr>
fdt apply <addr> - Apply overlay to the DT
fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active
fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed
fdt print <path> [<prop>] - Recursive print starting at <path>
fdt list <path> [<prop>] - Print one level starting at <path>
fdt get value <var> <path> <prop> - Get <property> and store in <var>
fdt get name <var> <path> <index> - Get name of node <index> and store in <var>
fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>
fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>
fdt set <path> <prop> [<val>] - Set <property> [to <val>]
fdt mknode <path> <node> - Create a new node after <path>
fdt rm <path> [<prop>] - Delete the node or <property>
fdt header - Display header info
fdt bootcpu <id> - Set boot cpuid
fdt memory <addr> <size> - Add/Update memory node
fdt rsvmem print - Show current mem reserves
fdt rsvmem add <addr> <size> - Add a mem reserve
fdt rsvmem delete <index> - Delete a mem reserves
fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree
<start>/<end> - initrd start/end addr
NOTE: Dereference aliases by omitting the leading '/', e.g. fdt print ethernet0.

fdt print加path参数,则打path内容,如下(其中/memory是path):

1
2
3
4
5
6
7
U-Boot> fdt print /memory
memory {
device_type = "memory";
reg = <0x70000000 0x4000000>;
};

U-Boot> fdt print #不加参数时,打印出整颗树

二、u-boot 获取GPT分区表

在uboot中通过命令打印分区表

1
part list mmc 0

3588-android-uboot

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include <common.h>
#include <command.h>
#include <android_image.h>
#include <mmc.h>
#include <stdlib.h>
#include <memalign.h>
#include <fdtdec.h>

#define PART_MAX_COUNT 128
#define LAB_SIZE 512
#define HEADER_OFFSET LAB_SIZE
#define ENTRY_OFFSET (2 * LAB_SIZE)
#define VAL1_OFFSET sizeof(u64)
#define VAL2_OFFSET (2 * sizeof(u64))

static u64 get_gpt_blk_cnt_and_print(struct blk_desc *dev_desc,
gpt_header *gpt_head, gpt_entry **gpt_pte) {
char efi_str[PARTNAME_SZ + 1];
u64 gpt_part_size, gpt_blk_cnt = 0;
gpt_entry *gpt_e;
int i;

gpt_e = *gpt_pte;
for (i = 0; i < gpt_head->num_partition_entries; i++) {

raite_gpt_convert_efi_name_to_char(efi_str, gpt_e[i].partition_name,
PARTNAME_SZ + 1);

printf("%s: part: %2d name - GPT: %16s ",
__func__, i, efi_str);
gpt_part_size = le64_to_cpu(gpt_e[i].ending_lba) -
le64_to_cpu(gpt_e[i].starting_lba) + 1;
gpt_blk_cnt += gpt_part_size;

if(gpt_part_size == 1)
break;

printf("size(LBA) - GPT: %8llu ",
(unsigned long long)gpt_part_size);

printf("start LBA - GPT: %8llu \n",
le64_to_cpu(gpt_e[i].starting_lba));
}

return gpt_blk_cnt + gpt_e[0].starting_lba - 1;
}

static int get_gpt_meta_data(u64 *data_size, void **data)
{
gpt_header *pgpt_head;
gpt_entry *entries;
void *meta_data;
u64 meta_data_size, gpt_entries_size;
struct blk_desc *dev_desc = NULL;
struct mmc *mmc = NULL;
u64 blk_size = 0;
u64 blk_cnt = 0;
u64 tag = 0x55AA;
lbaint_t lba;

if (!data_size || !data) {
printf("%s *** ERROR: Invalid Argument(s) ***\n", __func__);
return -1;
}

mmc = do_returnmmc();
if (!mmc)
return CMD_RET_FAILURE;

dev_desc = mmc_get_blk_desc(mmc);
if (!dev_desc) {
printf("%s *** ERROR: mmc_get_blk_desc err ***\n", __func__);
return -1;
}

gpt_entries_size = sizeof(gpt_entry) * PART_MAX_COUNT;
meta_data_size = LAB_SIZE + sizeof(gpt_header) + gpt_entries_size;
meta_data = malloc(meta_data_size);
if(!meta_data) {
printf("%s *** ERROR: malloc memory (gpt meta data) ***\n", __func__);
return -1;
}

memset(meta_data, 0, meta_data_size);
pgpt_head = (gpt_header *)((char *)meta_data + HEADER_OFFSET);
entries = (gpt_entry *)((char *)meta_data + ENTRY_OFFSET);
ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz);

/* Read MBR Header from device */
lba = 0; /* MBR is always at 0 */
blk_cnt = 1; /* MBR (1 block) */
if (blk_dread(dev_desc, lba, blk_cnt, (ulong *)mbr) != 1) {
printf("*** ERROR: Can't read MBR header ***\n");
goto ERROR_OUT;
}

/* Read GPT Header from device */
lba = GPT_PRIMARY_PARTITION_TABLE_LBA;
blk_cnt = 1; /* GPT Header (1 block) */
if (blk_dread(dev_desc, lba, blk_cnt, pgpt_head) != 1) {
printf("%s *** ERROR: Can't read GPT header ***\n", __func__);
goto ERROR_OUT;
}

lba = GPT_PRIMARY_PARTITION_TABLE_LBA;
if (validate_gpt_header(pgpt_head, lba, dev_desc->lba)) {
printf("%s *** ERROR: validate_gpt_header GPT header ***\n", __func__);
goto ERROR_OUT;
}

if (dev_desc->sig_type == SIG_TYPE_NONE) {
efi_guid_t empty = {};
if (memcmp(&pgpt_head->disk_guid, &empty, sizeof(empty))) {
dev_desc->sig_type = SIG_TYPE_GUID;
memcpy(&dev_desc->guid_sig, &pgpt_head->disk_guid,
sizeof(empty));
} else if (mbr->unique_mbr_signature != 0) {
dev_desc->sig_type = SIG_TYPE_MBR;
dev_desc->mbr_sig = mbr->unique_mbr_signature;
}
}

/* Read GPT Entries from device */
lba = le64_to_cpu(pgpt_head->partition_entry_lba);
blk_cnt = BLOCK_CNT((le32_to_cpu(pgpt_head->num_partition_entries) *
le32_to_cpu(pgpt_head->sizeof_partition_entry)),
dev_desc);
if (blk_dread(dev_desc, lba, blk_cnt, entries) != blk_cnt) {
printf("%s *** ERROR:read entries (lba=%llu) ***\n",
__func__, pgpt_head->partition_entry_lba);
goto ERROR_OUT;
}

blk_size = dev_desc->blksz;
validate_gpt_entries(pgpt_head, entries);

debug("%s read entries lba %llu (blk_cnt %llu blk_size=%llu)\n",
__func__, (unsigned long long)(ulong)lba, blk_cnt, blk_size);

blk_cnt = get_gpt_blk_cnt(dev_desc, pgpt_head, &entries);

/*
* build info, layout of meta_data:
* u64 tag | u64 blk_size | u64 blk_cnt | 488 bytes | gpt header | gpt entries
*/
blk_cnt = get_gpt_blk_cnt(dev_desc, pgpt_head, &entries);
/*this value makes we known the reserved memory is available*/
memcpy(meta_data, (void *)&tag, sizeof(u64));
memcpy(meta_data + VAL1_OFFSET, (void *)&blk_size, sizeof(u64));
memcpy(meta_data + VAL2_OFFSET, (void *)&blk_cnt, sizeof(u64));

*data_size = meta_data_size;
*data = meta_data;

return 0;

ERROR_OUT:
free(meta_data);
meta_data = NULL;
return -1;
}

参考:

Device Tree(四):文件结构解析 (wowotech.net)

Linux设备树语法分析详解教程(三)u-boot设备树的传递 - 知乎 (zhihu.com)

Linux设备树语法分析详解教程(四)kernel的解析 - 知乎 (zhihu.com)

https://www.cnblogs.com/solo666/p/16518154.html


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yengii@qq.com