diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index dd49939aaa..dc1c158450 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -155,6 +155,7 @@ endif if COND_i386_ieee1275 KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/alloc.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h @@ -242,6 +243,7 @@ endif if COND_powerpc_ieee1275 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/alloc.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c index cb42f60ebe..810a089a93 100644 --- a/grub-core/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -129,7 +129,10 @@ grub_ieee1275_find_options (void) #if defined(__powerpc__) if (grub_strncmp (tmp, "IBM,", 4) == 0) - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY); + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_POWER_VM); + } #endif } @@ -196,6 +199,9 @@ grub_ieee1275_find_options (void) grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM); grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF); +#if defined(__powerpc__) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_POWER_KVM); +#endif } } diff --git a/grub-core/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c index 86f81a3c46..f8a195e663 100644 --- a/grub-core/kern/ieee1275/ieee1275.c +++ b/grub-core/kern/ieee1275/ieee1275.c @@ -593,6 +593,9 @@ grub_ieee1275_claim (grub_addr_t addr, grub_size_t size, unsigned int align, *result = args.base; if (args.base == IEEE1275_CELL_INVALID) return -1; + grub_dprintf ("mmap", "CLAIMED: 0x%" PRIxGRUB_IEEE1275_CELL_T " (%" + PRIuGRUB_IEEE1275_CELL_T " MiB) size: %" PRIuGRUB_SIZE " MiB\n", + args.base, args.base >> 20, ALIGN_UP (size, 1 << 20) >> 20); return 0; } diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 8e7f742fad..48f938afcd 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -46,6 +46,9 @@ #ifdef __sparc__ #include #endif +#if defined(__powerpc__) || defined(__i386__) +#include +#endif #include /* The maximum heap size we're going to claim at boot. Not used by sparc. */ @@ -317,11 +320,11 @@ count_free (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, return 0; } -static int -regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, - unsigned int flags, void *data) +int +grub_regions_claim (grub_uint64_t addr, grub_uint64_t len, + grub_memory_type_t type, void *data) { - grub_uint32_t total = *(grub_uint32_t *) data; + struct regions_claim_request *rcr = data; grub_uint64_t linux_rmo_save; if (type != GRUB_MEMORY_AVAILABLE) @@ -453,6 +456,9 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, check_kernel_dump (&upper_mem_limit); + grub_dprintf ("ieee1275", "upper_mem_limit is at %llx (%lld MiB)\n", + upper_mem_limit, upper_mem_limit >> 20); + /* * we order these cases to prefer higher addresses and avoid some * splitting issues @@ -471,7 +477,7 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, /* We must not exceed the upper_mem_limit (assuming it's >= RMO_ADDR_MAX) */ if (addr + len > upper_mem_limit) { - /* take the bigger chunk from either below linux_rmo_save or above upper_mem_limit */ + /* Take the bigger chunk from either below linux_rmo_save or above RMO_ADDR_MAX. */ len = upper_mem_limit - addr; if (orig_addr < linux_rmo_save && linux_rmo_save - orig_addr > len) { @@ -501,11 +507,25 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, } } } - if (flags & GRUB_MM_ADD_REGION_CONSECUTIVE && len < total) + + /* Honor alignment restrictions on candidate addr */ + if (rcr->align) + { + grub_uint64_t align_addr = ALIGN_UP (addr, rcr->align); + grub_uint64_t d = align_addr - addr; + + if (d > len) + return 0; + + len -= d; + addr = align_addr; + } + + if (rcr->flags & GRUB_MM_ADD_REGION_CONSECUTIVE && len < rcr->total) return 0; - if (len > total) - len = total; + if (len > rcr->total) + len = rcr->total; if (len) { @@ -514,13 +534,16 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, err = grub_claimmap (addr, len); if (err) return err; - grub_mm_init_region ((void *) (grub_addr_t) addr, len); - total -= len; + if (rcr->init_region) + grub_mm_init_region ((void *) (grub_addr_t) addr, len); + rcr->total -= len; + + rcr->addr = addr; } - *(grub_uint32_t *) data = total; + *(grub_uint32_t *) data = rcr->total; - if (total == 0) + if (rcr->total == 0) return 1; return 0; @@ -530,14 +553,36 @@ static int heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, void *data) { - return regions_claim (addr, len, type, GRUB_MM_ADD_REGION_NONE, data); + struct regions_claim_request rcr = { + .flags = GRUB_MM_ADD_REGION_NONE, + .total = *(grub_uint32_t *) data, + .init_region = true, + }; + int ret; + + ret = grub_regions_claim (addr, len, type, &rcr); + + *(grub_uint32_t *) data = rcr.total; + + return ret; } static int region_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, void *data) { - return regions_claim (addr, len, type, GRUB_MM_ADD_REGION_CONSECUTIVE, data); + struct regions_claim_request rcr = { + .flags = GRUB_MM_ADD_REGION_CONSECUTIVE, + .total = *(grub_uint32_t *) data, + .init_region = true, + }; + int ret; + + ret = grub_regions_claim (addr, len, type, &rcr); + + *(grub_uint32_t *) data = rcr.total; + + return ret; } static grub_err_t diff --git a/grub-core/loader/powerpc/ieee1275/linux.c b/grub-core/loader/powerpc/ieee1275/linux.c index 6fdd863130..5c9ad4867a 100644 --- a/grub-core/loader/powerpc/ieee1275/linux.c +++ b/grub-core/loader/powerpc/ieee1275/linux.c @@ -30,6 +30,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -116,6 +117,22 @@ grub_linux_claimmap_iterate (grub_addr_t target, grub_size_t size, return ctx.found_addr; } +static grub_addr_t +grub_linux_claimmap_iterate_restricted (grub_size_t size, grub_size_t align) +{ + struct regions_claim_request rcr = { + .flags = GRUB_MM_ADD_REGION_CONSECUTIVE, + .total = size, + .init_region = false, + .addr = (grub_uint64_t) -1, + .align = align, + }; + + grub_machine_mmap_iterate (grub_regions_claim, &rcr); + + return rcr.addr; +} + static grub_err_t grub_linux_boot (void) { @@ -227,10 +244,18 @@ grub_linux_load64 (grub_elf_t elf, const char *filename) offset = entry - base_addr; /* Linux's incorrectly contains a virtual address. */ - /* On some systems, firmware occupies the memory we're trying to use. - * Happily, Linux can be loaded anywhere (it relocates itself). Iterate - * until we find an open area. */ - seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF64_LOADMASK, linux_size, align); + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_POWER_VM) || + grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_POWER_KVM)) + { + seg_addr = grub_linux_claimmap_iterate_restricted (linux_size, align); + } + else + { + /* On some systems, firmware occupies the memory we're trying to use. + * Happily, Linux can be loaded anywhere (it relocates itself). Iterate + * until we find an open area. */ + seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF64_LOADMASK, linux_size, align); + } if (seg_addr == (grub_addr_t) -1) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory"); @@ -339,13 +364,25 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), size = grub_get_initrd_size (&initrd_ctx); - first_addr = linux_addr + linux_size; - /* Attempt to claim at a series of addresses until successful in - the same way that grub_rescue_cmd_linux does. */ - addr = grub_linux_claimmap_iterate (first_addr, size, 0x100000); + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_POWER_VM) || + grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_POWER_KVM)) + { + addr = grub_linux_claimmap_iterate_restricted (size, 0x100000); + } + else + { + /* Attempt to claim at a series of addresses until successful in + the same way that grub_rescue_cmd_linux does. */ + first_addr = linux_addr + linux_size; + addr = grub_linux_claimmap_iterate (first_addr, size, 0x100000); + } + if (addr == (grub_addr_t) -1) - goto fail; + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + goto fail; + } grub_dprintf ("loader", "Loading initrd at 0x%x, size 0x%x\n", addr, size); diff --git a/include/grub/ieee1275/alloc.h b/include/grub/ieee1275/alloc.h new file mode 100644 index 0000000000..67a7856572 --- /dev/null +++ b/include/grub/ieee1275/alloc.h @@ -0,0 +1,39 @@ +/* alloc.h - Memory allocation for PowerVM, KVM on Power, and i386 */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 Free Software Foundation, Inc. + * Copyright (C) 2023 IBM Corporation + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_IEEE1275_ALLOC_HEADER +#define GRUB_IEEE1275_ALLOC_HEADER 1 + +#include + +#include + +struct regions_claim_request { + unsigned int flags; /* GRUB_MM_ADD_REGION_(NONE|CONSECUTIVE) */ + grub_uint32_t total; /* number of requested bytes */ + bool init_region; /* whether to add memory to the heap using grub_mm_init_region() */ + grub_uint64_t addr; /* result address */ + grub_size_t align; /* alignment restrictions */ +}; + +int EXPORT_FUNC(grub_regions_claim) (grub_uint64_t addr, grub_uint64_t len, + grub_memory_type_t type, void *data); + +#endif /* GRUB_IEEE1275_ALLOC_HEADER */ diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 27b9cf259b..3fee66569d 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -152,6 +152,10 @@ enum grub_ieee1275_flag */ GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY, #endif + + GRUB_IEEE1275_FLAG_POWER_VM, + + GRUB_IEEE1275_FLAG_POWER_KVM, }; extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag); diff --git a/include/grub/powerpc/ieee1275/ieee1275.h b/include/grub/powerpc/ieee1275/ieee1275.h index 3c7683fad2..4eb2070188 100644 --- a/include/grub/powerpc/ieee1275/ieee1275.h +++ b/include/grub/powerpc/ieee1275/ieee1275.h @@ -25,4 +25,7 @@ #define GRUB_IEEE1275_CELL_SIZEOF 4 typedef grub_uint32_t grub_ieee1275_cell_t; +#define PRIxGRUB_IEEE1275_CELL_T PRIxGRUB_UINT32_T +#define PRIuGRUB_IEEE1275_CELL_T PRIuGRUB_UINT32_T + #endif /* ! GRUB_IEEE1275_MACHINE_HEADER */ diff --git a/include/grub/sparc64/ieee1275/ieee1275.h b/include/grub/sparc64/ieee1275/ieee1275.h index 4b18468d8d..ccc71aac66 100644 --- a/include/grub/sparc64/ieee1275/ieee1275.h +++ b/include/grub/sparc64/ieee1275/ieee1275.h @@ -25,6 +25,9 @@ #define GRUB_IEEE1275_CELL_SIZEOF 8 typedef grub_uint64_t grub_ieee1275_cell_t; +#define PRIxGRUB_IEEE1275_CELL_T PRIxGRUB_UINT64_T +#define PRIuGRUB_IEEE1275_CELL_T PRIuGRUB_UINT64_T + /* Encoding of 'mode' argument to grub_ieee1275_map_physical() */ #define IEEE1275_MAP_WRITE 0x0001 /* Writable */ #define IEEE1275_MAP_READ 0x0002 /* Readable */