[PATCH 6/6] powerpc/rtas: constrain user region allocation to RMA

Nathan Lynch nathanl at linux.ibm.com
Fri Jan 15 09:00:04 AEDT 2021


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.

Signed-off-by: Nathan Lynch <nathanl at 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 at 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 at 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();
 }
 
-- 
2.29.2



More information about the Linuxppc-dev mailing list