162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) 2019 ARM Limited */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <ctype.h>
562306a36Sopenharmony_ci#include <string.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "testcases.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistruct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
1062306a36Sopenharmony_ci				size_t resv_sz, size_t *offset)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	size_t offs = 0;
1362306a36Sopenharmony_ci	struct _aarch64_ctx *found = NULL;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	if (!head || resv_sz < HDR_SZ)
1662306a36Sopenharmony_ci		return found;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	while (offs <= resv_sz - HDR_SZ &&
1962306a36Sopenharmony_ci	       head->magic != magic && head->magic) {
2062306a36Sopenharmony_ci		offs += head->size;
2162306a36Sopenharmony_ci		head = GET_RESV_NEXT_HEAD(head);
2262306a36Sopenharmony_ci	}
2362306a36Sopenharmony_ci	if (head->magic == magic) {
2462306a36Sopenharmony_ci		found = head;
2562306a36Sopenharmony_ci		if (offset)
2662306a36Sopenharmony_ci			*offset = offs;
2762306a36Sopenharmony_ci	}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	return found;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cibool validate_extra_context(struct extra_context *extra, char **err,
3362306a36Sopenharmony_ci			    void **extra_data, size_t *extra_size)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct _aarch64_ctx *term;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (!extra || !err)
3862306a36Sopenharmony_ci		return false;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	fprintf(stderr, "Validating EXTRA...\n");
4162306a36Sopenharmony_ci	term = GET_RESV_NEXT_HEAD(&extra->head);
4262306a36Sopenharmony_ci	if (!term || term->magic || term->size) {
4362306a36Sopenharmony_ci		*err = "Missing terminator after EXTRA context";
4462306a36Sopenharmony_ci		return false;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci	if (extra->datap & 0x0fUL)
4762306a36Sopenharmony_ci		*err = "Extra DATAP misaligned";
4862306a36Sopenharmony_ci	else if (extra->size & 0x0fUL)
4962306a36Sopenharmony_ci		*err = "Extra SIZE misaligned";
5062306a36Sopenharmony_ci	else if (extra->datap != (uint64_t)term + 0x10UL)
5162306a36Sopenharmony_ci		*err = "Extra DATAP misplaced (not contiguous)";
5262306a36Sopenharmony_ci	if (*err)
5362306a36Sopenharmony_ci		return false;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	*extra_data = (void *)extra->datap;
5662306a36Sopenharmony_ci	*extra_size = extra->size;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return true;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cibool validate_sve_context(struct sve_context *sve, char **err)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	/* Size will be rounded up to a multiple of 16 bytes */
6462306a36Sopenharmony_ci	size_t regs_size
6562306a36Sopenharmony_ci		= ((SVE_SIG_CONTEXT_SIZE(sve_vq_from_vl(sve->vl)) + 15) / 16) * 16;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (!sve || !err)
6862306a36Sopenharmony_ci		return false;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Either a bare sve_context or a sve_context followed by regs data */
7162306a36Sopenharmony_ci	if ((sve->head.size != sizeof(struct sve_context)) &&
7262306a36Sopenharmony_ci	    (sve->head.size != regs_size)) {
7362306a36Sopenharmony_ci		*err = "bad size for SVE context";
7462306a36Sopenharmony_ci		return false;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!sve_vl_valid(sve->vl)) {
7862306a36Sopenharmony_ci		*err = "SVE VL invalid";
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		return false;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return true;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cibool validate_za_context(struct za_context *za, char **err)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	/* Size will be rounded up to a multiple of 16 bytes */
8962306a36Sopenharmony_ci	size_t regs_size
9062306a36Sopenharmony_ci		= ((ZA_SIG_CONTEXT_SIZE(sve_vq_from_vl(za->vl)) + 15) / 16) * 16;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (!za || !err)
9362306a36Sopenharmony_ci		return false;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* Either a bare za_context or a za_context followed by regs data */
9662306a36Sopenharmony_ci	if ((za->head.size != sizeof(struct za_context)) &&
9762306a36Sopenharmony_ci	    (za->head.size != regs_size)) {
9862306a36Sopenharmony_ci		*err = "bad size for ZA context";
9962306a36Sopenharmony_ci		return false;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (!sve_vl_valid(za->vl)) {
10362306a36Sopenharmony_ci		*err = "SME VL in ZA context invalid";
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		return false;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return true;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cibool validate_zt_context(struct zt_context *zt, char **err)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	if (!zt || !err)
11462306a36Sopenharmony_ci		return false;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/* If the context is present there should be at least one register */
11762306a36Sopenharmony_ci	if (zt->nregs == 0) {
11862306a36Sopenharmony_ci		*err = "no registers";
11962306a36Sopenharmony_ci		return false;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* Size should agree with the number of registers */
12362306a36Sopenharmony_ci	if (zt->head.size != ZT_SIG_CONTEXT_SIZE(zt->nregs)) {
12462306a36Sopenharmony_ci		*err = "register count does not match size";
12562306a36Sopenharmony_ci		return false;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return true;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cibool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	bool terminated = false;
13462306a36Sopenharmony_ci	size_t offs = 0;
13562306a36Sopenharmony_ci	int flags = 0;
13662306a36Sopenharmony_ci	int new_flags, i;
13762306a36Sopenharmony_ci	struct extra_context *extra = NULL;
13862306a36Sopenharmony_ci	struct sve_context *sve = NULL;
13962306a36Sopenharmony_ci	struct za_context *za = NULL;
14062306a36Sopenharmony_ci	struct zt_context *zt = NULL;
14162306a36Sopenharmony_ci	struct _aarch64_ctx *head =
14262306a36Sopenharmony_ci		(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
14362306a36Sopenharmony_ci	void *extra_data = NULL;
14462306a36Sopenharmony_ci	size_t extra_sz = 0;
14562306a36Sopenharmony_ci	char magic[4];
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (!err)
14862306a36Sopenharmony_ci		return false;
14962306a36Sopenharmony_ci	/* Walk till the end terminator verifying __reserved contents */
15062306a36Sopenharmony_ci	while (head && !terminated && offs < resv_sz) {
15162306a36Sopenharmony_ci		if ((uint64_t)head & 0x0fUL) {
15262306a36Sopenharmony_ci			*err = "Misaligned HEAD";
15362306a36Sopenharmony_ci			return false;
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		new_flags = 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		switch (head->magic) {
15962306a36Sopenharmony_ci		case 0:
16062306a36Sopenharmony_ci			if (head->size) {
16162306a36Sopenharmony_ci				*err = "Bad size for terminator";
16262306a36Sopenharmony_ci			} else if (extra_data) {
16362306a36Sopenharmony_ci				/* End of main data, walking the extra data */
16462306a36Sopenharmony_ci				head = extra_data;
16562306a36Sopenharmony_ci				resv_sz = extra_sz;
16662306a36Sopenharmony_ci				offs = 0;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci				extra_data = NULL;
16962306a36Sopenharmony_ci				extra_sz = 0;
17062306a36Sopenharmony_ci				continue;
17162306a36Sopenharmony_ci			} else {
17262306a36Sopenharmony_ci				terminated = true;
17362306a36Sopenharmony_ci			}
17462306a36Sopenharmony_ci			break;
17562306a36Sopenharmony_ci		case FPSIMD_MAGIC:
17662306a36Sopenharmony_ci			if (flags & FPSIMD_CTX)
17762306a36Sopenharmony_ci				*err = "Multiple FPSIMD_MAGIC";
17862306a36Sopenharmony_ci			else if (head->size !=
17962306a36Sopenharmony_ci				 sizeof(struct fpsimd_context))
18062306a36Sopenharmony_ci				*err = "Bad size for fpsimd_context";
18162306a36Sopenharmony_ci			new_flags |= FPSIMD_CTX;
18262306a36Sopenharmony_ci			break;
18362306a36Sopenharmony_ci		case ESR_MAGIC:
18462306a36Sopenharmony_ci			if (head->size != sizeof(struct esr_context))
18562306a36Sopenharmony_ci				*err = "Bad size for esr_context";
18662306a36Sopenharmony_ci			break;
18762306a36Sopenharmony_ci		case TPIDR2_MAGIC:
18862306a36Sopenharmony_ci			if (head->size != sizeof(struct tpidr2_context))
18962306a36Sopenharmony_ci				*err = "Bad size for tpidr2_context";
19062306a36Sopenharmony_ci			break;
19162306a36Sopenharmony_ci		case SVE_MAGIC:
19262306a36Sopenharmony_ci			if (flags & SVE_CTX)
19362306a36Sopenharmony_ci				*err = "Multiple SVE_MAGIC";
19462306a36Sopenharmony_ci			/* Size is validated in validate_sve_context() */
19562306a36Sopenharmony_ci			sve = (struct sve_context *)head;
19662306a36Sopenharmony_ci			new_flags |= SVE_CTX;
19762306a36Sopenharmony_ci			break;
19862306a36Sopenharmony_ci		case ZA_MAGIC:
19962306a36Sopenharmony_ci			if (flags & ZA_CTX)
20062306a36Sopenharmony_ci				*err = "Multiple ZA_MAGIC";
20162306a36Sopenharmony_ci			/* Size is validated in validate_za_context() */
20262306a36Sopenharmony_ci			za = (struct za_context *)head;
20362306a36Sopenharmony_ci			new_flags |= ZA_CTX;
20462306a36Sopenharmony_ci			break;
20562306a36Sopenharmony_ci		case ZT_MAGIC:
20662306a36Sopenharmony_ci			if (flags & ZT_CTX)
20762306a36Sopenharmony_ci				*err = "Multiple ZT_MAGIC";
20862306a36Sopenharmony_ci			/* Size is validated in validate_za_context() */
20962306a36Sopenharmony_ci			zt = (struct zt_context *)head;
21062306a36Sopenharmony_ci			new_flags |= ZT_CTX;
21162306a36Sopenharmony_ci			break;
21262306a36Sopenharmony_ci		case EXTRA_MAGIC:
21362306a36Sopenharmony_ci			if (flags & EXTRA_CTX)
21462306a36Sopenharmony_ci				*err = "Multiple EXTRA_MAGIC";
21562306a36Sopenharmony_ci			else if (head->size !=
21662306a36Sopenharmony_ci				 sizeof(struct extra_context))
21762306a36Sopenharmony_ci				*err = "Bad size for extra_context";
21862306a36Sopenharmony_ci			new_flags |= EXTRA_CTX;
21962306a36Sopenharmony_ci			extra = (struct extra_context *)head;
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci		case KSFT_BAD_MAGIC:
22262306a36Sopenharmony_ci			/*
22362306a36Sopenharmony_ci			 * This is a BAD magic header defined
22462306a36Sopenharmony_ci			 * artificially by a testcase and surely
22562306a36Sopenharmony_ci			 * unknown to the Kernel parse_user_sigframe().
22662306a36Sopenharmony_ci			 * It MUST cause a Kernel induced SEGV
22762306a36Sopenharmony_ci			 */
22862306a36Sopenharmony_ci			*err = "BAD MAGIC !";
22962306a36Sopenharmony_ci			break;
23062306a36Sopenharmony_ci		default:
23162306a36Sopenharmony_ci			/*
23262306a36Sopenharmony_ci			 * A still unknown Magic: potentially freshly added
23362306a36Sopenharmony_ci			 * to the Kernel code and still unknown to the
23462306a36Sopenharmony_ci			 * tests.  Magic numbers are supposed to be allocated
23562306a36Sopenharmony_ci			 * as somewhat meaningful ASCII strings so try to
23662306a36Sopenharmony_ci			 * print as such as well as the raw number.
23762306a36Sopenharmony_ci			 */
23862306a36Sopenharmony_ci			memcpy(magic, &head->magic, sizeof(magic));
23962306a36Sopenharmony_ci			for (i = 0; i < sizeof(magic); i++)
24062306a36Sopenharmony_ci				if (!isalnum(magic[i]))
24162306a36Sopenharmony_ci					magic[i] = '?';
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci			fprintf(stdout,
24462306a36Sopenharmony_ci				"SKIP Unknown MAGIC: 0x%X (%c%c%c%c) - Is KSFT arm64/signal up to date ?\n",
24562306a36Sopenharmony_ci				head->magic,
24662306a36Sopenharmony_ci				magic[3], magic[2], magic[1], magic[0]);
24762306a36Sopenharmony_ci			break;
24862306a36Sopenharmony_ci		}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		if (*err)
25162306a36Sopenharmony_ci			return false;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		offs += head->size;
25462306a36Sopenharmony_ci		if (resv_sz < offs + sizeof(*head)) {
25562306a36Sopenharmony_ci			*err = "HEAD Overrun";
25662306a36Sopenharmony_ci			return false;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		if (new_flags & EXTRA_CTX)
26062306a36Sopenharmony_ci			if (!validate_extra_context(extra, err,
26162306a36Sopenharmony_ci						    &extra_data, &extra_sz))
26262306a36Sopenharmony_ci				return false;
26362306a36Sopenharmony_ci		if (new_flags & SVE_CTX)
26462306a36Sopenharmony_ci			if (!validate_sve_context(sve, err))
26562306a36Sopenharmony_ci				return false;
26662306a36Sopenharmony_ci		if (new_flags & ZA_CTX)
26762306a36Sopenharmony_ci			if (!validate_za_context(za, err))
26862306a36Sopenharmony_ci				return false;
26962306a36Sopenharmony_ci		if (new_flags & ZT_CTX)
27062306a36Sopenharmony_ci			if (!validate_zt_context(zt, err))
27162306a36Sopenharmony_ci				return false;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		flags |= new_flags;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		head = GET_RESV_NEXT_HEAD(head);
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (terminated && !(flags & FPSIMD_CTX)) {
27962306a36Sopenharmony_ci		*err = "Missing FPSIMD";
28062306a36Sopenharmony_ci		return false;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (terminated && (flags & ZT_CTX) && !(flags & ZA_CTX)) {
28462306a36Sopenharmony_ci		*err = "ZT context but no ZA context";
28562306a36Sopenharmony_ci		return false;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return true;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/*
29262306a36Sopenharmony_ci * This function walks through the records inside the provided reserved area
29362306a36Sopenharmony_ci * trying to find enough space to fit @need_sz bytes: if not enough space is
29462306a36Sopenharmony_ci * available and an extra_context record is present, it throws away the
29562306a36Sopenharmony_ci * extra_context record.
29662306a36Sopenharmony_ci *
29762306a36Sopenharmony_ci * It returns a pointer to a new header where it is possible to start storing
29862306a36Sopenharmony_ci * our need_sz bytes.
29962306a36Sopenharmony_ci *
30062306a36Sopenharmony_ci * @shead: points to the start of reserved area
30162306a36Sopenharmony_ci * @need_sz: needed bytes
30262306a36Sopenharmony_ci * @resv_sz: reserved area size in bytes
30362306a36Sopenharmony_ci * @offset: if not null, this will be filled with the offset of the return
30462306a36Sopenharmony_ci *	    head pointer from @shead
30562306a36Sopenharmony_ci *
30662306a36Sopenharmony_ci * @return: pointer to a new head where to start storing need_sz bytes, or
30762306a36Sopenharmony_ci *	    NULL if space could not be made available.
30862306a36Sopenharmony_ci */
30962306a36Sopenharmony_cistruct _aarch64_ctx *get_starting_head(struct _aarch64_ctx *shead,
31062306a36Sopenharmony_ci				       size_t need_sz, size_t resv_sz,
31162306a36Sopenharmony_ci				       size_t *offset)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	size_t offs = 0;
31462306a36Sopenharmony_ci	struct _aarch64_ctx *head;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	head = get_terminator(shead, resv_sz, &offs);
31762306a36Sopenharmony_ci	/* not found a terminator...no need to update offset if any */
31862306a36Sopenharmony_ci	if (!head)
31962306a36Sopenharmony_ci		return head;
32062306a36Sopenharmony_ci	if (resv_sz - offs < need_sz) {
32162306a36Sopenharmony_ci		fprintf(stderr, "Low on space:%zd. Discarding extra_context.\n",
32262306a36Sopenharmony_ci			resv_sz - offs);
32362306a36Sopenharmony_ci		head = get_header(shead, EXTRA_MAGIC, resv_sz, &offs);
32462306a36Sopenharmony_ci		if (!head || resv_sz - offs < need_sz) {
32562306a36Sopenharmony_ci			fprintf(stderr,
32662306a36Sopenharmony_ci				"Failed to reclaim space on sigframe.\n");
32762306a36Sopenharmony_ci			return NULL;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	fprintf(stderr, "Available space:%zd\n", resv_sz - offs);
33262306a36Sopenharmony_ci	if (offset)
33362306a36Sopenharmony_ci		*offset = offs;
33462306a36Sopenharmony_ci	return head;
33562306a36Sopenharmony_ci}
336