[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