Blame SOURCES/0483-ieee1275-request-memory-with-ibm-client-architecture.patch

4e7deb
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
4e7deb
From: Daniel Axtens <dja@axtens.net>
4e7deb
Date: Fri, 16 Apr 2021 11:48:46 +1000
4e7deb
Subject: [PATCH] ieee1275: request memory with ibm,client-architecture-support
4e7deb
4e7deb
On PowerVM, the first time we boot a Linux partition, we may only get
4e7deb
256MB of real memory area, even if the partition has more memory.
4e7deb
4e7deb
This isn't really enough. Fortunately, the Power Architecture Platform
4e7deb
Reference (PAPR) defines a method we can call to ask for more memory.
4e7deb
This is part of the broad and powerful ibm,client-architecture-support
4e7deb
(CAS) method.
4e7deb
4e7deb
CAS can do an enormous amount of things on a PAPR platform: as well as
4e7deb
asking for memory, you can set the supported processor level, the interrupt
4e7deb
controller, hash vs radix mmu, and so on. We want to touch as little of
4e7deb
this as possible because we don't want to step on the toes of the future OS.
4e7deb
4e7deb
If:
4e7deb
4e7deb
 - we are running under what we think is PowerVM (compatible property of /
4e7deb
   begins with "IBM"), and
4e7deb
4e7deb
 - the full amount of RMA is less than 512MB (as determined by the reg
4e7deb
   property of /memory)
4e7deb
4e7deb
then call CAS as follows: (refer to the Linux on Power Architecture
4e7deb
Reference, LoPAR, which is public, at B.5.2.3):
4e7deb
4e7deb
 - Use the "any" PVR value and supply 2 option vectors.
4e7deb
4e7deb
 - Set option vector 1 (PowerPC Server Processor Architecture Level)
4e7deb
   to "ignore".
4e7deb
4e7deb
 - Set option vector 2 with default or Linux-like options, including a
4e7deb
   min-rma-size of 512MB.
4e7deb
4e7deb
This will cause a CAS reboot and the partition will restart with 512MB
4e7deb
of RMA. Grub will notice the 512MB and not call CAS again.
4e7deb
4e7deb
(A partition can be configured with only 256MB of memory, which would
4e7deb
mean this request couldn't be satisfied, but PFW refuses to load with
4e7deb
only 256MB of memory, so it's a bit moot. SLOF will run fine with 256MB,
4e7deb
but we will never call CAS under qemu/SLOF because /compatible won't
4e7deb
begin with "IBM".)
4e7deb
4e7deb
One of the first things Linux does while still running under OpenFirmware
4e7deb
is to call CAS with a much fuller set of options (including asking for
4e7deb
512MB of memory). This includes a much more restrictive set of PVR values
4e7deb
and processor support levels, and this will induce another reboot. On this
4e7deb
reboot grub will again notice the higher RMA, and not call CAS. We will get
4e7deb
to Linux, Linux will call CAS but because the values are now set for Linux
4e7deb
this will not induce another CAS reboot and we will finally boot.
4e7deb
4e7deb
On all subsequent boots, everything will be configured with 512MB of RMA
4e7deb
and all the settings Linux likes, so there will be no further CAS reboots.
4e7deb
4e7deb
(phyp is super sticky with the RMA size - it persists even on cold boots.
4e7deb
So if you've ever booted Linux in a partition, you'll probably never have
4e7deb
grub call CAS. It'll only ever fire the first time a partition loads grub,
4e7deb
or if you deliberately lower the amount of memory your partition has below
4e7deb
512MB.)
4e7deb
4e7deb
Signed-off-by: Daniel Axtens <dja@axtens.net>
4e7deb
---
4e7deb
 grub-core/kern/ieee1275/cmain.c  |   3 +
4e7deb
 grub-core/kern/ieee1275/init.c   | 144 ++++++++++++++++++++++++++++++++++++++-
4e7deb
 include/grub/ieee1275/ieee1275.h |   8 ++-
4e7deb
 3 files changed, 152 insertions(+), 3 deletions(-)
4e7deb
4e7deb
diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c
4e7deb
index 3e14f539368..9d5156462c1 100644
4e7deb
--- a/grub-core/kern/ieee1275/cmain.c
4e7deb
+++ b/grub-core/kern/ieee1275/cmain.c
4e7deb
@@ -124,6 +124,9 @@ grub_ieee1275_find_options (void)
4e7deb
 	      break;
4e7deb
 	    }
4e7deb
 	}
