Print this page
15254 %ymm registers not restored after signal handler
15367 x86 getfpregs() summons corrupting %xmm ghosts
15333 want x86 /proc xregs support (libc_db, libproc, mdb, etc.)
15336 want libc functions for extended ucontext_t
15334 want ps_lwphandle-specific reg routines
15328 FPU_CW_INIT mistreats reserved bit
15335 i86pc fpu_subr.c isn't really platform-specific
15332 setcontext(2) isn't actually noreturn
15331 need <sys/stdalign.h>
Change-Id: I7060aa86042dfb989f77fc3323c065ea2eafa9ad
Conflicts:
    usr/src/uts/common/fs/proc/prcontrol.c
    usr/src/uts/intel/os/archdep.c
    usr/src/uts/intel/sys/ucontext.h
    usr/src/uts/intel/syscall/getcontext.c

*** 25,34 **** --- 25,38 ---- */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ + /* + * Copyright 2023 Oxide Computer Company + */ + #include <sys/types.h> #include <sys/t_lock.h> #include <sys/param.h> #include <sys/cred.h> #include <sys/debug.h>
*** 45,54 **** --- 49,59 ---- #include <sys/pcb.h> #include <sys/buf.h> #include <sys/signal.h> #include <sys/user.h> #include <sys/cpuvar.h> + #include <sys/stdalign.h> #include <sys/fault.h> #include <sys/syscall.h> #include <sys/procfs.h> #include <sys/cmn_err.h>
*** 234,280 **** setfpregs32(lwp, pfp); } #endif /* _SYSCALL32_IMPL */ /* ! * Does the system support extra register state? */ - /* ARGSUSED */ int prhasx(proc_t *p) { ! return (0); } /* ! * Get the size of the extra registers. */ ! /* ARGSUSED */ ! int prgetprxregsize(proc_t *p) { ! return (0); } /* * Get extra registers. */ - /*ARGSUSED*/ void ! prgetprxregs(klwp_t *lwp, caddr_t prx) { ! /* no extra registers */ } /* * Set extra registers. */ ! /*ARGSUSED*/ ! void ! prsetprxregs(klwp_t *lwp, caddr_t prx) { ! /* no extra registers */ } /* * Return the base (lower limit) of the process stack. */ --- 239,442 ---- setfpregs32(lwp, pfp); } #endif /* _SYSCALL32_IMPL */ /* ! * This is a general function that the main part of /proc and the rest of the ! * system uses to ask does a given process actually have extended state. Right ! * now, this question is not process-specific, but rather CPU specific. We look ! * at whether xsave has been enabled to determine that. While strictly speaking ! * one could make the argument that all amd64 CPUs support fxsave and we could ! * emulate something that only supports that, we don't think that makes sense. */ int prhasx(proc_t *p) { ! return (fpu_xsave_enabled()); } /* ! * Return the minimum size that we need to determine the full size of a ! * prxregset_t. */ ! boolean_t ! prwriteminxreg(size_t *sizep) ! { ! *sizep = sizeof (prxregset_hdr_t); ! return (B_TRUE); ! } ! ! /* ! * This routine services both ILP32 and LP64 callers. We cannot assume anything ! * about the alignment of argp and must bcopy things to known structures that we ! * care about. We are guaranteed we have prxregset_hdr_t bytes because we asked ! * for them above. ! */ ! boolean_t ! prwritesizexreg(const void *argp, size_t *sizep) ! { ! prxregset_hdr_t hdr; ! ! /* ! * While it's tempting to validate everything here, the only thing we ! * care about is that we understand the type and the size meets our ! * constraints: ! * ! * o We actually have an item of type PR_TYPE_XSAVE, otherwise we ! * don't know what this is. ! * o The indicated size actually contains at least the ! * prxregset_hdr_t. ! * o The indicated size isn't larger than what the FPU tells us is ! * allowed. ! * ! * We do not check if the reset of the structure makes semantic sense at ! * this point. We save all other validation for the normal set function ! * as that's when we'll have the rest of our data. ! */ ! bcopy(argp, &hdr, sizeof (hdr)); ! if (hdr.pr_type != PR_TYPE_XSAVE || ! hdr.pr_size > fpu_proc_xregs_max_size() || ! hdr.pr_size < sizeof (prxregset_hdr_t)) { ! return (B_FALSE); ! } ! ! *sizep = hdr.pr_size - sizeof (prxregset_hdr_t); ! return (B_TRUE); ! } ! ! /* ! * Get the size of the extra registers. The ultimate size here depends on a ! * combination of a few different things. Right now the xregs always have our ! * header, the illumos-specific XCR information, the xsave information, and then ! * otherwise this varies based on the items that the CPU supports. ! * ! * The ultimate size here is going to be: ! * ! * o 1x prxregset_hdr_t ! * o n prxregset_info_t structures ! * o The individual data for each one ! */ ! size_t prgetprxregsize(proc_t *p) { ! uint32_t size; ! ! fpu_proc_xregs_info(p, NULL, &size, NULL); ! return (size); } /* * Get extra registers. */ void ! prgetprxregs(klwp_t *lwp, prxregset_t *prx) { ! fpu_proc_xregs_get(lwp, prx); } /* * Set extra registers. + * + * We've been given a regset to set. Before we hand it off to the FPU, we have + * to go through and make sure that the different parts of this actually make + * sense. The kernel has guaranteed us through the functions above that we have + * the number of bytes that the header indicates are present. In particular we + * need to validate: + * + * o The information in the header is reasonable: we have a known type, flags + * and padding are zero, and there is at least one info structure. + * o Each of the info structures has a valid type, size, and fits within the + * data we were given. + * o We do not validate or modify the actual data in the different pieces for + * validity. That is considered something that the FPU does. Similarly if + * something is read-only or not used, that is something that it checks. + * + * While we would like to return something other than EINVAL, the /proc APIs + * pretty much lead that to being the primary errno for all sorts of situations. */ ! int ! prsetprxregs(klwp_t *lwp, prxregset_t *prx) { ! size_t infosz; ! prxregset_hdr_t *hdr = (prxregset_hdr_t *)prx; ! ! if (hdr->pr_type != PR_TYPE_XSAVE || hdr->pr_flags != 0 || ! hdr->pr_pad[0] != 0 || hdr->pr_pad[1] != 0 || hdr->pr_pad[2] != 0 || ! hdr->pr_pad[3] != 0 || hdr->pr_ninfo == 0) { ! return (EINVAL); ! } ! ! infosz = hdr->pr_ninfo * sizeof (prxregset_info_t) + ! sizeof (prxregset_hdr_t); ! if (infosz > hdr->pr_size) { ! return (EINVAL); ! } ! ! for (uint32_t i = 0; i < hdr->pr_ninfo; i++) { ! uint32_t exp_size; ! size_t need_len, exp_align; ! const prxregset_info_t *info = &hdr->pr_info[i]; ! ! switch (info->pri_type) { ! case PRX_INFO_XCR: ! exp_size = sizeof (prxregset_xcr_t); ! exp_align = alignof (prxregset_xcr_t); ! break; ! case PRX_INFO_XSAVE: ! exp_size = sizeof (prxregset_xsave_t); ! exp_align = alignof (prxregset_xsave_t); ! break; ! case PRX_INFO_YMM: ! exp_size = sizeof (prxregset_ymm_t); ! exp_align = alignof (prxregset_ymm_t); ! break; ! case PRX_INFO_OPMASK: ! exp_size = sizeof (prxregset_opmask_t); ! exp_align = alignof (prxregset_opmask_t); ! break; ! case PRX_INFO_ZMM: ! exp_size = sizeof (prxregset_zmm_t); ! exp_align = alignof (prxregset_zmm_t); ! break; ! case PRX_INFO_HI_ZMM: ! exp_size = sizeof (prxregset_hi_zmm_t); ! exp_align = alignof (prxregset_hi_zmm_t); ! break; ! default: ! return (EINVAL); ! } ! ! if (info->pri_flags != 0 || info->pri_size != exp_size) { ! return (EINVAL); ! } ! ! if ((info->pri_offset % exp_align) != 0) { ! return (EINVAL); ! } ! ! /* ! * No bytes of this item's entry should overlap with the ! * information area. If users want to overlap the actual data ! * information for some odd reason, we don't check that and let ! * them do what they want. However, the total data for this ! * region must actually fit. Because exp_size and pri_offset are ! * uint32_t's, we can sum them without overflow worries in an ! * LP64 environment. ! * ! * While we try to grantee alignment when writing this structure ! * out to userland, that is in no way a requirement and users ! * are allowed to start these structures wherever they want. ! * Hence that is not checked here. ! */ ! need_len = (size_t)exp_size + (size_t)info->pri_offset; ! if (info->pri_offset < infosz || ! need_len > (size_t)hdr->pr_size) { ! return (EINVAL); ! } ! } ! ! return (fpu_proc_xregs_set(lwp, prx)); } /* * Return the base (lower limit) of the process stack. */