[PATCH] powerpc/64: allow rtas to be called in real-mode, use this in machine check

Ganesh ganeshgr at linux.ibm.com
Mon Mar 23 21:19:55 AEDT 2020



On 3/20/20 8:58 PM, Nicholas Piggin wrote:
> rtas_call allocates and uses memory in failure paths, which is
> not safe for RMA. It also calls local_irq_save() which may not be safe
> in all real mode contexts.
>
> Particularly machine check may run with interrupts not "reconciled",
> and it may have hit while it was in tracing code that should not be
> rentered.
>
> Create minimal rtas call that should be usable by guest machine check
> code, use it there to call "ibm,nmi-interlock".
>
> Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
> ---
>   arch/powerpc/include/asm/rtas.h      |  1 +
>   arch/powerpc/kernel/entry_64.S       | 12 ++++++--
>   arch/powerpc/kernel/rtas.c           | 43 ++++++++++++++++++++++++++++
>   arch/powerpc/platforms/pseries/ras.c |  2 +-
>   4 files changed, 54 insertions(+), 4 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
> index 3c1887351c71..4ffc499ce1ac 100644
> --- a/arch/powerpc/include/asm/rtas.h
> +++ b/arch/powerpc/include/asm/rtas.h
> @@ -352,6 +352,7 @@ extern struct rtas_t rtas;
>   extern int rtas_token(const char *service);
>   extern int rtas_service_present(const char *service);
>   extern int rtas_call(int token, int, int, int *, ...);
> +extern int raw_rtas_call(int token, int, int, int *, ...);
>   void rtas_call_unlocked(struct rtas_args *args, int token, int nargs,
>   			int nret, ...);
>   extern void __noreturn rtas_restart(char *cmd);
> diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
> index 51c5b681f70c..309abb677788 100644
> --- a/arch/powerpc/kernel/entry_64.S
> +++ b/arch/powerpc/kernel/entry_64.S
> @@ -759,6 +759,13 @@ _GLOBAL(enter_rtas)
>   	li	r0,0
>   	mtcr	r0
>   
> +	/* enter_rtas called from real-mode may not have irqs reconciled
> +	 * but will always have interrupts disabled.
> +	 */
> +	mfmsr	r6
> +	andi.	r7,r6,(MSR_IR|MSR_DR)
> +	beq	2f
> +
>   #ifdef CONFIG_BUG
>   	/* There is no way it is acceptable to get here with interrupts enabled,
>   	 * check it with the asm equivalent of WARN_ON
> @@ -769,10 +776,10 @@ _GLOBAL(enter_rtas)
>   #endif
>   
>   	/* Hard-disable interrupts */
> -	mfmsr	r6
>   	rldicl	r7,r6,48,1
>   	rotldi	r7,r7,16
>   	mtmsrd	r7,1
> +2:
>   
>   	/* Unfortunately, the stack pointer and the MSR are also clobbered,
>   	 * so they are saved in the PACA which allows us to restore
> @@ -795,7 +802,6 @@ _GLOBAL(enter_rtas)
>   	ori	r9,r9,MSR_IR|MSR_DR|MSR_FE0|MSR_FE1|MSR_FP|MSR_RI|MSR_LE
>   	andc	r6,r0,r9
>   
> -__enter_rtas:
>   	sync				/* disable interrupts so SRR0/1 */
>   	mtmsrd	r0			/* don't get trashed */
>   
> @@ -837,7 +843,7 @@ rtas_return_loc:
>   	mtspr	SPRN_SRR1,r4
>   	RFI_TO_KERNEL
>   	b	.	/* prevent speculative execution */
> -_ASM_NOKPROBE_SYMBOL(__enter_rtas)
> +_ASM_NOKPROBE_SYMBOL(enter_rtas)
>   _ASM_NOKPROBE_SYMBOL(rtas_return_loc)
>   
>   	.align	3
> diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
> index c5fa251b8950..a058dcfb6726 100644
> --- a/arch/powerpc/kernel/rtas.c
> +++ b/arch/powerpc/kernel/rtas.c
> @@ -450,6 +450,8 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
>   	char *buff_copy = NULL;
>   	int ret;
>   
> +	WARN_ON_ONCE((mfmsr() & (MSR_IR|MSR_DR)) != (MSR_IR|MSR_DR));
> +
>   	if (!rtas.entry || token == RTAS_UNKNOWN_SERVICE)
>   		return -1;
>   
> @@ -483,6 +485,47 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
>   }
>   EXPORT_SYMBOL(rtas_call);
>   
> +/*
> + * Like rtas_call but no kmalloc or printk etc in error handling, so
> + * error won't go through log_error. No tracing, may be called in real mode.
> + */
> +int notrace raw_rtas_call(int token, int nargs, int nret, int *outputs, ...)
> +{
> +	va_list list;
> +	int i;
> +	struct rtas_args *rtas_args;
> +	int ret;
> +
> +	WARN_ON_ONCE((mfmsr() & MSR_EE));
> +
> +	if (!rtas.entry || token == RTAS_UNKNOWN_SERVICE)
> +		return -1;
> +
> +	/*
> +	 * Real mode must have MSR[EE]=0 and we prefer not to touch any
> +	 * irq or preempt state (this may be called in machine check).
> +	 */
> +	preempt_disable_notrace();
> +	arch_spin_lock(&rtas.lock);

I wonder, if its ok to attempt for this lock in nested MCE.

> +
> +	/* We use the global rtas args buffer */
> +	rtas_args = &rtas.args;
> +
> +	va_start(list, outputs);
> +	va_rtas_call_unlocked(rtas_args, token, nargs, nret, list);
> +	va_end(list);
> +
> +	if (nret > 1 && outputs != NULL)
> +		for (i = 0; i < nret-1; ++i)
> +			outputs[i] = be32_to_cpu(rtas_args->rets[i+1]);
> +	ret = (nret > 0)? be32_to_cpu(rtas_args->rets[0]): 0;
> +
> +	arch_spin_unlock(&rtas.lock);
> +	preempt_enable_notrace();
> +
> +	return ret;
> +}
> +
>   /* For RTAS_BUSY (-2), delay for 1 millisecond.  For an extended busy status
>    * code of 990n, perform the hinted delay of 10^n (last digit) milliseconds.
>    */
> diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c
> index c74d5e740922..e87f86f02569 100644
> --- a/arch/powerpc/platforms/pseries/ras.c
> +++ b/arch/powerpc/platforms/pseries/ras.c
> @@ -458,7 +458,7 @@ static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
>    */
>   static void fwnmi_release_errinfo(void)
>   {
> -	int ret = rtas_call(ibm_nmi_interlock_token, 0, 1, NULL);
> +	int ret = raw_rtas_call(ibm_nmi_interlock_token, 0, 1, NULL);
>   	if (ret != 0)
>   		printk(KERN_ERR "FWNMI: nmi-interlock failed: %d\n", ret);
>   }

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linuxppc-dev/attachments/20200323/4c4ba7c1/attachment.htm>


More information about the Linuxppc-dev mailing list