On Wed, Aug 02, 2023 at 11:48:18AM -0400, Stefan Berger wrote:
> When a kernel dump is present then restrict the high memory regions to
> avoid allocating memory where the kernel dump resides. Use the
> ibm,kernel-dump node under /rtas to determine whether a kernel dump exists
> and up to which limit grub can use available memory. Set the
> upper_mem_limit to the size of the kernel dump section of type
> 'REAL_MODE_REGION' and therefore only allow grub's memory usage for high
> addresses from RMO_ADDR_MAX to 'upper_mem_limit'. This means that grub can
> use high memory in the range of RMO_ADDR_MAX (768MB) to upper_mem_limit and
> the kernel-dump memory regions above 'upper_mem_limit' remain untouched.
> This change has no effect on memory allocations below 'linux_rmo_save'
> (typically at 640MB).
>
> Also, fall back to allocating below rmo_linux_save in case the chunk of
> memory there would be larger than the chunk of memory above RMO_ADDR_MAX.
> This can for example occur if a free memory area is found starting at 300MB
> extending up to 1GB but a kernel dump is located at 768MB and therefore
> does not allow the allocation of the high memory area but requiring to use
> the chunk starting at 300MB to avoid an unnecessary out-of-memory
> condition.
>
> Signed-off-by: Stefan Berger <stef...@linux.ibm.com>
> Reviewed-by: Hari Bathini <hbath...@linux.ibm.com>
> Cc: Pavithra Prakash <pavra...@in.ibm.com>
> Cc: Michael Ellerman <m...@ellerman.id.au>
> Cc: Carolyn Scherrer <cpsch...@us.ibm.com>
> Cc: Mahesh Salgaonkar <mah...@linux.ibm.com>
> Cc: Sourabh Jain <sourabhj...@linux.ibm.com>
> ---
>  grub-core/kern/ieee1275/init.c | 145 ++++++++++++++++++++++++++++++++-
>  1 file changed, 143 insertions(+), 2 deletions(-)
>
> diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
> index bd9a4804b..ed609070a 100644
> --- a/grub-core/kern/ieee1275/init.c
> +++ b/grub-core/kern/ieee1275/init.c
> @@ -17,6 +17,8 @@
>   *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
>   */
>
> +#include <stddef.h> /* offsetof() */
> +
>  #include <grub/kernel.h>
>  #include <grub/dl.h>
>  #include <grub/disk.h>
> @@ -196,6 +198,96 @@ grub_claim_heap (void)
>  #else
>  /* Helpers for mm on powerpc. */
>
> +/* ibm,kernel-dump data structures */
> +struct kd_section
> +{
> +  grub_uint32_t flags;
> +  grub_uint16_t src_datatype;
> +#define KD_SRC_DATATYPE_REAL_MODE_REGION  0x0011
> +  grub_uint16_t error_flags;
> +  grub_uint64_t src_address;
> +  grub_uint64_t num_bytes;
> +  grub_uint64_t act_bytes;
> +  grub_uint64_t dst_address;
> +} GRUB_PACKED;
> +
> +#define MAX_KD_SECTIONS 10
> +
> +struct kernel_dump
> +{
> +  grub_uint32_t format;
> +  grub_uint16_t num_sections;
> +  grub_uint16_t status_flags;
> +  grub_uint32_t offset_1st_section;
> +  grub_uint32_t num_blocks;
> +  grub_uint64_t start_block;
> +  grub_uint64_t num_blocks_avail;
> +  grub_uint32_t offet_path_string;
> +  grub_uint32_t max_time_allowed;
> +  struct kd_section kds[MAX_KD_SECTIONS]; /* offset_1st_section should point 
> to kds[0] */
> +} GRUB_PACKED;
> +
> +/*
> + * Determine if a kernel dump exists and if it does, then determine the 
> highest
> + * address that grub can use for memory allocations.
> + * The caller must have initialized *highest to rmo_top. *highest will not
> + * be modified if no kernel dump is found.
> + */
> +static int
> +check_kernel_dump (grub_uint64_t *highest)
> +{
> +  struct kernel_dump kernel_dump;
> +  grub_ssize_t kernel_dump_size;
> +  grub_ieee1275_phandle_t rtas;
> +  struct kd_section *kds;
> +  grub_size_t i;
> +
> +  /* If there's a kernel-dump it must have at least one section */
> +  if (grub_ieee1275_finddevice ("/rtas", &rtas) ||
> +      grub_ieee1275_get_property (rtas, "ibm,kernel-dump", &kernel_dump,
> +                                  sizeof (kernel_dump), &kernel_dump_size) ||
> +      kernel_dump_size <= (grub_ssize_t) offsetof (struct kernel_dump, 
> kds[1]))
> +    return 0;
> +
> +  kernel_dump_size = grub_min (kernel_dump_size, (grub_ssize_t) sizeof 
> (kernel_dump));
> +
> +  if (grub_be_to_cpu32 (kernel_dump.format) != 1)
> +    {
> +      grub_printf (_("Error: ibm,kernel-dump has an unexpected format 
> version '%u'\n"),
> +                   grub_be_to_cpu32 (kernel_dump.format));
> +      return 0;
> +    }
> +
> +  if (grub_be_to_cpu16 (kernel_dump.num_sections) > MAX_KD_SECTIONS)
> +    {
> +      grub_printf (_("Error: Too many kernel dump sections: %d\n"),
> +                   grub_be_to_cpu32 (kernel_dump.num_sections));
> +      return 0;
> +    }
> +
> +  for (i = 0; i < grub_be_to_cpu16 (kernel_dump.num_sections); i++)
> +    {
> +      kds = (struct kd_section *) ((grub_addr_t) &kernel_dump +
> +                                   grub_be_to_cpu32 
> (kernel_dump.offset_1st_section) +
> +                                   i * sizeof (struct kd_section));
> +      /* sanity check the address is within the 'kernel_dump' struct */
> +      if ((grub_addr_t) kds > (grub_addr_t) &kernel_dump + kernel_dump_size 
> + sizeof (*kds))
> +        {
> +          grub_printf (_("Error: 'kds' address beyond last available 
> section\n"));
> +          return 0;
> +        }
> +
> +      if ((grub_be_to_cpu16 (kds->src_datatype) == 
> KD_SRC_DATATYPE_REAL_MODE_REGION) &&
> +          (grub_be_to_cpu64 (kds->src_address) == 0))
> +        {
> +          *highest = grub_min (*highest, grub_be_to_cpu64 (kds->num_bytes));
> +          break;
> +        }
> +    }
> +
> +  return 1;
> +}
> +
>  /*
>   * How much memory does OF believe exists in total?
>   *
> @@ -275,10 +367,31 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, 
> grub_memory_type_t type,
>     *
>     * Finally, we also want to make sure that when grub loads the kernel,
>     * it isn't going to use up all the memory we're trying to reserve! So
> -   * enforce our entire RUNTIME_MIN_SPACE here:
> +   * enforce our entire RUNTIME_MIN_SPACE here (no fadump):
> +   *
> +   * | Top of memory == upper_mem_limit -|
> +   * |                                   |
> +   * |             available             |
> +   * |                                   |
> +   * |----------     768 MB    ----------|
> +   * |                                   |
> +   * |              reserved             |
> +   * |                                   |
> +   * |--- 768 MB - runtime min space  ---|
> +   * |                                   |
> +   * |             available             |
> +   * |                                   |
> +   * |----------      0 MB     ----------|
> +   *
> +   * In case fadump is used, we allow the following:
>     *
>     * |---------- Top of memory ----------|
>     * |                                   |
> +   * |             unavailable           |
> +   * |         (kernel dump area)        |
> +   * |                                   |
> +   * |--------- upper_mem_limit ---------|
> +   * |                                   |
>     * |             available             |
>     * |                                   |
>     * |----------     768 MB    ----------|
> @@ -333,17 +446,45 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, 
> grub_memory_type_t type,
>          }
>        else
>          {
> +          grub_uint64_t upper_mem_limit = rmo_top;
> +          grub_uint64_t orig_addr = addr;
> +          int has_kernel_dump;
> +
> +          has_kernel_dump = check_kernel_dump (&upper_mem_limit);

Compiler complains... :-(

kern/ieee1275/init.c: In function ‘regions_claim’:
kern/ieee1275/init.c:451:15: error: variable ‘has_kernel_dump’ set but not used 
[-Werror=unused-but-set-variable]
           int has_kernel_dump;
               ^~~~~~~~~~~~~~~

If you do not use check_kernel_dump() return value than maybe you
should return void. Should not you?

Daniel

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to