Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ppc64: Avoid restricted memory areas when allocating memory #152

Open
wants to merge 10 commits into
base: rhel-9.3.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions grub-core/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion grub-core/kern/ieee1275/cmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
}

Expand Down
3 changes: 3 additions & 0 deletions grub-core/kern/ieee1275/ieee1275.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
73 changes: 59 additions & 14 deletions grub-core/kern/ieee1275/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
#ifdef __sparc__
#include <grub/machine/kernel.h>
#endif
#if defined(__powerpc__) || defined(__i386__)
#include <grub/ieee1275/alloc.h>
#endif
#include <grub/lockdown.h>

/* The maximum heap size we're going to claim at boot. Not used by sparc. */
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand All @@ -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
Expand Down
55 changes: 46 additions & 9 deletions grub-core/loader/powerpc/ieee1275/linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <grub/lib/cmdline.h>
#include <grub/cache.h>
#include <grub/linux.h>
#include <grub/ieee1275/alloc.h>

GRUB_MOD_LICENSE ("GPLv3+");

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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);

Expand Down
39 changes: 39 additions & 0 deletions include/grub/ieee1275/alloc.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#ifndef GRUB_IEEE1275_ALLOC_HEADER
#define GRUB_IEEE1275_ALLOC_HEADER 1

#include <stdbool.h>

#include <grub/memory.h>

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 */
4 changes: 4 additions & 0 deletions include/grub/ieee1275/ieee1275.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions include/grub/powerpc/ieee1275/ieee1275.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
3 changes: 3 additions & 0 deletions include/grub/sparc64/ieee1275/ieee1275.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down