Commit 01af50af authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull arm64 fixes from Catalin Marinas:

 - arm64 stacktrace: address some fallout from the recent changes to
   unwinding across exception boundaries

 - Ensure the arm64 signal delivery failure is recoverable - only
   override the return registers after all the user accesses took place

 - Fix the arm64 kselftest access to SVCR - only when SME is detected

* tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux:
  kselftest/arm64: abi: fix SVCR detection
  arm64: signal: Ensure signal delivery failure is recoverable
  arm64: stacktrace: Don't WARN when unwinding other tasks
  arm64: stacktrace: Skip reporting LR at exception boundaries
parents a5b3d5df ce03573a
Loading
Loading
Loading
Loading
+33 −15
Original line number Diff line number Diff line
@@ -1462,10 +1462,33 @@ static int setup_return(struct pt_regs *regs, struct ksignal *ksig,
			 struct rt_sigframe_user_layout *user, int usig)
{
	__sigrestore_t sigtramp;
	int err;

	if (ksig->ka.sa.sa_flags & SA_RESTORER)
		sigtramp = ksig->ka.sa.sa_restorer;
	else
		sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp);

	err = gcs_signal_entry(sigtramp, ksig);
	if (err)
		return err;

	/*
	 * We must not fail from this point onwards. We are going to update
	 * registers, including SP, in order to invoke the signal handler. If
	 * we failed and attempted to deliver a nested SIGSEGV to a handler
	 * after that point, the subsequent sigreturn would end up restoring
	 * the (partial) state for the original signal handler.
	 */

	regs->regs[0] = usig;
	if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
		regs->regs[1] = (unsigned long)&user->sigframe->info;
		regs->regs[2] = (unsigned long)&user->sigframe->uc;
	}
	regs->sp = (unsigned long)user->sigframe;
	regs->regs[29] = (unsigned long)&user->next_frame->fp;
	regs->regs[30] = (unsigned long)sigtramp;
	regs->pc = (unsigned long)ksig->ka.sa.sa_handler;

	/*
@@ -1506,14 +1529,7 @@ static int setup_return(struct pt_regs *regs, struct ksignal *ksig,
		sme_smstop();
	}

	if (ksig->ka.sa.sa_flags & SA_RESTORER)
		sigtramp = ksig->ka.sa.sa_restorer;
	else
		sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp);

	regs->regs[30] = (unsigned long)sigtramp;

	return gcs_signal_entry(sigtramp, ksig);
	return 0;
}

static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
@@ -1537,14 +1553,16 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,

	err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
	err |= setup_sigframe(&user, regs, set, &ua_state);
	if (err == 0) {
		err = setup_return(regs, ksig, &user, usig);
		if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
	if (ksig->ka.sa.sa_flags & SA_SIGINFO)
		err |= copy_siginfo_to_user(&frame->info, &ksig->info);
			regs->regs[1] = (unsigned long)&frame->info;
			regs->regs[2] = (unsigned long)&frame->uc;
		}
	}

	if (err == 0)
		err = setup_return(regs, ksig, &user, usig);

	/*
	 * We must not fail if setup_return() succeeded - see comment at the
	 * beginning of setup_return().
	 */

	if (err == 0)
		set_handler_user_access_state();
+7 −25
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ enum kunwind_source {
	KUNWIND_SOURCE_CALLER,
	KUNWIND_SOURCE_TASK,
	KUNWIND_SOURCE_REGS_PC,
	KUNWIND_SOURCE_REGS_LR,
};

union unwind_flags {
@@ -138,8 +137,10 @@ kunwind_recover_return_address(struct kunwind_state *state)
		orig_pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
						state->common.pc,
						(void *)state->common.fp);
		if (WARN_ON_ONCE(state->common.pc == orig_pc))
		if (state->common.pc == orig_pc) {
			WARN_ON_ONCE(state->task == current);
			return -EINVAL;
		}
		state->common.pc = orig_pc;
		state->flags.fgraph = 1;
	}
@@ -178,23 +179,8 @@ int kunwind_next_regs_pc(struct kunwind_state *state)
	state->regs = regs;
	state->common.pc = regs->pc;
	state->common.fp = regs->regs[29];
	state->source = KUNWIND_SOURCE_REGS_PC;
	return 0;
}

static __always_inline int
kunwind_next_regs_lr(struct kunwind_state *state)
{
	/*
	 * The stack for the regs was consumed by kunwind_next_regs_pc(), so we
	 * cannot consume that again here, but we know the regs are safe to
	 * access.
	 */
	state->common.pc = state->regs->regs[30];
	state->common.fp = state->regs->regs[29];
	state->regs = NULL;
	state->source = KUNWIND_SOURCE_REGS_LR;

	state->source = KUNWIND_SOURCE_REGS_PC;
	return 0;
}

