Logical Memory Blocks (LMB)
U-Boot has support for reserving chunks of memory which is primarily used for loading images to the DRAM memory, before these are booted, or written to non-volatile storage medium. This functionality is provided through the Logical Memory Blocks (LMB) module.
Introduction
The LMB module manages allocation requests for memory region not occupied by the U-Boot image. Allocation requests that are made through malloc() and similar functions result in memory getting allocated from the heap region, which is part of the U-Boot image. Typically, the heap memory is a few MiB in size. Loading an image like the linux kernel might require lot more memory than what the heap can provide. Such allocations are usually handled through the LMB module.
The U-Boot image typically gets relocated to the top of the usable DRAM memory region. A typical memory layout looks as follows:
| |
| |
| |
| |
| |
--- +--------------+ <--- U-Boot ram top
| | |
| | Text |
| +--------------+
| | |
| | Data |
| +--------------+
| | |
| | BSS |
U-Boot Image +--------------+
| | |
| | Heap |
| | |
| +--------------+
| | |
| | |
| | Stack |
| | |
| | |
--- +--------------+
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
+--------------+ <--- ram start
The region of memory below the U-Boot image is the one controlled by the LMB module.
Types of LMB Allocations
There are two classes of allocation requests that get made to the LMB module. One type of allocation requests are requesting memory of a particular number of bytes. This type of allocation is similar to that done using the malloc type of function calls. The other type of allocations, are requests made for a specific memory address. The second type of allocations are usually made for loading images to a particular memory address.
LMB design Pre 2025.01
The earlier versions of U-Boot (pre 2025.01 release) had a local memory map based LMB implementation whereby it was possible to declare the LMB map inside a function or a C file. This design resulted in temporary, non-global LMB maps, which also allowed for re-use of memory. This meant that it was possible to use a region of memory to load some image, and subsequently the same region of memory could be used for loading a different image. A typical example of this usage would be loading an image to a memory address, followed by writing that image to some non-volatile storage medium. Once this is done, the same address can be used for loading a different image and then writing it to it’s non-volatile storage destination. Typically, environment variables like loadaddr, kernel_addr_r, ramdisk_addr_r are used for loading images to memory regions.
Current LMB implementation
Changes were made in the 2025.01 release to make the LMB memory map global and persistent. With this, the LMB memory map is the same across all of U-Boot, and also persists as long as U-Boot is active. Even with this change, there has been consistency as far as re-use of memory is concerned to maintain backward compatibility. It is allowed for re-requesting the same region of memory if the memory region has a particular attribute (LMB_NONE).
As part of the platform boot, DRAM memory available for use in U-Boot gets added to the LMB memory map. Any allocation requests made subsequently will be made from this memory added as part of the board init.
Allocation API
Any request for non-heap memory can be made through the LMB allocation API.
int lmb_alloc_mem(enum lmb_mem_type type, u64 align,
phys_addr_t *addr, phys_size_t size,
u32 flags);
Correspondingly, the allocated memory can be free’d
long lmb_free(phys_addr_t base, phys_size_t size, u32 flags);
For a detailed API description, please refer to the header file.
UEFI allocations with LMB as the backend
The UEFI specification describes boot-time API’s for allocation of memory. These API’s use the same memory that is being used by the LMB module. Pre 2025.01 release, there wasn’t any synchronisation between the EFI sub-system and the LMB module about the memory that was getting allocated by each of these modules. This was the primary reason for making the LMB memory map global and persistent. With this change, the EFI memory allocation API’s have also been changed to use the LMB module as the backend for the allocation requests. Any other sub-system which might wish to use the same memory region for it’s use can then use the LMB as the backend for the memory allocations and it’s associated book-keeping.
API documentation
Memory region attribute flags.
LMB_NONE: No special request
LMB_NOMAP: Don’t add to MMU configuration
LMB_NOOVERWRITE: The memory region cannot be overwritten/re-reserved
LMB_NONOTIFY: Do not notify other modules of changes to this memory region
-
enum lmb_mem_type
type of memory allocation request
Constants
LMB_MEM_ALLOC_ADDRrequest for a particular region of memory
LMB_MEM_ALLOC_ANYallocate any available memory region
LMB_MEM_ALLOC_MAXallocate memory below a particular address
-
enum lmb_map_op
memory map operation
Constants
LMB_MAP_OP_RESERVEreserve memory
LMB_MAP_OP_FREEfree memory
LMB_MAP_OP_ADDadd memory
-
struct lmb_region
Description of one region
Definition
struct lmb_region {
phys_addr_t base;
phys_size_t size;
u32 flags;
};
Members
baseBase address of the region
sizeSize of the region
flagsMemory region attributes
-
struct lmb
The LMB structure
Definition
struct lmb {
struct alist available_mem;
struct alist used_mem;
bool test;
};
Members
available_memList of memory available to LMB
used_memList of used/reserved memory regions
testIs structure being used for LMB tests
-
int lmb_alloc_mem(enum lmb_mem_type type, u64 align, phys_addr_t *addr, phys_size_t size, u32 flags)
Request LMB memory
Parameters
enum lmb_mem_type typeType of memory allocation request
u64 alignAlignment of the memory region requested(0 for none)
phys_addr_t *addrBase address of the allocated memory region
phys_size_t sizeSize in bytes of the allocation request
u32 flagsMemory region attributes to be set
Description
Allocate a region of memory where the allocation is based on the parameters that have been passed to the function.The first parameter specifies the type of allocation that is being requested. The second parameter, align is used to specify if the allocation is to be made with a particular alignment. Use 0 for no alignment requirements.
The allocated address is returned through the addr parameter when type is LMB_MEM_ALLOC_ANY or LMB_MEM_ALLOC_MAX. If type is LMB_MEM_ALLOC_ADDR the addr parameter would contain the address being requested.
The flags parameter is used to specify the memory attributes of the requested region.
When the allocation is of type LMB_MEM_ALLOC_ADDR, the return value can be -EINVAL if the requested memory region is not part of the LMB memory map, and -EEXIST if the requested region is already allocated.
Return
0 on success, -ve value on failure
-
int lmb_init(void)
Initialise the LMB module.
Parameters
voidno arguments
Return
0 on success, negative error code on failure.
Description
Initialise the LMB lists needed for keeping the memory map. There are two lists, in form of allocated list data structure. One for the available memory, and one for the used memory. Initialise the two lists as part of board init. Add memory to the available memory list and reserve common areas by adding them to the used memory list.
-
int lmb_is_reserved_flags(phys_addr_t addr, int flags)
Test if address is in reserved region with flag bits set
Parameters
phys_addr_t addrAddress to be tested
int flagsBitmap with bits to be tested
Description
The function checks if a reserved region comprising addr exists which has all flag bits set which are set in flags.
Return
1 if matching reservation exists, 0 otherwise.
-
long lmb_free(phys_addr_t base, phys_size_t size, u32 flags)
Free up a region of memory
Parameters
phys_addr_t baseBase Address of region to be freed
phys_size_t sizeSize of the region to be freed
u32 flagsMemory region attributes
Return
0 on success, negative error code on failure.
Parameters
struct lmb *io_lmbIO LMB to initialize
Return
0 on success, negative error code on failure.
Parameters
struct lmb *io_lmbIO LMB to teardown
-
long io_lmb_add(struct lmb *io_lmb, phys_addr_t base, phys_size_t size)
Add an IOVA range for allocations
Parameters
struct lmb *io_lmbLMB to add the space to
phys_addr_t baseBase Address of region to add
phys_size_t sizeSize of the region to add
Description
Add the IOVA space [base, base + size] to be managed by io_lmb.
Return
0 on success, negative error code on failure.
-
phys_addr_t io_lmb_alloc(struct lmb *io_lmb, phys_size_t size, ulong align)
Allocate specified IO memory address with specified alignment
Parameters
struct lmb *io_lmbLMB to alloc from
phys_size_t sizeSize of the region requested
ulong alignRequired address and size alignment
Description
Allocate a region of IO memory. The base parameter is used to specify the base address of the requested region.
Return
Base IO address on success, 0 on error.
-
long io_lmb_free(struct lmb *io_lmb, phys_addr_t base, phys_size_t size)
Free up a region of IOVA space
Parameters
struct lmb *io_lmbLMB to return the IO address space to
phys_addr_t baseBase Address of region to be freed
phys_size_t sizeSize of the region to be freed
Return
0 on success, negative error code on failure.