From 4b5c7fe7e1f43487370b747e8176478dcd09e3b5 Mon Sep 17 00:00:00 2001 From: Alexander von Gluck IV Date: Sat, 6 Nov 2021 15:20:35 -0500 Subject: [PATCH] system/boot: add optional alignment to kernel_args_malloc * A few things need alignment, instead of forcing them all to align themselves, support alignment of the kernel_args * Default of 1 is "no alignment" Change-Id: Iff05dcec8adaa963c8444d701464ea11616062f6 Reviewed-on: https://review.haiku-os.org/c/haiku/+/4698 Reviewed-by: Alex von Gluck IV Reviewed-by: Adrien Destugues --- headers/private/kernel/boot/stage2.h | 8 +- src/system/boot/arch/x86/arch_cpu.cpp | 7 +- src/system/boot/loader/kernel_args.cpp | 123 +++++++++++++------------ src/system/boot/platform/efi/dtb.cpp | 9 +- src/system/boot/platform/riscv/fdt.cpp | 8 +- 5 files changed, 74 insertions(+), 81 deletions(-) diff --git a/headers/private/kernel/boot/stage2.h b/headers/private/kernel/boot/stage2.h index f7835a4db6..74fbfcbc7b 100644 --- a/headers/private/kernel/boot/stage2.h +++ b/headers/private/kernel/boot/stage2.h @@ -19,14 +19,14 @@ extern KMessage gBootVolume; extern "C" { #endif -extern void *kernel_args_malloc(size_t size); -extern char *kernel_args_strdup(const char *string); -extern void kernel_args_free(void *address); - extern int main(struct stage2_args *args); #ifdef __cplusplus } #endif +extern void *kernel_args_malloc(size_t size, uint8 alignment = 1); +extern char *kernel_args_strdup(const char *string); +extern void kernel_args_free(void *address); + #endif /* KERNEL_BOOT_STAGE2_H */ diff --git a/src/system/boot/arch/x86/arch_cpu.cpp b/src/system/boot/arch/x86/arch_cpu.cpp index f24bac34dc..fad2cd98dc 100644 --- a/src/system/boot/arch/x86/arch_cpu.cpp +++ b/src/system/boot/arch/x86/arch_cpu.cpp @@ -350,11 +350,10 @@ ucode_load(BootVolume& volume) } ssize_t length = stat.st_size; - const uint32 alignment = 16; -#define ALIGN(size, align) (((size) + align - 1) & ~(align - 1)) - void *buffer = kernel_args_malloc(length + alignment - 1); + + // 16-byte alignment required + void *buffer = kernel_args_malloc(length, 16); if (buffer != NULL) { - buffer = (void*)ALIGN((addr_t)buffer, alignment); if (read(fd, buffer, length) != length) { dprintf("ucode_load: couldn't read microcode file\n"); kernel_args_free(buffer); diff --git a/src/system/boot/loader/kernel_args.cpp b/src/system/boot/loader/kernel_args.cpp index 0b68133b60..74a63185f5 100644 --- a/src/system/boot/loader/kernel_args.cpp +++ b/src/system/boot/loader/kernel_args.cpp @@ -369,68 +369,10 @@ ignore_physical_memory_ranges_beyond_4gb() // #pragma mark - kernel_args allocations -/*! This function can be used to allocate memory that is going - to be passed over to the kernel. For example, the preloaded_image - structures are allocated this way. - The boot loader heap doesn't make it into the kernel! -*/ -extern "C" void* -kernel_args_malloc(size_t size) -{ - //dprintf("kernel_args_malloc(): %ld bytes (%ld bytes left)\n", size, sFree); - - if (sFirstFree != NULL && size <= sFree) { - // there is enough space in the current buffer - void* address = sFirstFree; - sFirstFree = (void*)((addr_t)sFirstFree + size); - sLast = address; - sFree -= size; - - return address; - } - - if (size > kChunkSize / 2 && sFree < size) { - // the block is so large, we'll allocate a new block for it - void* block = NULL; - if (platform_allocate_region(&block, size, B_READ_AREA | B_WRITE_AREA, - false) != B_OK) { - return NULL; - } - - // Translate the address if needed on the current platform - addr_t translated_block; - platform_bootloader_address_to_kernel_address(block, &translated_block); - if (add_kernel_args_range((void *)translated_block, size) != B_OK) - panic("kernel_args max range too low!\n"); - - return block; - } - - // just allocate a new block and "close" the old one - void* block = NULL; - if (platform_allocate_region(&block, kChunkSize, B_READ_AREA | B_WRITE_AREA, - false) != B_OK) { - return NULL; - } - - sFirstFree = (void*)((addr_t)block + size); - sLast = block; - sFree = kChunkSize - size; - - // Translate the address if needed on the current platform - addr_t translated_block; - platform_bootloader_address_to_kernel_address(block, &translated_block); - if (add_kernel_args_range((void *)translated_block, kChunkSize) != B_OK) - panic("kernel_args max range too low!\n"); - - return block; -} - - /*! Convenience function that copies strdup() functions for the kernel args heap. */ -extern "C" char* +char* kernel_args_strdup(const char* string) { if (string == NULL || string[0] == '\0') @@ -447,11 +389,72 @@ kernel_args_strdup(const char* string) } +/*! This function can be used to allocate (optionally aligned) memory that + is going to be passed over to the kernel. For example, the preloaded_image + structures are allocated this way. + The boot loader heap doesn't make it into the kernel! +*/ +void* +kernel_args_malloc(size_t size, uint8 alignment) +{ + //dprintf("kernel_args_malloc(): %ld bytes (%ld bytes left)\n", size, sFree); + + #define ALIGN(addr, align) (((addr) + align - 1) & ~(align - 1)) + size_t alignedSize = size + alignment - 1; + + if (sFirstFree != NULL && alignedSize <= sFree) { + // there is enough space in the current buffer + void* address = (void*)ALIGN((addr_t)sFirstFree, alignment); + sFirstFree = (void*)((addr_t)sFirstFree + alignedSize); + sLast = address; + sFree -= alignedSize; + + return address; + } + + if (alignedSize > kChunkSize / 2 && sFree < alignedSize) { + // the block is so large, we'll allocate a new block for it + void* block = NULL; + if (platform_allocate_region(&block, alignedSize, + B_READ_AREA | B_WRITE_AREA, false) != B_OK) { + return NULL; + } + + // Translate the address if needed on the current platform + addr_t translated_block; + platform_bootloader_address_to_kernel_address(block, &translated_block); + if (add_kernel_args_range((void *)translated_block, size) != B_OK) + panic("kernel_args max range too low!\n"); + + return (void*)ALIGN((addr_t)block, alignment); + } + + // just allocate a new block and "close" the old one + void* block = NULL; + if (platform_allocate_region(&block, kChunkSize, B_READ_AREA | B_WRITE_AREA, + false) != B_OK) { + return NULL; + } + + sFirstFree = (void*)((addr_t)block + alignedSize); + sLast = block; + sFree = kChunkSize - alignedSize; + + // Translate the address if needed on the current platform + addr_t translated_block; + platform_bootloader_address_to_kernel_address(block, &translated_block); + if (add_kernel_args_range((void *)translated_block, kChunkSize) != B_OK) + panic("kernel_args max range too low!\n"); + + return (void*)ALIGN((addr_t)block, alignment); +} + + /*! This function frees a block allocated via kernel_args_malloc(). It's very simple; it can only free the last allocation. It's enough for its current usage in the boot loader, though. */ -extern "C" void +void kernel_args_free(void* block) { if (sLast != block) { diff --git a/src/system/boot/platform/efi/dtb.cpp b/src/system/boot/platform/efi/dtb.cpp index 8470113c72..88241e898e 100644 --- a/src/system/boot/platform/efi/dtb.cpp +++ b/src/system/boot/platform/efi/dtb.cpp @@ -464,13 +464,8 @@ dtb_set_kernel_args() if (sDtbTable != NULL) { #if defined(__ARM__) || defined(__riscv) - // FDT needs to be 8-byte aligned for libfdt - // TODO: we need kernel_args_malloc with alignment! - #define FDT_ALIGNMENT 8 - #define FDT_ALIGN(addr) (((addr) + FDT_ALIGNMENT - 1) & ~(FDT_ALIGNMENT - 1)) - gKernelArgs.arch_args.fdt - = (void*)FDT_ALIGN((addr_t)kernel_args_malloc(sDtbSize - + FDT_ALIGNMENT - 1)); + // libfdt requires 8-byte alignment + gKernelArgs.arch_args.fdt = (void*)(addr_t)kernel_args_malloc(sDtbSize, 8); if (gKernelArgs.arch_args.fdt != NULL) memcpy(gKernelArgs.arch_args.fdt, sDtbTable, sDtbSize); diff --git a/src/system/boot/platform/riscv/fdt.cpp b/src/system/boot/platform/riscv/fdt.cpp index 56afdd5600..ad23365e3b 100644 --- a/src/system/boot/platform/riscv/fdt.cpp +++ b/src/system/boot/platform/riscv/fdt.cpp @@ -86,12 +86,8 @@ fdt_set_kernel_args() { uint32_t fdtSize = fdt_totalsize(gFdt); - // FDT needs to be 8-byte aligned for libfdt - // TODO: We need kernel_args_malloc with alignment! - #define FDT_ALIGNMENT 8 - #define FDT_ALIGN(addr) (((addr) + FDT_ALIGNMENT - 1) & ~(FDT_ALIGNMENT - 1)) - gKernelArgs.arch_args.fdt - = (void*)FDT_ALIGN((addr_t)kernel_args_malloc(fdtSize + FDT_ALIGNMENT - 1)); + // libfdt requires 8-byte alignment + gKernelArgs.arch_args.fdt = (void*)(addr_t)kernel_args_malloc(fdtSize, 8); if (gKernelArgs.arch_args.fdt != NULL) { memcpy(gKernelArgs.arch_args.fdt, gFdt, fdtSize);