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.
*/