1. U-Boot获取布局内存
U-Boot在启动过程中,默认内存布局通常在.config
中定义。
1CONFIG_SYS_SDRAM_BASE=0x240000000
2CONFIG_SYS_DDR_SIZE=0xC0000000
在DDR控制器初始化完成之后,就要确定系统中有多少可用内存,这通常是由dram_init_banksize
来实现的,而不同的板级文件会实现不同的dram_init_banksize
,这也就决定了不同的板级可以定义不同的获取内存布局的方法。这里介绍一种从dtb中获取内存布局的实现,这可以通过简单的调用fdtdec_setup_memory_banksize
来实现。而在使能了CONFIG_OF_LIBFDT
的情况下,U-Boot又会通过fdt_fixup_memory_banks
来为内核dtb添加或修改memory
节点,这样就可以只在U-Boot中定义memory
信息,内核dtb省略memory
定义,从而可以使其更加简洁通用。相信这也是后续U-Boot推荐使用的方式。
这里涉及的函数逻辑都比较简单,下面简单介绍一下。
1.1. fdtdec_setup_memory_banksize:从dtb获取内存布局
fdtdec_setup_memory_banksize的逻辑很简单,就是从dtb中解析memory节点,然后将内存的start和size填充到gd->bd->bi_dram数组。
1.2. arch_fixup_fdt:转换gd->bd->bi_dram
arch_fixup_fdt是一个处理器或板级相关的函数,默认实现为一个空的weak函数。这里以ARM的实现举例。
bi_dram本质是一个结构体数组,而fdt_fixup_memory_banks的参数是start数组和size数组,arch_fixup_fdt完成一个简单的转换。
1 struct { /* RAM configuration */
2 phys_addr_t start;
3 phys_size_t size;
4 } bi_dram[CONFIG_NR_DRAM_BANKS];
1.3. fdt_fixup_memory_banks:修改内核dtb
fdt_fixup_memory_banks的主要逻辑如下:
- 找到内核dtb的memory节点,如果没有则创建一个memory节点
- 设置节点的属性为device_type
- 将所有的memory的start和size信息写到内核dtb
这里需要注意的是,如果内核dtb中已有memory节点,fdt_fixup_memory_banks只修改第一个memory节点。
2. 函数源码
U-Boot版本:v2023.04
2.1. dram_init_banksize
如下只是dram_init_banksize的一个示例,不同的板级会不同的实现。
1int dram_init_banksize(void)
2{
3 return fdtdec_setup_memory_banksize();
4}
2.2. fdtdec_setup_memory_banksize
1/**
2 * fdtdec_setup_memory_banksize() - decode and populate gd->bd->bi_dram
3 *
4 * Decode the /memory 'reg' property to determine the address and size of the
5 * memory banks. Use this data to populate the global data board info with the
6 * phys address and size of memory banks.
7 *
8 * This function should be called from a boards dram_init_banksize(). This
9 * helper function allows for boards to query the device tree for memory bank
10 * information instead of hard coding the information in cases where it cannot
11 * be detected automatically.
12 *
13 * Return: 0 if OK, -EINVAL if the /memory node or reg property is missing or
14 * invalid
15 */
16
17int fdtdec_setup_memory_banksize(void)
18{
19 int bank, ret, reg = 0;
20 struct resource res;
21 ofnode mem = ofnode_null();
22
23 mem = get_next_memory_node(mem);
24 if (!ofnode_valid(mem)) {
25 debug("%s: Missing /memory node\n", __func__);
26 return -EINVAL;
27 }
28
29 for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
30 ret = ofnode_read_resource(mem, reg++, &res);
31 if (ret < 0) {
32 reg = 0;
33 mem = get_next_memory_node(mem);
34 if (!ofnode_valid(mem))
35 break;
36
37 ret = ofnode_read_resource(mem, reg++, &res);
38 if (ret < 0)
39 break;
40 }
41
42 if (ret != 0)
43 return -EINVAL;
44
45 gd->bd->bi_dram[bank].start = (phys_addr_t)res.start;
46 gd->bd->bi_dram[bank].size =
47 (phys_size_t)(res.end - res.start + 1);
48
49 debug("%s: DRAM Bank #%d: start = 0x%llx, size = 0x%llx\n",
50 __func__, bank,
51 (unsigned long long)gd->bd->bi_dram[bank].start,
52 (unsigned long long)gd->bd->bi_dram[bank].size);
53 }
54
55 return 0;
56}
2.3. arch_fixup_fdt
源码文件:arch/arm/lib/bootm-fdt.c
1int arch_fixup_fdt(void *blob)
2{
3 __maybe_unused int ret = 0;
4#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_OF_LIBFDT)
5 struct bd_info *bd = gd->bd;
6 int bank;
7 u64 start[CONFIG_NR_DRAM_BANKS];
8 u64 size[CONFIG_NR_DRAM_BANKS];
9
10 for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
11 start[bank] = bd->bi_dram[bank].start;
12 size[bank] = bd->bi_dram[bank].size;
13#ifdef CONFIG_ARMV7_NONSEC
14 ret = armv7_apply_memory_carveout(&start[bank], &size[bank]);
15 if (ret)
16 return ret;
17#endif
18 }
19
20#ifdef CONFIG_OF_LIBFDT
21 ret = fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS);
22 if (ret)
23 return ret;
24#endif
25
26#ifdef CONFIG_ARMV8_SPIN_TABLE
27 ret = spin_table_update_dt(blob);
28 if (ret)
29 return ret;
30#endif
31
32#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV8_PSCI) || \
33 CONFIG_IS_ENABLED(SEC_FIRMWARE_ARMV8_PSCI)
34 ret = psci_update_dt(blob);
35 if (ret)
36 return ret;
37#endif
38#endif
39
40#ifdef CONFIG_FMAN_ENET
41 ret = fdt_update_ethernet_dt(blob);
42 if (ret)
43 return ret;
44#endif
45 return 0;
46}
2.4. fdt_fixup_memory_banks
1/**
2 * fdt_fixup_memory_banks - Update DT memory node
3 * @blob: Pointer to DT blob
4 * @start: Pointer to memory start addresses array
5 * @size: Pointer to memory sizes array
6 * @banks: Number of memory banks
7 *
8 * Return: 0 on success, negative value on failure
9 *
10 * Based on the passed number of banks and arrays, the function is able to
11 * update existing DT memory nodes to match run time detected/changed memory
12 * configuration. Implementation is handling one specific case with only one
13 * memory node where multiple tuples could be added/updated.
14 * The case where multiple memory nodes with a single tuple (base, size) are
15 * used, this function is only updating the first memory node without removing
16 * others.
17 */
18int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks)
19{
20 int err, nodeoffset;
21 int len, i;
22 u8 tmp[MEMORY_BANKS_MAX * 16]; /* Up to 64-bit address + 64-bit size */
23
24 if (banks > MEMORY_BANKS_MAX) {
25 printf("%s: num banks %d exceeds hardcoded limit %d."
26 " Recompile with higher MEMORY_BANKS_MAX?\n",
27 __FUNCTION__, banks, MEMORY_BANKS_MAX);
28 return -1;
29 }
30
31 err = fdt_check_header(blob);
32 if (err < 0) {
33 printf("%s: %s\n", __FUNCTION__, fdt_strerror(err));
34 return err;
35 }
36
37 /* find or create "/memory" node. */
38 nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory");
39 if (nodeoffset < 0)
40 return nodeoffset;
41
42 err = fdt_setprop(blob, nodeoffset, "device_type", "memory",
43 sizeof("memory"));
44 if (err < 0) {
45 printf("WARNING: could not set %s %s.\n", "device_type",
46 fdt_strerror(err));
47 return err;
48 }
49
50 for (i = 0; i < banks; i++) {
51 if (start[i] == 0 && size[i] == 0)
52 break;
53 }
54
55 banks = i;
56
57 if (!banks)
58 return 0;
59
60 len = fdt_pack_reg(blob, tmp, start, size, banks);
61
62 err = fdt_setprop(blob, nodeoffset, "reg", tmp, len);
63 if (err < 0) {
64 printf("WARNING: could not set %s %s.\n",
65 "reg", fdt_strerror(err));
66 return err;
67 }
68 return 0;
69}