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