1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2023 Oxide Computer Company
  14  */
  15 
  16 /*
  17  * In illumos#15367 we noted that on some CPUs it was possible to get the xsave
  18  * state into a place where it has not set components that are used for the x87
  19  * and XMM state. This test attempts to create those situations and then get a
  20  * ucontext_t to see that we actually have cleared it out. Because this behavior
  21  * varies from CPU to CPU (e.g. in the original case we only saw this on AMD and
  22  * not Intel), it can be tricky to guarantee we've recreated this.
  23  */
  24 
  25 #include <err.h>
  26 #include <stdio.h>
  27 #include <sys/types.h>
  28 #include <ucontext.h>
  29 #include <stdlib.h>
  30 
  31 int
  32 main(void)
  33 {
  34         ucontext_t ctx;
  35         upad128_t u, *up;
  36 
  37         u._l[0] = 0x1;
  38         u._l[1] = 0x2;
  39         u._l[2] = 0x3;
  40         u._l[3] = 0x4;
  41 
  42         /*
  43          * Load %xmm0 and then lock in the data into the pcb with a call to
  44          * getcontext(2) which will force an FPU save.
  45          */
  46         /* BEGIN CSTYLED */
  47         __asm__ __volatile__ ("movdqu %0, %%xmm0" : : "m" (u));
  48         __asm__ __volatile__ ("fldl %0" : : "m" (u));
  49         /* END CSTYLED */
  50         if (getcontext(&ctx) != 0) {
  51                 errx(EXIT_FAILURE, "TEST_FAILED: failed to get initial "
  52                     "ucontext");
  53         }
  54 
  55         /*
  56          * Attempt to reset the FPU at this point and then call getcontext. The
  57          * fninit is for the x87 part. The vzeroall covers all the higher
  58          * registers and this combined should reset the x87 and xmm regions back
  59          * to 0. It appears that on some Intel processors, the vzeroall is
  60          * required to get the XMM set to not be written out as opposed to just
  61          * doing a pxor or similar.
  62          */
  63         /* BEGIN CSTYLED */
  64         __asm__ __volatile__ ("fninit" : : :);
  65         __asm__ __volatile__ ("vzeroall" : : :);
  66         /* END CSTYLED */
  67         if (getcontext(&ctx) != 0) {
  68                 errx(EXIT_FAILURE, "TEST_FAILED: failed to get second "
  69                     "ucontext");
  70         }
  71         up = &ctx.uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[0];
  72         if (up->_l[0] != 0 || up->_l[1] != 0 || up->_l[2] != 0 ||
  73             up->_l[3] != 0) {
  74                 errx(EXIT_FAILURE, "TEST FAILED: %%xmm0 was not zero, found: "
  75                     "0x%x 0x%x 0x%x 0x%x", up->_l[3], up->_l[2],
  76                     up->_l[1], up->_l[0]);
  77         }
  78 
  79         (void) printf("TEST PASSED: successfully got zeored %%xmm0\n");
  80         return (EXIT_SUCCESS);
  81 }