Merge patch series "Allow showing the memory map"

Simon Glass <sjg@chromium.org> says:

This little series adds a new 'memmap' command, intended to show the
layout of memory within U-Boot and how much memory is available for
loading images.

Link: https://lore.kernel.org/r/20241021081934.289473-1-sjg@chromium.org
This commit is contained in:
Tom Rini 2024-10-25 14:22:36 -06:00
commit deafcdc8e0
17 changed files with 347 additions and 42 deletions

View file

@ -885,9 +885,21 @@ config MD5SUM_VERIFY
config CMD_MEMINFO
bool "meminfo"
default y if SANDBOX
help
Display memory information.
config CMD_MEMINFO_MAP
bool "- with memory map"
depends on CMD_MEMINFO
default y if SANDBOX
help
Shows a memory map, in addition to just the DRAM size. This allows
seeing where U-Boot's memory area is, at the top of DRAM, as well as
detail about each piece of it.
See doc/usage/cmd/meminfo.rst for more information.
config CMD_MEMORY
bool "md, mm, nm, mw, cp, cmp, base, loop"
default y

View file

@ -109,6 +109,7 @@ obj-$(CONFIG_CMD_LOG) += log.o
obj-$(CONFIG_CMD_LSBLK) += lsblk.o
obj-$(CONFIG_CMD_MD5SUM) += md5sum.o
obj-$(CONFIG_CMD_MEMORY) += mem.o
obj-$(CONFIG_CMD_MEMINFO) += meminfo.o
obj-$(CONFIG_CMD_IO) += io.o
obj-$(CONFIG_CMD_MII) += mii.o
obj-$(CONFIG_CMD_MISC) += misc.o

View file

