Proposal: U-Boot memory management

Heinrich Schuchardt xypron.glpk at gmx.de
Fri Dec 29 16:42:41 CET 2023



Am 29. Dezember 2023 06:36:09 MEZ schrieb Simon Glass <sjg at chromium.org>:
>Hi,
>
>On Sat, Dec 16, 2023 at 6:01 PM Simon Glass <sjg at chromium.org> wrote:
>>
>> Hi,
>>
>> This records my thoughts after a discussion with Ilias & Heinrich re
>> memory allocation in U-Boot.
>>
>> 1. malloc()
>>
>> malloc() is used for programmatic memory allocation. It allows memory
>> to be freed. It is not designed for very large allocations (e.g. a
>> 10MB kernel or 100MB ramdisk).
>>
>> 2. lmb
>>
>> lmb is used for large blocks of memory, such as those needed for a
>> kernel or ramdisk. Allocation is only transitory, for the purposes of
>> loading some images and booting. If the boot fails, then all lmb
>> allocations go away.
>>
>> lmb is set up by getting all available memory and then removing what
>> is used by U-Boot (code, data, malloc() space, etc.)
>>
>> lmb reservations have a few flags so that areas of memory can be
>> provided with attributes
>>
>> There are some corner cases...e.g. loading a file does an lmb
>> allocation but only for the purpose of avoiding a file being loaded
>> over U-Boot code/data. The allocation is dropped immediately after the
>> file is loaded. Within the bootm command, or when using standard boot,
>> this would be fairly easy to solve.
>>
>> Linux has renamed lmb to memblock. We should consider doing the same.
>>
>> 3. EFI
>>
>> EFI has its own memory-allocation tables.
>>
>> Like lmb, EFI is able to deal with large allocations. But via a 'pool'
>> function it can also do smaller allocations similar to malloc(),
>> although each one uses at least 4KB at present.
>>
>> EFI allocations do not go away when a boot fails.
>>
>> With EFI it is possible to add allocations post facto, in which case
>> they are added to the allocation table just as if the memory was
>> allocated with EFI to begin with.
>>
>> The EFI allocations and the lmb allocations use the same memory, so in
>> principle could conflict.
>>
>> EFI allocations are sometimes used to allocate internal U-Boot data as
>> well, if needed by the EFI app. For example, while efi_image_parse()
>> uses malloc(), efi_var_mem.c uses EFI allocations since the code runs
>> in the app context and may need to access the memory after U-Boot has
>> exited. Also efi_smbios.c uses allocate_pages() and then adds a new
>> mapping as well.
>>
>> EFI memory has attributes, including what the memory is used for (to
>> some degree of granularity). See enum efi_memory_type and struct
>> efi_mem_desc. In the latter there are also attribute flags - whether
>> memory is cacheable, etc.
>>
>> EFI also has the x86 idea of 'conventional' memory, meaning (I
>> believe) that below 4GB that isn't reserved for the hardware/system.
>> This is meaningless, or at least confusing, on ARM systems.
>>
>> 4. reservations
>>
>> It is perhaps worth mentioning a fourth method of memory management,
>> where U-Boot reserves chunks of memory before relocation (in
>> board_init_f.c), e.g. for the framebuffer, U-Boot code, the malloc()
>> region, etc.
>>
>>
>> Problems
>> —-------
>>
>> There are no urgent problems, but here are some things that could be improved:
>>
>> 1. EFI should attach most of its data structures to driver model. This
>> work has started, with the partition support, but more effort would
>> help. This would make it easier to see which memory is related to
>> devices and which is separate.
>>
>> 2. Some drivers do EFI reservations today, whether EFI is used for
>> booting or not (e.g. rockchip video rk_vop_probe()).
>>
>> 3. U-Boot doesn't really map arch-specific memory attributes (e.g.
>> armv8's struct mm_region) to EFI ones.
>>
>> 4. EFI duplicates some code from bootm, some of which relates to
>> memory allocation (e.g. FDT fixup).
>>
>> 5. EFI code is used even if EFI is never used to boot
>>
>> 6. EFI allocations can result in the same memory being used as has
>> already been allocated by lmb. Users may load files which overwrite
>> memory allocated by EFI.
>
>7. We need to support doing an allocation when a file is loaded (to
>ensure files do not overlap), without making it too difficult to load
>multiple files to the same place, if desired.
>
>>
>>
>> Lifetime
>> --------
>>
>> We have three different memory allocators with different purposes. Can
>> we unify them a little?
>>
>> Within U-Boot:
>> - malloc() space lives forever
>> - lmb lives while setting out images for booting
>> - EFI (mostly) lives while booting an EFI app
>>
>> In practice, EFI is set up early in U-Boot. Some of this is necessary,
>> some not. EFI allocations stay around forever. This works OK since
>> large allocations are normally not done in EFI, so memory isn't really
>> consumed to any great degree by the boot process.
>>
>> What happens to EFI allocations if the app returns? They are still
>> present, in case another app is run. This seems fine.
>>
>> API
>> –--
>> Can we unify some APIs?
>>
>> It should be possible to use lmb for large EFI memory allocations, so
>> long as they are only needed for booting. We effectively do this
>> today, since EFI does not manage the arrangement of loaded images in
>> memory. for the most part.
>>
>> It would not make sense to use EFI allocation to replace lmb and
>> malloc(), of course.
>>
>> Could we use a common (lower-level) API for allocation, used by both
>> lmb and EFI? They do have some similarities. However they have
>> different lifetime constraints (EFI allocations are never dropped,
>> unlikely lmb).
>>
>> ** Overall, it seems that the existence of memory allocation in
>> boot-time services has created confusion. Memory allocation is
>> muddled, with both U-Boot code and boot-time services calling the same
>> memory allocator. This just has not been clearly thought out.
>>
>>
>> Proposal
>> —-------
>>
>> Here are some ideas:
>>
>> 1. For video, use the driver model API to locate the video regions, or
>> block off the entire framebuffer memory, for all devices as a whole.
>> Use efi_add_memory_map()
>>
>> 2. Add memory attributes to UCLASS_RAM and use them in EFI, mapping to
>> the EFI_MEMORY_... attributes in struct efi_mem_desc.
>>
>> 3. Add all EFI reservations just before booting the app, as we do with
>> devicetree fixup. With this model, malloc() and lmb are used for all
>> allocation. Then efi_add_memory_map() is called for each region in
>> turn just before booting. Memory attributes are dealt with above. The
>> type (enum efi_memory_type) can be determined simply by the data
>> structure stored in it, as is done today. For example, SMBIOS tables
>> can use EFI_ACPI_RECLAIM_MEMORY. Very few types are used and EFI code
>> understands the meaning of each.
>>
>> 4. Avoid setting up EFI memory at the start of U-Boot. Do it only when
>> booting. This looks to require very little effort.
>>
>> 5. Avoid calling efi_allocate_pages() and efi_allocate_pool() outside
>> boot-time services. This solves the problem 6. If memory is needed by
>> an app, allocate it with malloc() and see 3. There are only two
>> efi_allocate_pages() (smbios and efi_runtime). There are more calls of
>> efi_allocate_pool(), but most of these seem easy to fix up. For
>> example, efi_init_event_log() allocates a buffer, but this can be
>> allocated in normal malloc() space or in a bloblist.
>>
>> 6. Don't worry too much about whether EFI will be used for booting.
>> The cost is likely not that great: use bootstage to measure it as is
>> done for driver model. Try to minmise the cost of its tables,
>> particularly for execution time, but otherwise just rely on the
>> ability to disable EFI_LOADER.
>
>7. Add a flag to the 'load' command:
>
>-m <type> - make an lmb allocation for the file
>   <type> is the image type to use (kernel, ramdisk, flat_dt)
>
>any existing allocation for that type will be automatically freed
>first. If <type> is "none" then no freeing is possible: any loaded
>images just stack up in lmb.

It is unclear what your suggestion means for the address field.

If you still specify an address, what would be the benefit of such a flag?

If -m replaces the address, how do you refer to different loaded files of the same type, e.g. two EFI binaries?

Best regards

Heinrich


>
>Add an 'lmb' (or memblock) command to allow listing and clearing allocations.
>
>Regards,
>Simon


More information about the U-Boot mailing list