daandemeyer / rpms / grub2

Forked from rpms/grub2 5 months ago
Clone
7be2bf0
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
7be2bf0
From: Daniel Axtens <dja@axtens.net>
7be2bf0
Date: Thu, 21 Apr 2022 15:24:15 +1000
7be2bf0
Subject: [PATCH] mm: When adding a region, merge with region after as well as
7be2bf0
 before
7be2bf0
7be2bf0
On x86_64-efi (at least) regions seem to be added from top down. The mm
7be2bf0
code will merge a new region with an existing region that comes
7be2bf0
immediately before the new region. This allows larger allocations to be
7be2bf0
satisfied that would otherwise be the case.
7be2bf0
7be2bf0
On powerpc-ieee1275, however, regions are added from bottom up. So if
7be2bf0
we add 3x 32MB regions, we can still only satisfy a 32MB allocation,
7be2bf0
rather than the 96MB allocation we might otherwise be able to satisfy.
7be2bf0
7be2bf0
  * Define 'post_size' as being bytes lost to the end of an allocation
7be2bf0
    due to being given weird sizes from firmware that are not multiples
7be2bf0
    of GRUB_MM_ALIGN.
7be2bf0
7be2bf0
  * Allow merging of regions immediately _after_ existing regions, not
7be2bf0
    just before. As with the other approach, we create an allocated
7be2bf0
    block to represent the new space and the pass it to grub_free() to
7be2bf0
    get the metadata right.
7be2bf0
7be2bf0
Signed-off-by: Daniel Axtens <dja@axtens.net>
7be2bf0
Tested-by: Stefan Berger <stefanb@linux.ibm.com>
7be2bf0
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
7be2bf0
Tested-by: Patrick Steinhardt <ps@pks.im>
7be2bf0
(cherry picked from commit 052e6068be622ff53f1238b449c300dbd0a8abcd)
7be2bf0
---
7be2bf0
 grub-core/kern/mm.c       | 128 +++++++++++++++++++++++++++++-----------------
7be2bf0
 include/grub/mm_private.h |   9 ++++
7be2bf0
 2 files changed, 91 insertions(+), 46 deletions(-)
7be2bf0
7be2bf0
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
7be2bf0
index 1cbf98c7ab..7be33e23bf 100644
7be2bf0
--- a/grub-core/kern/mm.c
7be2bf0
+++ b/grub-core/kern/mm.c
7be2bf0
@@ -130,53 +130,88 @@ grub_mm_init_region (void *addr, grub_size_t size)
7be2bf0
 
7be2bf0
   /* Attempt to merge this region with every existing region */
7be2bf0
   for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p)