4e7deb
+
4e7deb
+      if (grub_strncmp (tmp, "IBM,", 4) == 0)
4e7deb
+	grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY);
4e7deb
     }
4e7deb
 
4e7deb
   if (is_smartfirmware)
4e7deb
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
4e7deb
index a6e169bd003..adf4bd5a88b 100644
4e7deb
--- a/grub-core/kern/ieee1275/init.c
4e7deb
+++ b/grub-core/kern/ieee1275/init.c
4e7deb
@@ -238,6 +238,135 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
4e7deb
   return 0;
4e7deb
 }
4e7deb
 
4e7deb
+/* How much memory does OF believe it has? (regardless of whether
4e7deb
+   it's accessible or not) */
4e7deb
+static grub_err_t
4e7deb
+grub_ieee1275_total_mem (grub_uint64_t *total)
4e7deb
+{
4e7deb
+  grub_ieee1275_phandle_t root;
4e7deb
+  grub_ieee1275_phandle_t memory;
4e7deb
+  grub_uint32_t reg[4];
4e7deb
+  grub_ssize_t reg_size;
4e7deb
+  grub_uint32_t address_cells = 1;
4e7deb
+  grub_uint32_t size_cells = 1;
4e7deb
+  grub_uint64_t size;
4e7deb
+
4e7deb
+  /* If we fail to get to the end, report 0. */
4e7deb
+  *total = 0;
4e7deb
+
4e7deb
+  /* Determine the format of each entry in `reg'.  */
4e7deb
+  grub_ieee1275_finddevice ("/", &root);
4e7deb
+  grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells,
4e7deb
+				      sizeof address_cells, 0);
4e7deb
+  grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells,
4e7deb
+				      sizeof size_cells, 0);
4e7deb
+
4e7deb
+  if (size_cells > address_cells)
4e7deb
+    address_cells = size_cells;
4e7deb
+
4e7deb
+  /* Load `/memory/reg'.  */
4e7deb
+  if (grub_ieee1275_finddevice ("/memory", &memory))
4e7deb
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
4e7deb
+		       "couldn't find /memory node");
4e7deb
+  if (grub_ieee1275_get_integer_property (memory, "reg", reg,
4e7deb
+					  sizeof reg, &reg_size))
4e7deb
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
4e7deb
+		       "couldn't examine /memory/reg property");
4e7deb
+  if (reg_size < 0 || (grub_size_t) reg_size > sizeof (reg))
4e7deb
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
4e7deb
+                       "/memory response buffer exceeded");
4e7deb
+
4e7deb
+  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS))
4e7deb
+    {
4e7deb
+      address_cells = 1;
4e7deb
+      size_cells = 1;
4e7deb
+    }
4e7deb
+
4e7deb
+  /* Decode only the size */
4e7deb
+  size = reg[address_cells];
4e7deb
+  if (size_cells == 2)
4e7deb
+    size = (size << 32) | reg[address_cells + 1];
4e7deb
+
4e7deb
+  *total = size;
4e7deb
+
4e7deb
+  return grub_errno;
4e7deb
+}
4e7deb
+
4e7deb
+/* Based on linux - arch/powerpc/kernel/prom_init.c */
4e7deb
+struct option_vector2 {
4e7deb
+	grub_uint8_t byte1;
4e7deb
+	grub_uint16_t reserved;
4e7deb
+	grub_uint32_t real_base;
4e7deb
+	grub_uint32_t real_size;
4e7deb
+	grub_uint32_t virt_base;
4e7deb
+	grub_uint32_t virt_size;
4e7deb
+	grub_uint32_t load_base;
4e7deb
+	grub_uint32_t min_rma;
4e7deb
+	grub_uint32_t min_load;
4e7deb
+	grub_uint8_t min_rma_percent;
4e7deb
+	grub_uint8_t max_pft_size;
4e7deb
+} __attribute__((packed));
4e7deb
+
4e7deb
+struct pvr_entry {
4e7deb
+	  grub_uint32_t mask;
4e7deb
+	  grub_uint32_t entry;
4e7deb
+};
4e7deb
+
4e7deb
+struct cas_vector {
4e7deb
+    struct {
4e7deb
+      struct pvr_entry terminal;
4e7deb
+    } pvr_list;
4e7deb
+    grub_uint8_t num_vecs;
4e7deb
+    grub_uint8_t vec1_size;
4e7deb
+    grub_uint8_t vec1;
4e7deb
+    grub_uint8_t vec2_size;
4e7deb
+    struct option_vector2 vec2;
4e7deb
+} __attribute__((packed));
4e7deb
+
4e7deb
+/* Call ibm,client-architecture-support to try to get more RMA.
4e7deb
+   We ask for 512MB which should be enough to verify a distro kernel.
4e7deb
+   We ignore most errors: if we don't succeed we'll proceed with whatever
4e7deb
+   memory we have. */
4e7deb
+static void
4e7deb
+grub_ieee1275_ibm_cas (void)
4e7deb
+{
4e7deb
+  int rc;
4e7deb
+  grub_ieee1275_ihandle_t root;
4e7deb
+  struct cas_args {
4e7deb
+    struct grub_ieee1275_common_hdr common;
4e7deb
+    grub_ieee1275_cell_t method;
4e7deb
+    grub_ieee1275_ihandle_t ihandle;
4e7deb
+    grub_ieee1275_cell_t cas_addr;
4e7deb
+    grub_ieee1275_cell_t result;
4e7deb
+  } args;
4e7deb
+  struct cas_vector vector = {
4e7deb
+    .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */
4e7deb
+    .num_vecs = 2 - 1,
4e7deb
+    .vec1_size = 0,
4e7deb
+    .vec1 = 0x80, /* ignore */
4e7deb
+    .vec2_size = 1 + sizeof(struct option_vector2) - 2,
4e7deb
+    .vec2 = {
4e7deb
+      0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48
4e7deb
+    },
4e7deb
+  };
4e7deb
+
4e7deb
+  INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2);
4e7deb
+  args.method = (grub_ieee1275_cell_t)"ibm,client-architecture-support";
4e7deb
+  rc = grub_ieee1275_open("/", &root);
4e7deb
+  if (rc) {
4e7deb
+	  grub_error (GRUB_ERR_IO, "could not open root when trying to call CAS");
4e7deb
+	  return;
4e7deb
+  }
4e7deb
+  args.ihandle = root;
4e7deb
+  args.cas_addr = (grub_ieee1275_cell_t)&vector;
4e7deb
+
4e7deb
+  grub_printf("Calling ibm,client-architecture-support...");
4e7deb
+  IEEE1275_CALL_ENTRY_FN (&args);
4e7deb
+  grub_printf("done\n");
4e7deb
+
4e7deb
+  grub_ieee1275_close(root);
4e7deb
+}
4e7deb
+
4e7deb
 static void 
