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_ADDR

request for a particular region of memory

LMB_MEM_ALLOC_ANY

allocate any available memory region

LMB_MEM_ALLOC_MAX

allocate memory below a particular address

enum lmb_map_op

memory map operation

Constants

LMB_MAP_OP_RESERVE

reserve memory

LMB_MAP_OP_FREE

free memory

LMB_MAP_OP_ADD

add memory

struct lmb_region

Description of one region

Definition

struct lmb_region {
  phys_addr_t base;
  phys_size_t size;
  u32 flags;
};

Members

base

Base address of the region

size

Size of the region

flags

Memory region attributes

struct lmb

The LMB structure

Definition

struct lmb {
  struct alist available_mem;
  struct alist used_mem;
  bool test;
};

Members

available_mem

List of memory available to LMB

used_mem

List of used/reserved memory regions

test

Is 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 type

Type of memory allocation request

u64 align

Alignment of the memory region requested(0 for none)

phys_addr_t *addr

Base address of the allocated memory region

phys_size_t size

Size in bytes of the allocation request

u32 flags

Memory 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

void

no 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 addr

Address to be tested

int flags

Bitmap 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 base

Base Address of region to be freed

phys_size_t size

Size of the region to be freed

u32 flags

Memory region attributes

Return

0 on success, negative error code on failure.

int io_lmb_setup(struct lmb *io_lmb)

Initialize LMB struct

Parameters

struct lmb *io_lmb

IO LMB to initialize

Return

0 on success, negative error code on failure.

void io_lmb_teardown(struct lmb *io_lmb)

Tear LMB struct down

Parameters

struct lmb *io_lmb

IO 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_lmb

LMB to add the space to

phys_addr_t base

Base Address of region to add

phys_size_t size

Size 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_lmb

LMB to alloc from

phys_size_t size

Size of the region requested

ulong align

Required 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_lmb

LMB to return the IO address space to

phys_addr_t base

Base Address of region to be freed

phys_size_t size

Size of the region to be freed

Return

0 on success, negative error code on failure.