On 15/01/2021 09:00, Nathan Lynch wrote:
Memory locations passed as arguments from the OS to RTAS usually need
to be addressable in 32-bit mode and must reside in the Real Mode
Area. On PAPR guests, the RMA starts at logical address 0 and is the
first logical memory block reported in the LPAR’s device tree.

On powerpc targets with RTAS, Linux makes available to user space a
region of memory suitable for arguments to be passed to RTAS via
sys_rtas(). This region (rtas_rmo_buf) is allocated via the memblock
API during boot in order to ensure that it satisfies the requirements
described above.

With radix MMU, the upper limit supplied to the memblock allocation
can exceed the bounds of the first logical memory block, since
ppc64_rma_size is ULONG_MAX and RTAS_INSTANTIATE_MAX is 1GB. (512MB is
a common size of the first memory block according to a small sample of
LPARs I have checked.) This leads to failures when user space invokes
an RTAS function that uses a work area, such as
ibm,configure-connector.

Alter the determination of the upper limit for rtas_rmo_buf's
allocation to consult the device tree directly, ensuring placement
within the RMA regardless of the MMU in use.

Can we tie this with RTAS (which also needs to be in RMA) and simply add extra 64K in prom_instantiate_rtas() and advertise this address (ALIGH_UP(rtas-base + rtas-size, PAGE_SIZE)) to the user space? We do not need this RMO area before that point.

And probably do the same with per-cpu RTAS argument structures mentioned in the cover letter?




Signed-off-by: Nathan Lynch <nath...@linux.ibm.com>
---
  arch/powerpc/kernel/rtas.c | 80 +++++++++++++++++++++++++++++++-------
  1 file changed, 65 insertions(+), 15 deletions(-)

diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index da65faadbbb2..98dfb112f4df 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -1166,6 +1166,70 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
        return 0;
  }
+/*
+ * Memory locations passed to RTAS must be in the RMA as described by
+ * the range in /memory@0.
+ */
+static phys_addr_t rtas_arg_addr_limit(void)
+{
+       unsigned int addr_cells;
+       unsigned int size_cells;
+       struct device_node *np;
+       const __be32 *prop;
+       u64 limit;
+       u64 base;
+
+       /* RTAS is instantiated in 32-bit mode. */
+       limit = 1ULL << 32;
+
+       /* Account for mem=. */
+       if (memory_limit != 0)
+               limit = min(limit, memory_limit);
+
+       np = of_find_node_by_path("/memory@0");
+       if (!np)
+               goto out;
+
+       prop = of_get_property(np, "reg", NULL);
+       if (!prop)
+               goto put;
+
+       addr_cells = of_n_addr_cells(np);
+       base = of_read_number(prop, addr_cells);
+       prop += addr_cells;
+       size_cells = of_n_size_cells(np);
+       limit = min(limit, of_read_number(prop, size_cells));
+put:
+       of_node_put(np);
+out:
+       pr_debug("%s: base = %#llx limit = %#llx", __func__, base, limit);
+
+       return limit;
+}
+
+static void __init rtas_user_region_setup(void)
+{
+       phys_addr_t limit, align, size;
+
+       limit = rtas_arg_addr_limit();
+       size = RTAS_USER_REGION_SIZE;
+
+       /*
+        * Although work areas need only 4KB alignment, user space
+        * accesses this region via mmap so it must be placed on a
+        * page boundary.
+        */
+       align = PAGE_SIZE;
+
+       rtas_rmo_buf = memblock_phys_alloc_range(size, align, 0, limit);
+       if (rtas_rmo_buf == 0) {
+               panic("Failed to allocate %llu bytes for user region below 
%pa\n",
+                     size, &limit);
+       }
+
+       pr_debug("RTAS user region allocated at %pa\n", &rtas_rmo_buf);
+}
+
  /*
   * Call early during boot, before mem init, to retrieve the RTAS
   * information from the device-tree and allocate the RMO buffer for userland
@@ -1173,7 +1237,6 @@ SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
   */
  void __init rtas_initialize(void)
  {
-       unsigned long rtas_region = RTAS_INSTANTIATE_MAX;
        u32 base, size, entry;
        int no_base, no_size, no_entry;
@@ -1197,23 +1260,10 @@ void __init rtas_initialize(void)
        no_entry = of_property_read_u32(rtas.dev, "linux,rtas-entry", &entry);
        rtas.entry = no_entry ? rtas.base : entry;
- /* If RTAS was found, allocate the RMO buffer for it and look for
-        * the stop-self token if any
-        */
-#ifdef CONFIG_PPC64
-       if (firmware_has_feature(FW_FEATURE_LPAR))
-               rtas_region = min(ppc64_rma_size, RTAS_INSTANTIATE_MAX);
-#endif
-       rtas_rmo_buf = memblock_phys_alloc_range(RTAS_USER_REGION_SIZE, 
PAGE_SIZE,
-                                                0, rtas_region);
-       if (!rtas_rmo_buf)
-               panic("ERROR: RTAS: Failed to allocate %lx bytes below %pa\n",
-                     PAGE_SIZE, &rtas_region);
-
  #ifdef CONFIG_RTAS_ERROR_LOGGING
        rtas_last_error_token = rtas_token("rtas-last-error");
  #endif
-
+       rtas_user_region_setup();
        rtas_syscall_filter_init();
  }

--
Alexey

Reply via email to