7be2bf0
-    /*
7be2bf0
-     * Is the new region immediately below an existing region? That
7be2bf0
-     * is, is the address of the memory we're adding now (addr) + size
7be2bf0
-     * of the memory we're adding (size) + the bytes we couldn't use
7be2bf0
-     * at the start of the region we're considering (q->pre_size)
7be2bf0
-     * equal to the address of q? In other words, does the memory
7be2bf0
-     * looks like this?
7be2bf0
-     *
7be2bf0
-     * addr                          q
7be2bf0
-     *   |----size-----|-q->pre_size-|<q region>|
7be2bf0
-     */
7be2bf0
-    if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
7be2bf0
-      {
7be2bf0
-	/*
7be2bf0
-	 * Yes, we can merge the memory starting at addr into the
7be2bf0
-	 * existing region from below. Align up addr to GRUB_MM_ALIGN
7be2bf0
-	 * so that our new region has proper alignment.
7be2bf0
-	 */
7be2bf0
-	r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
7be2bf0
-	/* Copy the region data across */
7be2bf0
-	*r = *q;
7be2bf0
-	/* Consider all the new size as pre-size */
7be2bf0
-	r->pre_size += size;
7be2bf0
+    {
7be2bf0
+      /*
7be2bf0
+       * Is the new region immediately below an existing region? That
7be2bf0
+       * is, is the address of the memory we're adding now (addr) + size
7be2bf0
+       * of the memory we're adding (size) + the bytes we couldn't use
7be2bf0
+       * at the start of the region we're considering (q->pre_size)
7be2bf0
+       * equal to the address of q? In other words, does the memory
7be2bf0
+       * looks like this?
7be2bf0
+       *
7be2bf0
+       * addr                          q
7be2bf0
+       *   |----size-----|-q->pre_size-|<q region>|
7be2bf0
+       */
7be2bf0
+      if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
7be2bf0
+        {
7be2bf0
+          /*
7be2bf0
+           * Yes, we can merge the memory starting at addr into the
7be2bf0
+           * existing region from below. Align up addr to GRUB_MM_ALIGN
7be2bf0
+           * so that our new region has proper alignment.
7be2bf0
+           */
7be2bf0
+          r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
7be2bf0
+          /* Copy the region data across */
7be2bf0
+          *r = *q;
7be2bf0
+          /* Consider all the new size as pre-size */
7be2bf0
+          r->pre_size += size;
7be2bf0
 
7be2bf0
-	/*
7be2bf0
-	 * If we have enough pre-size to create a block, create a
7be2bf0
-	 * block with it. Mark it as allocated and pass it to
7be2bf0
-	 * grub_free (), which will sort out getting it into the free
7be2bf0
-	 * list.
7be2bf0
-	 */
7be2bf0
-	if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
7be2bf0
-	  {
7be2bf0
-	    h = (grub_mm_header_t) (r + 1);
7be2bf0
-	    /* block size is pre-size converted to cells */
7be2bf0
-	    h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
7be2bf0
-	    h->magic = GRUB_MM_ALLOC_MAGIC;
7be2bf0
-	    /* region size grows by block size converted back to bytes */
7be2bf0
-	    r->size += h->size << GRUB_MM_ALIGN_LOG2;
7be2bf0
-	    /* adjust pre_size to be accurate */
7be2bf0
-	    r->pre_size &= (GRUB_MM_ALIGN - 1);
7be2bf0
-	    *p = r;
7be2bf0
-	    grub_free (h + 1);
7be2bf0
-	  }
7be2bf0
-	/* Replace the old region with the new region */
7be2bf0
-	*p = r;
7be2bf0
-	return;
7be2bf0
-      }
7be2bf0
+          /*
7be2bf0
+           * If we have enough pre-size to create a block, create a
7be2bf0
+           * block with it. Mark it as allocated and pass it to
7be2bf0
+           * grub_free (), which will sort out getting it into the free
7be2bf0
+           * list.
7be2bf0
+           */
7be2bf0
+          if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
7be2bf0
+            {
7be2bf0
+              h = (grub_mm_header_t) (r + 1);
7be2bf0
+              /* block size is pre-size converted to cells */
7be2bf0
+              h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
7be2bf0
+              h->magic = GRUB_MM_ALLOC_MAGIC;
7be2bf0
+              /* region size grows by block size converted back to bytes */
7be2bf0
+              r->size += h->size << GRUB_MM_ALIGN_LOG2;
7be2bf0
+              /* adjust pre_size to be accurate */
7be2bf0
+              r->pre_size &= (GRUB_MM_ALIGN - 1);
7be2bf0
+              *p = r;
7be2bf0
+              grub_free (h + 1);
7be2bf0
+            }
7be2bf0
+          /* Replace the old region with the new region */
7be2bf0
+          *p = r;
7be2bf0
+          return;
7be2bf0
+        }
7be2bf0
+
7be2bf0
+      /*
7be2bf0
+       * Is the new region immediately above an existing region? That
7be2bf0
+       * is:
7be2bf0
+       *   q                       addr
7be2bf0
+       *   |<q region>|-q->post_size-|----size-----|
7be2bf0
+       */
7be2bf0
+      if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size ==
7be2bf0
+	  (grub_uint8_t *) addr)
7be2bf0
+	{
7be2bf0
+	  /*
7be2bf0
+	   * Yes! Follow a similar pattern to above, but simpler.
7be2bf0
+	   * Our header starts at address - post_size, which should align us
7be2bf0
+	   * to a cell boundary.
7be2bf0
+	   *
7be2bf0
+	   * Cast to (void *) first to avoid the following build error:
7be2bf0
+	   *   kern/mm.c: In function ‘grub_mm_init_region’:
7be2bf0
+	   *   kern/mm.c:211:15: error: cast increases required alignment of target type [-Werror=cast-align]
7be2bf0
+	   *     211 |           h = (grub_mm_header_t) ((grub_uint8_t *) addr - q->post_size);
7be2bf0
+	   *         |               ^
7be2bf0
+	   * It is safe to do that because proper alignment is enforced in grub_mm_size_sanity_check().
7be2bf0
+	   */
7be2bf0
+	  h = (grub_mm_header_t)(void *) ((grub_uint8_t *) addr - q->post_size);
7be2bf0
+	  /* our size is the allocated size plus post_size, in cells */
7be2bf0
+	  h->size = (size + q->post_size) >> GRUB_MM_ALIGN_LOG2;
7be2bf0
+	  h->magic = GRUB_MM_ALLOC_MAGIC;
7be2bf0
+	  /* region size grows by block size converted back to bytes */
7be2bf0
+	  q->size += h->size << GRUB_MM_ALIGN_LOG2;
7be2bf0
+	  /* adjust new post_size to be accurate */
7be2bf0
+	  q->post_size = (q->post_size + size) & (GRUB_MM_ALIGN - 1);
7be2bf0
+	  grub_free (h + 1);
7be2bf0
+	  return;
7be2bf0
+	}
7be2bf0
+    }
7be2bf0
 
7be2bf0
   /* Allocate a region from the head.  */
7be2bf0
   r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
7be2bf0
@@ -195,6 +230,7 @@ grub_mm_init_region (void *addr, grub_size_t size)
7be2bf0
   r->first = h;
7be2bf0
   r->pre_size = (grub_addr_t) r - (grub_addr_t) addr;
7be2bf0
   r->size = (h->size << GRUB_MM_ALIGN_LOG2);
7be2bf0
+  r->post_size = size - r->size;
7be2bf0
 
7be2bf0
   /* Find where to insert this region. Put a smaller one before bigger ones,
7be2bf0
      to prevent fragmentation.  */
7be2bf0
diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
7be2bf0
index a688b92a83..96c2d816be 100644
7be2bf0
--- a/include/grub/mm_private.h
7be2bf0
+++ b/include/grub/mm_private.h
7be2bf0
@@ -81,8 +81,17 @@ typedef struct grub_mm_region
7be2bf0
    */
7be2bf0
   grub_size_t pre_size;
7be2bf0
 
7be2bf0
+  /*
7be2bf0
+   * Likewise, the post-size is the number of bytes we wasted at the end
7be2bf0
+   * of the allocation because it wasn't a multiple of GRUB_MM_ALIGN
7be2bf0
+   */
7be2bf0
+  grub_size_t post_size;
7be2bf0
+
7be2bf0
   /* How many bytes are in this region? (free and allocated) */
7be2bf0
   grub_size_t size;
7be2bf0
+
7be2bf0
+  /* pad to a multiple of cell size */
7be2bf0
+  char padding[3 * GRUB_CPU_SIZEOF_VOID_P];
7be2bf0
 }
7be2bf0
 *grub_mm_region_t;
7be2bf0