一块S905D3的板子,贴了4G的内存,但是在linux下只看到3456MB的可用内存,于是分析原因。
之前的文章也分析到,影响内存大小有两个地方,一是uboot中的 firmware/timing.c,这个定义对了uboot才可以正确分析出内存的大小。uboot启动的日志节选
auto size-- 65535DDR cs0 size: 2048MB
DDR cs1 size: 2048MB
DMC_DDR_CTRL: 00700024DDR size: 3928MB
...
U-Boot 2015.01 (Feb 09 2023 - 16:12:09)
DRAM: 3.8 GiB
...
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
[rsvmem] fdt set /reserved-memory/ramoops reg error.
Loading Ramdisk to 7f4c6000, end 7f7ff545 ... OK
Loading Device Tree to 000000001ffe4000, end 000000001ffff66d ... OK
fdt_fixup_memory_banks, reg:0000000000000000
影响内存大小的第二个地方在dtb中。/memory@00000000/linux,usable-memory 会影响linux下可用内存的大小。如果在dts中,完全屏蔽了memory一段的设置,则linux看到的内存大小是3456MB。如果设置 linux,usable-memory = <0x0 0xf0000000>; 则linux看到的内存大小为3840MB。
内核文档中对于 usable-memory 的解析
- linux,usable-memory : This property overrides the reg property.
It can be used if the bootloader changes the reg property to
improper values.
那就是说,如果dts中配置了 linux,usable-memory,内存大小以usable-memory的配置为准。如果dts中没有这个配置,则内存大小由bootloader填入memory的reg字段决定。
从上面的uboot日志中,可以看到 fdt_fixup_memory_banks, reg:0000000000000000 一段。其对应的代码位于 common/fdt_support.c
int fdt_fixup_memory_banks(void *blob, u64 start[], u64 size[], int banks)
{
int err, nodeoffset;
int len;
const void *reg;
u8 tmp[MEMORY_BANKS_MAX * 16]; /* Up to 64-bit address + 64-bit size */
if (banks > MEMORY_BANKS_MAX) {
printf("%s: num banks %d exceeds hardcoded limit %d."
" Recompile with higher MEMORY_BANKS_MAX?\n",
__FUNCTION__, banks, MEMORY_BANKS_MAX);
return -1;
}
err = fdt_check_header(blob);
if (err < 0) {
printf("%s: %s\n", __FUNCTION__, fdt_strerror(err));
return err;
}
/* find or create "/memory" node. */
nodeoffset = fdt_find_or_add_subnode(blob, 0, "memory");
if (nodeoffset < 0)
return nodeoffset;
err = fdt_setprop(blob, nodeoffset, "device_type", "memory",
sizeof("memory"));
if (err < 0) {
printf("WARNING: could not set %s %s.\n", "device_type",
fdt_strerror(err));
return err;
}
reg = fdt_getprop(blob, nodeoffset, "reg", NULL);
printf("%s, reg:%p\n", __func__, reg);
if (reg) {
printf("DTS already have 'reg' property\n");
return 0;
}
len = fdt_pack_reg(blob, tmp, start, size, banks);
err = fdt_setprop(blob, nodeoffset, "reg", tmp, len);
if (err < 0) {
printf("WARNING: could not set %s %s.\n",
"reg", fdt_strerror(err));
return err;
}
return 0;
}
基本没干啥,就是往dtb里面写入 /memory/reg 的信息。试了一下,添加打印信息,把 u64 start[], u64 size[] 都打印出来。
start=0 size=3623878656
size对应的十六进制为 d800 0000,很明显没到4G应有的1 0000 0000。为什么是这个值呢?需要进一步跟踪代码。先看什么地方调用了 fdt_fixup_memory_banks,在 arch/arm/lib/bootm-fdt.c
int arch_fixup_fdt(void *blob)
{
bd_t *bd = gd->bd;
int bank, ret;
u64 start[CONFIG_NR_DRAM_BANKS];
u64 size[CONFIG_NR_DRAM_BANKS];
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
start[bank] = bd->bi_dram[bank].start;
size[bank] = bd->bi_dram[bank].size;
}
ret = fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS);
./board/amlogic/configs/g12a_u200_v1.h:#define CONFIG_NR_DRAM_BANKS 1
继续找什么地方设置了 bd->bi_dram,在 common/board_f.c 的 show_dram_config
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
if ((gd->bd->bi_dram[0].size+CONFIG_SYS_MEM_TOP_HIDE) > (0xE0000000UL)) {
gd->bd->bi_dram[0].size = 0xE0000000UL - CONFIG_SYS_MEM_TOP_HIDE;
}
#else
if (gd->bd->bi_dram[0].size > (0xE0000000UL)) {
gd->bd->bi_dram[0].size = 0xE0000000UL;
}
#endif
./build/include/autoconf.mk:CONFIG_SYS_MEM_TOP_HIDE=0x08000000
因此 0xE0000000 - 0x08000000 = 0xD800 0000。为什么要限制这个内存大小呢?从uboot的代码修改记录找到的答案 https://github.com/khadas/u-boot/commit/106a245a7de2a0038f5db6ddcbc8bbe48c8bddfe
Problem:
g12a/b 4GB ddr use 3584M for cache alignment reason
好无语的限制
网友评论