Commit 8c46def4 authored by Mark Brown's avatar Mark Brown Committed by Catalin Marinas
Browse files

arm64/signal: Add FPMR signal handling



Expose FPMR in the signal context on systems where it is supported. The
kernel validates the exact size of the FPSIMD registers so we can't readily
add it to fpsimd_context without disruption.

Signed-off-by: default avatarMark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20240306-arm64-2023-dpisa-v5-4-c568edc8ed7f@kernel.org


Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 203f2b95
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -152,6 +152,14 @@ struct tpidr2_context {
	__u64 tpidr2;
};

/* FPMR context */
#define FPMR_MAGIC	0x46504d52

struct fpmr_context {
	struct _aarch64_ctx head;
	__u64 fpmr;
};

#define ZA_MAGIC	0x54366345

struct za_context {
+59 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ struct rt_sigframe_user_layout {
	unsigned long tpidr2_offset;
	unsigned long za_offset;
	unsigned long zt_offset;
	unsigned long fpmr_offset;
	unsigned long extra_offset;
	unsigned long end_offset;
};
@@ -182,6 +183,8 @@ struct user_ctxs {
	u32 za_size;
	struct zt_context __user *zt;
	u32 zt_size;
	struct fpmr_context __user *fpmr;
	u32 fpmr_size;
};

static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
@@ -227,6 +230,33 @@ static int restore_fpsimd_context(struct user_ctxs *user)
	return err ? -EFAULT : 0;
}

static int preserve_fpmr_context(struct fpmr_context __user *ctx)
{
	int err = 0;

	current->thread.uw.fpmr = read_sysreg_s(SYS_FPMR);

	__put_user_error(FPMR_MAGIC, &ctx->head.magic, err);
	__put_user_error(sizeof(*ctx), &ctx->head.size, err);
	__put_user_error(current->thread.uw.fpmr, &ctx->fpmr, err);

	return err;
}

static int restore_fpmr_context(struct user_ctxs *user)
{
	u64 fpmr;
	int err = 0;

	if (user->fpmr_size != sizeof(*user->fpmr))
		return -EINVAL;

	__get_user_error(fpmr, &user->fpmr->fpmr, err);
	if (!err)
		write_sysreg_s(fpmr, SYS_FPMR);

	return err;
}

#ifdef CONFIG_ARM64_SVE

@@ -590,6 +620,7 @@ static int parse_user_sigframe(struct user_ctxs *user,
	user->tpidr2 = NULL;
	user->za = NULL;
	user->zt = NULL;
	user->fpmr = NULL;

	if (!IS_ALIGNED((unsigned long)base, 16))
		goto invalid;
@@ -684,6 +715,17 @@ static int parse_user_sigframe(struct user_ctxs *user,
			user->zt_size = size;
			break;

		case FPMR_MAGIC:
			if (!system_supports_fpmr())
				goto invalid;

			if (user->fpmr)
				goto invalid;

			user->fpmr = (struct fpmr_context __user *)head;
			user->fpmr_size = size;
			break;

		case EXTRA_MAGIC:
			if (have_extra_context)
				goto invalid;
@@ -806,6 +848,9 @@ static int restore_sigframe(struct pt_regs *regs,
	if (err == 0 && system_supports_tpidr2() && user.tpidr2)
		err = restore_tpidr2_context(&user);

	if (err == 0 && system_supports_fpmr() && user.fpmr)
		err = restore_fpmr_context(&user);

	if (err == 0 && system_supports_sme() && user.za)
		err = restore_za_context(&user);

@@ -928,6 +973,13 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
		}
	}

	if (system_supports_fpmr()) {
		err = sigframe_alloc(user, &user->fpmr_offset,
				     sizeof(struct fpmr_context));
		if (err)
			return err;
	}

	return sigframe_alloc_end(user);
}

@@ -983,6 +1035,13 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
		err |= preserve_tpidr2_context(tpidr2_ctx);
	}

	/* FPMR if supported */
	if (system_supports_fpmr() && err == 0) {
		struct fpmr_context __user *fpmr_ctx =
			apply_user_offset(user, user->fpmr_offset);
		err |= preserve_fpmr_context(fpmr_ctx);
	}

	/* ZA state if present */
	if (system_supports_sme() && err == 0 && user->za_offset) {
		struct za_context __user *za_ctx =