1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2018, Joyent, Inc.
  25  */
  26 
  27 /*
  28  * Floating point configuration.
  29  */
  30 
  31 #include <sys/types.h>
  32 #include <sys/regset.h>
  33 #include <sys/privregs.h>
  34 #include <sys/x86_archext.h>
  35 #include <sys/archsystm.h>
  36 #include <sys/fp.h>
  37 #include <sys/cmn_err.h>
  38 #include <sys/exec.h>
  39 
  40 #define XMM_ALIGN       16
  41 
  42 /*
  43  * See section 10.5.1 in the Intel 64 and IA-32 Architectures Software
  44  * Developer’s Manual, Volume 1.
  45  */
  46 #define FXSAVE_ALIGN    16
  47 
  48 /*
  49  * See section 13.4 in the Intel 64 and IA-32 Architectures Software
  50  * Developer’s Manual, Volume 1.
  51  */
  52 #define XSAVE_ALIGN     64
  53 
  54 /*
  55  * If fpu_exists is non-zero, fpu_probe will attempt to use any
  56  * hardware FPU (subject to other constraints, see below).  If
  57  * fpu_exists is zero, fpu_probe will report that there is no
  58  * FPU even if there is one.
  59  */
  60 int fpu_exists = 1;
  61 
  62 int fp_kind = FP_387;
  63 
  64 /*
  65  * The variable fpu_ignored is provided to allow other code to
  66  * determine whether emulation is being done because there is
  67  * no FPU or because of an override requested via /etc/system.
  68  */
  69 int fpu_ignored = 0;
  70 
  71 /*
  72  * Used by ppcopy and ppzero to determine whether or not to use the
  73  * SSE-based pagecopy and pagezero routines
  74  */
  75 int use_sse_pagecopy = 0;
  76 int use_sse_pagezero = 0;
  77 int use_sse_copy = 0;
  78 
  79 #if defined(__xpv)
  80 
  81 /*
  82  * Use of SSE or otherwise is forcibly configured for us by the hypervisor.
  83  */
  84 
  85 #define ENABLE_SSE()
  86 #define DISABLE_SSE()
  87 
  88 #else   /* __xpv */
  89 
  90 #define ENABLE_SSE()    setcr4(CR4_ENABLE_SSE_FLAGS(getcr4()))
  91 #define DISABLE_SSE()   setcr4(CR4_DISABLE_SSE_FLAGS(getcr4()))
  92 
  93 #endif  /* __xpv */
  94 
  95 /*
  96  * Try and figure out what kind of FP capabilities we have, and
  97  * set up the control registers accordingly.
  98  */
  99 void
 100 fpu_probe(void)
 101 {
 102         if (fpu_initial_probe() != 0)
 103                 goto nofpu;
 104 
 105         if (fpu_exists == 0) {
 106                 fpu_ignored = 1;
 107                 goto nofpu;
 108         }
 109 
 110 #ifndef __xpv
 111         /*
 112          * Check and see if the fpu is present by looking
 113          * at the "extension type" bit.  (While this used to
 114          * indicate a 387DX coprocessor in days gone by,
 115          * it's forced on by modern implementations for
 116          * compatibility.)
 117          */
 118         if ((getcr0() & CR0_ET) == 0)
 119                 goto nofpu;
 120 #endif
 121 
 122         /* Use the more complex exception clearing code if necessary */
 123         if (cpuid_need_fp_excp_handling())
 124                 fpsave_ctxt = fpxsave_excp_clr_ctxt;
 125 
 126         /*
 127          * SSE and SSE2 are required for the 64-bit ABI.
 128          *
 129          * If they're not present, we can in principal run
 130          * 32-bit userland, though 64-bit processes will be hosed.
 131          *
 132          * (Perhaps we should complain more about this case!)
 133          */
 134         if (is_x86_feature(x86_featureset, X86FSET_SSE) &&
 135             is_x86_feature(x86_featureset, X86FSET_SSE2)) {
 136                 fp_kind |= __FP_SSE;
 137                 ENABLE_SSE();
 138 
 139                 if (is_x86_feature(x86_featureset, X86FSET_AVX)) {
 140                         ASSERT(is_x86_feature(x86_featureset, X86FSET_XSAVE));
 141                         fp_kind |= __FP_AVX;
 142                 }
 143 
 144                 if (is_x86_feature(x86_featureset, X86FSET_XSAVE)) {
 145                         fp_save_mech = FP_XSAVE;
 146                         fp_elf = AT_386_FPINFO_XSAVE;
 147                         if (is_x86_feature(x86_featureset, X86FSET_XSAVEOPT)) {
 148                                 /*
 149                                  * Use the more complex exception
 150                                  * clearing code if necessary.
 151                                  */
 152                                 if (cpuid_need_fp_excp_handling()) {
 153                                         fpsave_ctxt = xsaveopt_excp_clr_ctxt;
 154                                         fp_elf = AT_386_FPINFO_XSAVE_AMD;
 155                                 } else {
 156                                         fpsave_ctxt = xsaveopt_ctxt;
 157                                 }
 158                                 xsavep = xsaveopt;
 159                         } else {
 160                                 /*
 161                                  * Use the more complex exception
 162                                  * clearing code if necessary.
 163                                  */
 164                                 if (cpuid_need_fp_excp_handling()) {
 165                                         fpsave_ctxt = xsave_excp_clr_ctxt;
 166                                         fp_elf = AT_386_FPINFO_XSAVE_AMD;
 167                                 } else {
 168                                         fpsave_ctxt = xsave_ctxt;
 169                                 }
 170                         }
 171                         fprestore_ctxt = xrestore_ctxt;
 172                         fpsave_cachep = kmem_cache_create("xsave_cache",
 173                             cpuid_get_xsave_size(), XSAVE_ALIGN,
 174                             NULL, NULL, NULL, NULL, NULL, 0);
 175                 } else {
 176                         /* fp_save_mech defaults to FP_FXSAVE */
 177                         fpsave_cachep = kmem_cache_create("fxsave_cache",
 178                             sizeof (struct fxsave_state), FXSAVE_ALIGN,
 179                             NULL, NULL, NULL, NULL, NULL, 0);
 180                         fp_elf = AT_386_FPINFO_FXSAVE;
 181                 }
 182         }
 183 
 184         if (is_x86_feature(x86_featureset, X86FSET_SSE2)) {
 185                 use_sse_pagecopy = use_sse_pagezero = use_sse_copy = 1;
 186         }
 187 
 188         if (fp_kind & __FP_SSE) {
 189                 struct fxsave_state *fx;
 190                 uint8_t fxsave_state[sizeof (struct fxsave_state) + XMM_ALIGN];
 191 
 192                 /*
 193                  * Extract the mxcsr mask from our first fxsave
 194                  */
 195                 fx = (void *)(((uintptr_t)(&fxsave_state[0]) +
 196                     XMM_ALIGN) & ~(XMM_ALIGN - 1ul));
 197 
 198                 fx->fx_mxcsr_mask = 0;
 199                 fxsave_insn(fx);
 200                 if (fx->fx_mxcsr_mask != 0) {
 201                         /*
 202                          * Override default mask initialized in fpu.c
 203                          */
 204                         sse_mxcsr_mask = fx->fx_mxcsr_mask;
 205                 }
 206         }
 207 
 208         setcr0(CR0_ENABLE_FPU_FLAGS(getcr0()));
 209         return;
 210 
 211         /*
 212          * No FPU hardware present
 213          */
 214 nofpu:
 215         setcr0(CR0_DISABLE_FPU_FLAGS(getcr0()));
 216         DISABLE_SSE();
 217         fp_kind = FP_NO;
 218         fpu_exists = 0;
 219 }