18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (C) 2019 ARM Limited */
38c2ecf20Sopenharmony_ci#include "testcases.h"
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_cistruct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
68c2ecf20Sopenharmony_ci				size_t resv_sz, size_t *offset)
78c2ecf20Sopenharmony_ci{
88c2ecf20Sopenharmony_ci	size_t offs = 0;
98c2ecf20Sopenharmony_ci	struct _aarch64_ctx *found = NULL;
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci	if (!head || resv_sz < HDR_SZ)
128c2ecf20Sopenharmony_ci		return found;
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci	while (offs <= resv_sz - HDR_SZ &&
158c2ecf20Sopenharmony_ci	       head->magic != magic && head->magic) {
168c2ecf20Sopenharmony_ci		offs += head->size;
178c2ecf20Sopenharmony_ci		head = GET_RESV_NEXT_HEAD(head);
188c2ecf20Sopenharmony_ci	}
198c2ecf20Sopenharmony_ci	if (head->magic == magic) {
208c2ecf20Sopenharmony_ci		found = head;
218c2ecf20Sopenharmony_ci		if (offset)
228c2ecf20Sopenharmony_ci			*offset = offs;
238c2ecf20Sopenharmony_ci	}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	return found;
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cibool validate_extra_context(struct extra_context *extra, char **err)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct _aarch64_ctx *term;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (!extra || !err)
338c2ecf20Sopenharmony_ci		return false;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	fprintf(stderr, "Validating EXTRA...\n");
368c2ecf20Sopenharmony_ci	term = GET_RESV_NEXT_HEAD(&extra->head);
378c2ecf20Sopenharmony_ci	if (!term || term->magic || term->size) {
388c2ecf20Sopenharmony_ci		*err = "Missing terminator after EXTRA context";
398c2ecf20Sopenharmony_ci		return false;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci	if (extra->datap & 0x0fUL)
428c2ecf20Sopenharmony_ci		*err = "Extra DATAP misaligned";
438c2ecf20Sopenharmony_ci	else if (extra->size & 0x0fUL)
448c2ecf20Sopenharmony_ci		*err = "Extra SIZE misaligned";
458c2ecf20Sopenharmony_ci	else if (extra->datap != (uint64_t)term + sizeof(*term))
468c2ecf20Sopenharmony_ci		*err = "Extra DATAP misplaced (not contiguous)";
478c2ecf20Sopenharmony_ci	if (*err)
488c2ecf20Sopenharmony_ci		return false;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return true;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cibool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	bool terminated = false;
568c2ecf20Sopenharmony_ci	size_t offs = 0;
578c2ecf20Sopenharmony_ci	int flags = 0;
588c2ecf20Sopenharmony_ci	struct extra_context *extra = NULL;
598c2ecf20Sopenharmony_ci	struct _aarch64_ctx *head =
608c2ecf20Sopenharmony_ci		(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!err)
638c2ecf20Sopenharmony_ci		return false;
648c2ecf20Sopenharmony_ci	/* Walk till the end terminator verifying __reserved contents */
658c2ecf20Sopenharmony_ci	while (head && !terminated && offs < resv_sz) {
668c2ecf20Sopenharmony_ci		if ((uint64_t)head & 0x0fUL) {
678c2ecf20Sopenharmony_ci			*err = "Misaligned HEAD";
688c2ecf20Sopenharmony_ci			return false;
698c2ecf20Sopenharmony_ci		}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		switch (head->magic) {
728c2ecf20Sopenharmony_ci		case 0:
738c2ecf20Sopenharmony_ci			if (head->size)
748c2ecf20Sopenharmony_ci				*err = "Bad size for terminator";
758c2ecf20Sopenharmony_ci			else
768c2ecf20Sopenharmony_ci				terminated = true;
778c2ecf20Sopenharmony_ci			break;
788c2ecf20Sopenharmony_ci		case FPSIMD_MAGIC:
798c2ecf20Sopenharmony_ci			if (flags & FPSIMD_CTX)
808c2ecf20Sopenharmony_ci				*err = "Multiple FPSIMD_MAGIC";
818c2ecf20Sopenharmony_ci			else if (head->size !=
828c2ecf20Sopenharmony_ci				 sizeof(struct fpsimd_context))
838c2ecf20Sopenharmony_ci				*err = "Bad size for fpsimd_context";
848c2ecf20Sopenharmony_ci			flags |= FPSIMD_CTX;
858c2ecf20Sopenharmony_ci			break;
868c2ecf20Sopenharmony_ci		case ESR_MAGIC:
878c2ecf20Sopenharmony_ci			if (head->size != sizeof(struct esr_context))
888c2ecf20Sopenharmony_ci				*err = "Bad size for esr_context";
898c2ecf20Sopenharmony_ci			break;
908c2ecf20Sopenharmony_ci		case SVE_MAGIC:
918c2ecf20Sopenharmony_ci			if (flags & SVE_CTX)
928c2ecf20Sopenharmony_ci				*err = "Multiple SVE_MAGIC";
938c2ecf20Sopenharmony_ci			else if (head->size !=
948c2ecf20Sopenharmony_ci				 sizeof(struct sve_context))
958c2ecf20Sopenharmony_ci				*err = "Bad size for sve_context";
968c2ecf20Sopenharmony_ci			flags |= SVE_CTX;
978c2ecf20Sopenharmony_ci			break;
988c2ecf20Sopenharmony_ci		case EXTRA_MAGIC:
998c2ecf20Sopenharmony_ci			if (flags & EXTRA_CTX)
1008c2ecf20Sopenharmony_ci				*err = "Multiple EXTRA_MAGIC";
1018c2ecf20Sopenharmony_ci			else if (head->size !=
1028c2ecf20Sopenharmony_ci				 sizeof(struct extra_context))
1038c2ecf20Sopenharmony_ci				*err = "Bad size for extra_context";
1048c2ecf20Sopenharmony_ci			flags |= EXTRA_CTX;
1058c2ecf20Sopenharmony_ci			extra = (struct extra_context *)head;
1068c2ecf20Sopenharmony_ci			break;
1078c2ecf20Sopenharmony_ci		case KSFT_BAD_MAGIC:
1088c2ecf20Sopenharmony_ci			/*
1098c2ecf20Sopenharmony_ci			 * This is a BAD magic header defined
1108c2ecf20Sopenharmony_ci			 * artificially by a testcase and surely
1118c2ecf20Sopenharmony_ci			 * unknown to the Kernel parse_user_sigframe().
1128c2ecf20Sopenharmony_ci			 * It MUST cause a Kernel induced SEGV
1138c2ecf20Sopenharmony_ci			 */
1148c2ecf20Sopenharmony_ci			*err = "BAD MAGIC !";
1158c2ecf20Sopenharmony_ci			break;
1168c2ecf20Sopenharmony_ci		default:
1178c2ecf20Sopenharmony_ci			/*
1188c2ecf20Sopenharmony_ci			 * A still unknown Magic: potentially freshly added
1198c2ecf20Sopenharmony_ci			 * to the Kernel code and still unknown to the
1208c2ecf20Sopenharmony_ci			 * tests.
1218c2ecf20Sopenharmony_ci			 */
1228c2ecf20Sopenharmony_ci			fprintf(stdout,
1238c2ecf20Sopenharmony_ci				"SKIP Unknown MAGIC: 0x%X - Is KSFT arm64/signal up to date ?\n",
1248c2ecf20Sopenharmony_ci				head->magic);
1258c2ecf20Sopenharmony_ci			break;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		if (*err)
1298c2ecf20Sopenharmony_ci			return false;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci		offs += head->size;
1328c2ecf20Sopenharmony_ci		if (resv_sz < offs + sizeof(*head)) {
1338c2ecf20Sopenharmony_ci			*err = "HEAD Overrun";
1348c2ecf20Sopenharmony_ci			return false;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		if (flags & EXTRA_CTX)
1388c2ecf20Sopenharmony_ci			if (!validate_extra_context(extra, err))
1398c2ecf20Sopenharmony_ci				return false;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		head = GET_RESV_NEXT_HEAD(head);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (terminated && !(flags & FPSIMD_CTX)) {
1458c2ecf20Sopenharmony_ci		*err = "Missing FPSIMD";
1468c2ecf20Sopenharmony_ci		return false;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return true;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci * This function walks through the records inside the provided reserved area
1548c2ecf20Sopenharmony_ci * trying to find enough space to fit @need_sz bytes: if not enough space is
1558c2ecf20Sopenharmony_ci * available and an extra_context record is present, it throws away the
1568c2ecf20Sopenharmony_ci * extra_context record.
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci * It returns a pointer to a new header where it is possible to start storing
1598c2ecf20Sopenharmony_ci * our need_sz bytes.
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci * @shead: points to the start of reserved area
1628c2ecf20Sopenharmony_ci * @need_sz: needed bytes
1638c2ecf20Sopenharmony_ci * @resv_sz: reserved area size in bytes
1648c2ecf20Sopenharmony_ci * @offset: if not null, this will be filled with the offset of the return
1658c2ecf20Sopenharmony_ci *	    head pointer from @shead
1668c2ecf20Sopenharmony_ci *
1678c2ecf20Sopenharmony_ci * @return: pointer to a new head where to start storing need_sz bytes, or
1688c2ecf20Sopenharmony_ci *	    NULL if space could not be made available.
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_cistruct _aarch64_ctx *get_starting_head(struct _aarch64_ctx *shead,
1718c2ecf20Sopenharmony_ci				       size_t need_sz, size_t resv_sz,
1728c2ecf20Sopenharmony_ci				       size_t *offset)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	size_t offs = 0;
1758c2ecf20Sopenharmony_ci	struct _aarch64_ctx *head;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	head = get_terminator(shead, resv_sz, &offs);
1788c2ecf20Sopenharmony_ci	/* not found a terminator...no need to update offset if any */
1798c2ecf20Sopenharmony_ci	if (!head)
1808c2ecf20Sopenharmony_ci		return head;
1818c2ecf20Sopenharmony_ci	if (resv_sz - offs < need_sz) {
1828c2ecf20Sopenharmony_ci		fprintf(stderr, "Low on space:%zd. Discarding extra_context.\n",
1838c2ecf20Sopenharmony_ci			resv_sz - offs);
1848c2ecf20Sopenharmony_ci		head = get_header(shead, EXTRA_MAGIC, resv_sz, &offs);
1858c2ecf20Sopenharmony_ci		if (!head || resv_sz - offs < need_sz) {
1868c2ecf20Sopenharmony_ci			fprintf(stderr,
1878c2ecf20Sopenharmony_ci				"Failed to reclaim space on sigframe.\n");
1888c2ecf20Sopenharmony_ci			return NULL;
1898c2ecf20Sopenharmony_ci		}
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	fprintf(stderr, "Available space:%zd\n", resv_sz - offs);
1938c2ecf20Sopenharmony_ci	if (offset)
1948c2ecf20Sopenharmony_ci		*offset = offs;
1958c2ecf20Sopenharmony_ci	return head;
1968c2ecf20Sopenharmony_ci}
197