@ -1379,17 +1379,6 @@ U_BOOT_CMD(
#endif
#ifdef CONFIG_CMD_MEMINFO
static int do_mem_info(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
puts("DRAM: ");
print_size(gd->ram_size, "\n");
return 0;
}
#endif
U_BOOT_CMD(
base, 2, 1, do_mem_base,
"print or set address offset",
@ -1433,14 +1422,6 @@ U_BOOT_CMD(
);
#endif /* CONFIG_CMD_MX_CYCLIC */
#ifdef CONFIG_CMD_MEMINFO
U_BOOT_CMD(
meminfo, 3, 1, do_mem_info,
"display memory information",
""
);
#endif
#ifdef CONFIG_CMD_RANDOM
U_BOOT_CMD(
random, 4, 0, do_random,

99
cmd/meminfo.c Normal file
View file

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2024 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <bloblist.h>
#include <bootstage.h>
#include <command.h>
#include <display_options.h>
#include <lmb.h>
#include <malloc.h>
#include <mapmem.h>
#include <asm/global_data.h>
DECLARE_GLOBAL_DATA_PTR;
static void print_region(const char *name, ulong base, ulong size, ulong *uptop)
{
ulong end = base + size;
printf("%-12s %8lx %8lx %8lx", name, base, size, end);
if (*uptop)
printf(" %8lx", *uptop - end);
putc('\n');
*uptop = base;
}
static void show_lmb(const struct lmb *lmb, ulong *uptop)
{
int i;
for (i = lmb->used_mem.count - 1; i >= 0; i--) {
const struct lmb_region *rgn = alist_get(&lmb->used_mem, i,
struct lmb_region);
/*
* Assume that the top lmb region is the U-Boot region, so just
* take account of the memory not already reported
*/
if (lmb->used_mem.count - 1)
print_region("lmb", rgn->base, *uptop - rgn->base,
uptop);
else
print_region("lmb", rgn->base, rgn->size, uptop);
*uptop = rgn->base;
}
}
static int do_meminfo(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
ulong upto, stk_bot;
puts("DRAM: ");
print_size(gd->ram_size, "\n");
if (!IS_ENABLED(CONFIG_CMD_MEMINFO_MAP))
return 0;
printf("\n%-12s %8s %8s %8s %8s\n", "Region", "Base", "Size", "End",
"Gap");
printf("------------------------------------------------\n");
upto = 0;
if (IS_ENABLED(CONFIG_VIDEO))
print_region("video", gd_video_bottom(),
gd_video_size(), &upto);
if (IS_ENABLED(CONFIG_TRACE))
print_region("trace", map_to_sysmem(gd_trace_buff()),
gd_trace_size(), &upto);
print_region("code", gd->relocaddr, gd->mon_len, &upto);
print_region("malloc", map_to_sysmem((void *)mem_malloc_start),
mem_malloc_end - mem_malloc_start, &upto);
print_region("board_info", map_to_sysmem(gd->bd),
sizeof(struct bd_info), &upto);
print_region("global_data", map_to_sysmem((void *)gd),
sizeof(struct global_data), &upto);
print_region("devicetree", map_to_sysmem(gd->fdt_blob),
fdt_totalsize(gd->fdt_blob), &upto);
if (IS_ENABLED(CONFIG_BOOTSTAGE))
print_region("bootstage", map_to_sysmem(gd_bootstage()),
bootstage_get_size(false), &upto);
if (IS_ENABLED(CONFIG_BLOBLIST))
print_region("bloblist", map_to_sysmem(gd_bloblist()),
bloblist_get_total_size(), &upto);
stk_bot = gd->start_addr_sp - CONFIG_STACK_SIZE;
print_region("stack", stk_bot, CONFIG_STACK_SIZE, &upto);
if (IS_ENABLED(CONFIG_LMB))
show_lmb(lmb_get(), &upto);
print_region("free", gd->ram_base, upto, &upto);
return 0;
}
U_BOOT_CMD(
meminfo, 1, 1, do_meminfo,
"display memory information",
""
);

View file

@ -501,9 +501,9 @@ static unsigned long reserve_stack_aligned(size_t size)
static int reserve_noncached(void)
{
/*
* The value of gd->start_addr_sp must match the value of malloc_start
* calculated in board_r.c:initr_malloc(), which is passed to
* dlmalloc.c:mem_malloc_init() and then used by
* The value of gd->start_addr_sp must match the value of
* mem_malloc_start calculated in board_r.c:initr_malloc(), which is
* passed to dlmalloc.c:mem_malloc_init() and then used by
* cache.c:noncached_init()
*
* These calculations must match the code in cache.c:noncached_init()
@ -582,7 +582,7 @@ static int reserve_fdt(void)
static int reserve_bootstage(void)
{
#ifdef CONFIG_BOOTSTAGE
int size = bootstage_get_size();
int size = bootstage_get_size(true);
gd->start_addr_sp = reserve_stack_aligned(size);
gd->boardf->new_bootstage = map_sysmem(gd->start_addr_sp, size);

View file

@ -204,8 +204,7 @@ static int initr_malloc(void)
*/
start = gd->relocaddr - TOTAL_MALLOC_LEN;
gd_set_malloc_start(start);
mem_malloc_init((ulong)map_sysmem(start, TOTAL_MALLOC_LEN),
TOTAL_MALLOC_LEN);
mem_malloc_init(start, TOTAL_MALLOC_LEN);
return 0;
}

View file

@ -520,17 +520,19 @@ int _bootstage_unstash_default(void)
}
#endif
int bootstage_get_size(void)
int bootstage_get_size(bool add_strings)
{
struct bootstage_data *data = gd->bootstage;
struct bootstage_record *rec;
int size;
int i;
size = sizeof(struct bootstage_data);
for (rec = data->record, i = 0; i < data->rec_count;
i++, rec++)
size += strlen(rec->name) + 1;
if (add_strings) {
struct bootstage_data *data = gd->bootstage;
struct bootstage_record *rec;
int i;
for (rec = data->record, i = 0; i < data->rec_count; i++, rec++)
size += strlen(rec->name) + 1;
}
return size;
}

View file

@ -16,6 +16,8 @@
#include <asm/global_data.h>
#include <malloc.h>
#include <mapmem.h>
#include <string.h>
#include <asm/io.h>
#include <valgrind/memcheck.h>
@ -598,9 +600,9 @@ void *sbrk(ptrdiff_t increment)
void mem_malloc_init(ulong start, ulong size)
{
mem_malloc_start = start;
mem_malloc_end = start + size;
mem_malloc_brk = start;
mem_malloc_start = (ulong)map_sysmem(start, size);
mem_malloc_end = mem_malloc_start + size;
mem_malloc_brk = mem_malloc_start;
#ifdef CONFIG_SYS_MALLOC_DEFAULT_TO_INIT
malloc_init();

View file

@ -678,9 +678,7 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
spl_set_bd();
if (IS_ENABLED(CONFIG_SPL_SYS_MALLOC)) {
mem_malloc_init((ulong)map_sysmem(SPL_SYS_MALLOC_START,
SPL_SYS_MALLOC_SIZE),
SPL_SYS_MALLOC_SIZE);
mem_malloc_init(SPL_SYS_MALLOC_START, SPL_SYS_MALLOC_SIZE);
gd->flags |= GD_FLG_FULL_MALLOC_INIT;
}
if (!(gd->flags & GD_FLG_SPL_INIT)) {

128
doc/usage/cmd/meminfo.rst Normal file
View file

@ -0,0 +1,128 @@
.. SPDX-License-Identifier: GPL-2.0+:
.. index::
single: meminfo (command)
meminfo command
===============
Synopsis
--------
::
meminfo
Description
-----------
The meminfo command shows the amount of memory. If ``CONFIG_CMD_MEMINFO_MAP`` is
enabled, then it also shows the layout of memory used by U-Boot and the region
which is free for use by images.
The layout of memory is set up before relocation, within the init sequence in
``board_init_f()``, specifically the various ``reserve_...()`` functions. This
'reservation' of memory starts from the top of RAM and proceeds downwards,
ending with the stack. This results in the maximum possible amount of memory
being left free for image-loading.
The meminfo command writes the DRAM size, then the rest of its outputs in 5
columns:
Region
Name of the region
Base
Base address of the region, i.e. where it starts in memory
Size
Size of the region, which may be a little smaller than the actual size
reserved, e.g. due to alignment
End
End of the region. The last byte of the region is one lower than the address
shown here
Gap
Gap between the end of this region and the base of the one above
Regions shown are:
video
Memory reserved for video framebuffers. This reservation happens in the
bind() methods of all video drivers which are present before relocation,
so the size depends on that maximum amount of memory which all such drivers
want to reserve. This may be significantly greater than the amount actually
needed, if the display is ultimately set to a smaller resolution or colour
depth than the maximum supported.
code
U-Boot's code and Block-Starting Symbol (BSS) region. Before relocation,
U-Boot copies its code to a high region and sets up a BSS immediately after
that. The size of this region is generally therefore ``__bss_end`` -
``__image_copy_start``
malloc
Contains the malloc() heap. The size of this is set by
``CONFIG_SYS_MALLOC_LEN``.
board_info
Contains the ``bd_info`` structure, with some information about the current
board.
global_data
Contains the global-data structure, pointed to by ``gd``. This includes
various pointers, values and flags which control U-Boot.
devicetree
Contains the flatted devicetree blob (FDT) being used by U-Boot to configure
itself and its devices.
bootstage
Contains the bootstage records, which keep track of boot time as U-Boot
executes. The size of this is determined by
``CONFIG_BOOTSTAGE_RECORD_COUNT``, with each record taking approximately
32 bytes.
bloblist
Contains the bloblist, which is a list of tables and other data created by
U-Boot while executed. The size of this is determined by
``CONFIG_BLOBLIST_SIZE``.
stack
Contains U-Boot's stack, growing downwards from the top. The nominal size of
this region is set by ``CONFIG_STACK_SIZE`` but there is no actual limit
enforced, so the stack can grow behind that. Images should be loaded lower
in memory to avoid any conflict.
free
Free memory, which is available for loading images. The base address of
this is ``gd->ram_base`` which is generally set by ``CFG_SYS_SDRAM_BASE``.
Example
-------
This example shows output with both ``CONFIG_CMD_MEMINFO`` and
``CONFIG_CMD_MEMINFO_MAP`` enabled::
=> meminfo
DRAM: 256 MiB
Region Base Size End Gap
------------------------------------------------
video f000000 1000000 10000000
code ec3a000 3c5d28 efffd28 2d8
malloc 8c38000 6002000 ec3a000 0
board_info 8c37f90 68 8c37ff8 8
global_data 8c37d80 208 8c37f88 8
devicetree 8c33000 4d7d 8c37d7d 3
bootstage 8c32c20 3c8 8c32fe8 18
bloblist 8c32000 400 8c32400 820
stack 7c31ff0 1000000 8c31ff0 10
free 0 7c31ff0 7c31ff0 0
Return value
------------
The return value $? is always 0 (true).

View file

@ -84,6 +84,7 @@ Shell commands
cmd/loads
cmd/loadx
cmd/loady
cmd/meminfo
cmd/mbr
cmd/md
cmd/mmc

View file

@ -543,6 +543,36 @@ static_assert(sizeof(struct global_data) == GD_SIZE);
#define gd_set_upl(val)
#endif
#if CONFIG_IS_ENABLED(BLOBLIST)
#define gd_bloblist() gd->bloblist
#else
#define gd_bloblist() NULL
#endif
#if CONFIG_IS_ENABLED(BOOTSTAGE)
#define gd_bootstage() gd->bootstage
#else
#define gd_bootstage() NULL
#endif
#if CONFIG_IS_ENABLED(TRACE)
#define gd_trace_buff() gd->trace_buff
#define gd_trace_size() CONFIG_TRACE_BUFFER_SIZE
#else
#define gd_trace_buff() NULL
#define gd_trace_size() 0
#endif
#if CONFIG_IS_ENABLED(VIDEO)
#define gd_video_top() gd->video_top
#define gd_video_bottom() gd->video_bottom
#define gd_video_size() (gd->video_top - gd->video_bottom)
#else
#define gd_video_top() 0
#define gd_video_bottom() 0
#define gd_video_size() 0
#endif
/**
* enum gd_flags - global data flags
*

View file

@ -371,9 +371,10 @@ int bootstage_unstash(const void *base, int size);
/**
* bootstage_get_size() - Get the size of the bootstage data
*
* @add_strings: true to add the size of attached strings (for stashing)
* Return: size of boostage data in bytes
*/
int bootstage_get_size(void);
int bootstage_get_size(bool add_strings);
/**
* bootstage_init() - Prepare bootstage for use
@ -444,7 +445,7 @@ static inline int bootstage_unstash(const void *base, int size)
return 0; /* Pretend to succeed */
}
static inline int bootstage_get_size(void)
static inline int bootstage_get_size(bool add_strings)
{
return 0;
}

View file

@ -981,6 +981,14 @@ extern ulong mem_malloc_start;
extern ulong mem_malloc_end;
extern ulong mem_malloc_brk;
/**
* mem_malloc_init() - Set up the malloc() pool
*
* Sets the region of memory to be used for all future calls to malloc(), etc.
*
* @start: Start address
* @size: Size in bytes
*/
void mem_malloc_init(ulong start, ulong size);
#ifdef __cplusplus

View file

@ -887,12 +887,12 @@ int lmb_init(void)
return 0;
}
#if CONFIG_IS_ENABLED(UNIT_TEST)
struct lmb *lmb_get(void)
{
return &lmb;
}
#if CONFIG_IS_ENABLED(UNIT_TEST)
int lmb_push(struct lmb *store)
{
int ret;

View file

@ -19,8 +19,9 @@ obj-$(CONFIG_CMD_FDT) += fdt.o
obj-$(CONFIG_CONSOLE_TRUETYPE) += font.o
obj-$(CONFIG_CMD_HISTORY) += history.o
obj-$(CONFIG_CMD_LOADM) += loadm.o
obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o
obj-$(CONFIG_CMD_MEMINFO) += meminfo.o
obj-$(CONFIG_CMD_MEMORY) += mem_copy.o
obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o
ifdef CONFIG_CMD_PCI
obj-$(CONFIG_CMD_PCI_MPS) += pci_mps.o
endif

42
test/cmd/meminfo.c Normal file
View file

@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Test for 'meminfo' command
*
* Copyright 2024 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <dm/test.h>
#include <test/cmd.h>
#include <test/ut.h>
/* Test 'meminfo' command */
static int cmd_test_meminfo(struct unit_test_state *uts)
{
ut_assertok(run_command("meminfo", 0));
ut_assert_nextline("DRAM: 256 MiB");
ut_assert_nextline_empty();
ut_assert_nextline("Region Base Size End Gap");
ut_assert_nextlinen("-");
/* For now we don't worry about checking the values */
ut_assert_nextlinen("video");
ut_assert_nextlinen("code");
ut_assert_nextlinen("malloc");
ut_assert_nextlinen("board_info");
ut_assert_nextlinen("global_data");
ut_assert_nextlinen("devicetree");
ut_assert_nextlinen("bootstage");
ut_assert_nextlinen("bloblist");
ut_assert_nextlinen("stack");
/* we expect at least one lmb line, but don't know how many */
ut_assert_nextlinen("lmb");
ut_assert_skip_to_linen("free");
ut_assert_console_end();
return 0;
}
CMD_TEST(cmd_test_meminfo, UTF_CONSOLE);