@@ -215,12 +201,12 @@ kunwind_next_frame_record_meta(struct kunwind_state *state)
	case FRAME_META_TYPE_FINAL:
		if (meta == &task_pt_regs(tsk)->stackframe)
			return -ENOENT;
		WARN_ON_ONCE(1);
		WARN_ON_ONCE(tsk == current);
		return -EINVAL;
	case FRAME_META_TYPE_PT_REGS:
		return kunwind_next_regs_pc(state);
	default:
		WARN_ON_ONCE(1);
		WARN_ON_ONCE(tsk == current);
		return -EINVAL;
	}
}
@@ -274,11 +260,8 @@ kunwind_next(struct kunwind_state *state)
	case KUNWIND_SOURCE_FRAME:
	case KUNWIND_SOURCE_CALLER:
	case KUNWIND_SOURCE_TASK:
	case KUNWIND_SOURCE_REGS_LR:
		err = kunwind_next_frame_record(state);
		break;
	case KUNWIND_SOURCE_REGS_PC:
		err = kunwind_next_regs_lr(state);
		err = kunwind_next_frame_record(state);
		break;
	default:
		err = -EINVAL;
@@ -436,7 +419,6 @@ static const char *state_source_string(const struct kunwind_state *state)
	case KUNWIND_SOURCE_CALLER:	return "C";
	case KUNWIND_SOURCE_TASK:	return "T";
	case KUNWIND_SOURCE_REGS_PC:	return "P";
	case KUNWIND_SOURCE_REGS_LR:	return "L";
	default:			return "U";
	}
}
+15 −17
Original line number Diff line number Diff line
@@ -81,32 +81,31 @@ do_syscall:
	stp	x27, x28, [sp, #96]

	// Set SVCR if we're doing SME
	cbz	x1, 1f
	cbz	x1, load_gpr
	adrp	x2, svcr_in
	ldr	x2, [x2, :lo12:svcr_in]
	msr	S3_3_C4_C2_2, x2
1:

	// Load ZA and ZT0 if enabled - uses x12 as scratch due to SME LDR
	tbz	x2, #SVCR_ZA_SHIFT, 1f
	tbz	x2, #SVCR_ZA_SHIFT, load_gpr
	mov	w12, #0
	ldr	x2, =za_in
2:	_ldr_za 12, 2
1:	_ldr_za 12, 2
	add	x2, x2, x1
	add	x12, x12, #1
	cmp	x1, x12
	bne	2b
	bne	1b

	// ZT0
	mrs	x2, S3_0_C0_C4_5	// ID_AA64SMFR0_EL1
	ubfx	x2, x2, #ID_AA64SMFR0_EL1_SMEver_SHIFT, \
			 #ID_AA64SMFR0_EL1_SMEver_WIDTH
	cbz	x2, 1f
	cbz	x2, load_gpr
	adrp	x2, zt_in
	add	x2, x2, :lo12:zt_in
	_ldr_zt 2
1:

load_gpr:
	// Load GPRs x8-x28, and save our SP/FP for later comparison
	ldr	x2, =gpr_in
	add	x2, x2, #64
@@ -125,9 +124,9 @@ do_syscall:
	str	x30, [x2], #8		// LR

	// Load FPRs if we're not doing neither SVE nor streaming SVE
	cbnz	x0, 1f
	cbnz	x0, check_sve_in
	ldr	x2, =svcr_in
	tbnz	x2, #SVCR_SM_SHIFT, 1f
	tbnz	x2, #SVCR_SM_SHIFT, check_sve_in

	ldr	x2, =fpr_in
	ldp	q0, q1, [x2]
@@ -148,8 +147,8 @@ do_syscall:
	ldp	q30, q31, [x2, #16 * 30]

	b	2f
1:

check_sve_in:
	// Load the SVE registers if we're doing SVE/SME

	ldr	x2, =z_in
@@ -256,32 +255,31 @@ do_syscall:
	stp	q30, q31, [x2, #16 * 30]

	// Save SVCR if we're doing SME
	cbz	x1, 1f
	cbz	x1, check_sve_out
	mrs	x2, S3_3_C4_C2_2
	adrp	x3, svcr_out
	str	x2, [x3, :lo12:svcr_out]
1:

	// Save ZA if it's enabled - uses x12 as scratch due to SME STR
	tbz	x2, #SVCR_ZA_SHIFT, 1f
	tbz	x2, #SVCR_ZA_SHIFT, check_sve_out
	mov	w12, #0
	ldr	x2, =za_out
2:	_str_za 12, 2
1:	_str_za 12, 2
	add	x2, x2, x1
	add	x12, x12, #1
	cmp	x1, x12
	bne	2b
	bne	1b

	// ZT0
	mrs	x2, S3_0_C0_C4_5	// ID_AA64SMFR0_EL1
	ubfx	x2, x2, #ID_AA64SMFR0_EL1_SMEver_SHIFT, \
			#ID_AA64SMFR0_EL1_SMEver_WIDTH
	cbz	x2, 1f
	cbz	x2, check_sve_out
	adrp	x2, zt_out
	add	x2, x2, :lo12:zt_out
	_str_zt 2
1:

check_sve_out:
	// Save the SVE state if we have some
	cbz	x0, 1f