4e7deb
 grub_claim_heap (void)
4e7deb
 {
4e7deb
@@ -245,11 +374,22 @@ grub_claim_heap (void)
4e7deb
 
4e7deb
   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
4e7deb
     {
4e7deb
-      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
4e7deb
-		 1, &total);
4e7deb
+      heap_init (GRUB_IEEE1275_STATIC_HEAP_START,
4e7deb
+		 GRUB_IEEE1275_STATIC_HEAP_LEN, 1, &total);
4e7deb
       return;
4e7deb
     }
4e7deb
 
4e7deb
+  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY))
4e7deb
+    {
4e7deb
+      grub_uint64_t rma_size;
4e7deb
+      grub_err_t err;
4e7deb
+
4e7deb
+      err = grub_ieee1275_total_mem (&rma_size);
4e7deb
+      /* if we have an error, don't call CAS, just hope for the best */
4e7deb
+      if (!err && rma_size < (512 * 1024 * 1024))
4e7deb
+	grub_ieee1275_ibm_cas();
4e7deb
+    }
4e7deb
+
4e7deb
   grub_machine_mmap_iterate (heap_size, &total);
4e7deb
 
4e7deb
   total = total / 4;
4e7deb
diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
4e7deb
index ca08bd96681..131808d6197 100644
4e7deb
--- a/include/grub/ieee1275/ieee1275.h
4e7deb
+++ b/include/grub/ieee1275/ieee1275.h
4e7deb
@@ -147,7 +147,13 @@ enum grub_ieee1275_flag
4e7deb
 
4e7deb
   GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN,
4e7deb
 
4e7deb
-  GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT
4e7deb
+  GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT,
4e7deb
+  
4e7deb
+  /* On PFW, the first time we boot a Linux partition, we may only get 256MB
4e7deb
+     of real memory area, even if the partition has more memory. Set this flag
4e7deb
+     if we think we're running under PFW. Then, if this flag is set, and the
4e7deb
+     RMA is only 256MB in size, try asking for more with CAS. */
4e7deb
+  GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY,
4e7deb
 };
4e7deb
 
4e7deb
 extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag);