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的主要逻辑如下:

  1. 找到内核dtb的memory节点,如果没有则创建一个memory节点
  2. 设置节点的属性为device_type
  3. 将所有的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}