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

Alexey Kardashevskiy aik at ozlabs.ru
Fri Jan 15 15:38:07 AEDT 2021



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 <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();
>   }
>   
> 

-- 
Alexey


More information about the Linuxppc-dev mailing list