1/* mpxrt.c                  -*-C++-*-
2 *
3 *************************************************************************
4 *
5 *  @copyright
6 *  Copyright (C) 2014, Intel Corporation
7 *  All rights reserved.
8 *
9 *  @copyright
10 *  Redistribution and use in source and binary forms, with or without
11 *  modification, are permitted provided that the following conditions
12 *  are met:
13 *
14 *    * Redistributions of source code must retain the above copyright
15 *      notice, this list of conditions and the following disclaimer.
16 *    * Redistributions in binary form must reproduce the above copyright
17 *      notice, this list of conditions and the following disclaimer in
18 *      the documentation and/or other materials provided with the
19 *      distribution.
20 *    * Neither the name of Intel Corporation nor the names of its
21 *      contributors may be used to endorse or promote products derived
22 *      from this software without specific prior written permission.
23 *
24 *  @copyright
25 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
32 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
35 *  WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 *  POSSIBILITY OF SUCH DAMAGE.
37 *
38 **************************************************************************/
39
40#define __STDC_FORMAT_MACROS
41#include "config.h"
42#include <inttypes.h>
43#include <stdio.h>
44#include <string.h>
45#include <stdint.h>
46#include <stdbool.h>
47#include <signal.h>
48#include <assert.h>
49#include <stdlib.h>
50#include <sys/mman.h>
51#include <sys/prctl.h>
52#include <cpuid.h>
53#include "mpxrt-utils.h"
54
55#ifdef __i386__
56
57/* i386 directory size is 4MB */
58#define NUM_L1_BITS    20
59
60#define REG_IP_IDX      REG_EIP
61#define REX_PREFIX
62
63#define XSAVE_OFFSET_IN_FPMEM    sizeof (struct _libc_fpstate)
64
65#else /* __i386__ */
66
67/* x86_64 directory size is 2GB */
68#define NUM_L1_BITS   28
69
70#define REG_IP_IDX    REG_RIP
71#define REX_PREFIX    "0x48, "
72
73#define XSAVE_OFFSET_IN_FPMEM    0
74
75#endif /* !__i386__ */
76
77#define MPX_ENABLE_BIT_NO 0
78#define BNDPRESERVE_BIT_NO 1
79
80const size_t MPX_L1_SIZE = (1UL << NUM_L1_BITS) * sizeof (void *);
81
82struct xsave_hdr_struct
83{
84  uint64_t xstate_bv;
85  uint64_t reserved1[2];
86  uint64_t reserved2[5];
87} __attribute__ ((packed));
88
89struct bndregs_struct
90{
91  uint64_t bndregs[8];
92} __attribute__ ((packed));
93
94struct bndcsr_struct {
95	uint64_t cfg_reg_u;
96	uint64_t status_reg;
97} __attribute__((packed));
98
99struct xsave_struct
100{
101  uint8_t fpu_sse[512];
102  struct xsave_hdr_struct xsave_hdr;
103  uint8_t ymm[256];
104  uint8_t lwp[128];
105  struct bndregs_struct bndregs;
106  struct bndcsr_struct bndcsr;
107} __attribute__ ((packed));
108
109/* Following vars are initialized at process startup only
110   and thus are considered to be thread safe.  */
111static void *l1base = NULL;
112static int bndpreserve;
113static int enable = 1;
114
115/* Var holding number of occured BRs.  It is modified from
116   signal handler only and thus it should be thread safe.  */
117static uint64_t num_bnd_chk = 0;
118
119static inline void
120xrstor_state (struct xsave_struct *fx, uint64_t mask)
121{
122  uint32_t lmask = mask;
123  uint32_t hmask = mask >> 32;
124
125  asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x2f\n\t"
126		: : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
127		:   "memory");
128}
129
130static inline void
131xsave_state (struct xsave_struct *fx, uint64_t mask)
132{
133  uint32_t lmask = mask;
134  uint32_t hmask = mask >> 32;
135
136  asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x27\n\t"
137		: : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
138		:   "memory");
139}
140
141static inline uint64_t
142xgetbv (uint32_t index)
143{
144  uint32_t eax, edx;
145
146  asm volatile (".byte 0x0f,0x01,0xd0" /* xgetbv */
147		: "=a" (eax), "=d" (edx)
148		: "c" (index));
149  return eax + ((uint64_t)edx << 32);
150}
151
152static uint64_t
153read_mpx_status_sig (ucontext_t *uctxt)
154{
155  uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
156  struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
157
158  memset (buffer, 0, sizeof (buffer));
159  memcpy (buffer,
160	  (uint8_t *)uctxt->uc_mcontext.fpregs + XSAVE_OFFSET_IN_FPMEM,
161	  sizeof (struct xsave_struct));
162  return xsave_buf->bndcsr.status_reg;
163}
164
165static uint8_t *
166get_next_inst_ip (uint8_t *addr)
167{
168  uint8_t *ip = addr;
169  uint8_t  sib;
170
171  /* Determine the prefix.  */
172  switch (*ip)
173    {
174    case 0xf2:
175    case 0xf3:
176    case 0x66:
177      ip++;
178      break;
179    }
180
181  /* Look for rex prefix.  */
182  if ((*ip & 0x40) == 0x40)
183    ip++;
184
185  /* Make sure we have a MPX instruction.  */
186  if (*ip++ != 0x0f)
187    return addr;
188
189  /* Skip the op code byte.  */
190  ip++;
191
192  /* Get the moderm byte.  */
193  uint8_t modrm = *ip++;
194
195  /* Break it down into parts.  */
196  uint8_t rm = modrm & 7;
197  uint8_t mod = (modrm >> 6);
198
199  /* Init the parts of the address mode.  */
200  uint8_t base = 8;
201
202  /* Is it a mem mode?  */
203  if (mod != 3)
204    {
205      /* Look for scaled indexed addressing.  */
206      if (rm == 4)
207	{
208	  /* SIB addressing.  */
209	  sib = *ip++;
210	  base = sib & 7;
211	  switch (mod)
212	    {
213	    case 0:
214	      if (base == 5)
215		ip += 4;
216	      break;
217
218	    case 1:
219	      ip++;
220	      break;
221
222	    case 2:
223	      ip += 4;
224	      break;
225	    }
226	}
227      else
228	{
229	  /* MODRM addressing.  */
230	  switch (mod)
231	    {
232	    case 0:
233	      if (rm == 5)
234		/* DISP32 addressing, no base.  */
235		ip += 4;
236	      break;
237
238	    case 1:
239	      ip++;
240	      break;
241
242	    case 2:
243	      ip += 4;
244	      break;
245	    }
246	}
247    }
248  return ip;
249}
250
251static void
252handler (int sig __attribute__ ((unused)),
253	 siginfo_t *info __attribute__ ((unused)),
254	 void *vucontext,
255	 struct xsave_struct *buf  __attribute__ ((unused)))
256{
257  ucontext_t* uctxt;
258  greg_t trapno;
259  greg_t ip;
260
261  uctxt = vucontext;
262  trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO];
263  ip = uctxt->uc_mcontext.gregs[REG_IP_IDX];
264
265  if (trapno == 5)
266    {
267      uint64_t status = read_mpx_status_sig (uctxt);
268      uint64_t br_reason =  status & 0x3;
269
270      __mpxrt_write (VERB_BR, "Saw a #BR! status ");
271      __mpxrt_write_uint (VERB_BR, status, 10);
272      __mpxrt_write (VERB_BR, " at 0x");
273      __mpxrt_write_uint (VERB_BR, ip, 16);
274      __mpxrt_write (VERB_BR, "\n");
275
276      switch (br_reason)
277	{
278	case 1: /* traditional BR */
279	  num_bnd_chk++;
280	  uctxt->uc_mcontext.gregs[REG_IP_IDX] =
281	    (greg_t)get_next_inst_ip ((uint8_t *)ip);
282	  if (__mpxrt_mode () == MPX_RT_STOP)
283	    exit (255);
284	  return;
285
286	default:
287	  __mpxrt_write (VERB_BR, "Unexpected status with bound exception: ");
288	  __mpxrt_write_uint (VERB_BR, status, 10);
289	  __mpxrt_write (VERB_BR, "\n");
290	  break;
291	}
292    }
293  else if (trapno == 14)
294    {
295      __mpxrt_write (VERB_ERROR, "In signal handler, trapno = ");
296      __mpxrt_write_uint (VERB_ERROR, trapno, 10);
297      __mpxrt_write (VERB_ERROR, ", ip = 0x");
298      __mpxrt_write_uint (VERB_ERROR, ip, 16);
299      __mpxrt_write (VERB_ERROR, "\n");
300      exit (255);
301    }
302  else
303    {
304      __mpxrt_write (VERB_ERROR, "Unexpected trap ");
305      __mpxrt_write_uint (VERB_ERROR, trapno, 10);
306      __mpxrt_write (VERB_ERROR, "! at 0x");
307      __mpxrt_write_uint (VERB_ERROR, ip, 16);
308      __mpxrt_write (VERB_ERROR, "\n");
309      exit (255);
310    }
311}
312
313/* Using wrapper to the real handler in order to save the bnd regs
314   using xsave before any unprefixed call. an unprefixed call to
315   __i686.get_pc_thunk.bx is added by the linker in 32bit at the
316   beginning of handler function since there are references to
317   global variables.  */
318static void
319handler_wrap (int signum, siginfo_t* si, void* vucontext)
320{
321  /* Since the OS currently not handling chkptr regs.
322     We need to store them for later use. They might be
323     init due to unprefixed call,Jcc,ret. avoiding calling
324     function since the function will be unprefixed as well.  */
325  uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
326  struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
327  uint64_t mask = 0x18;
328  uint32_t lmask = mask;
329  uint32_t hmask = mask >> 32;
330
331  asm volatile (".byte " REX_PREFIX "0x0f,0xae,0x27\n\t"
332		: : "D" (xsave_buf), "m" (*xsave_buf),
333		  "a" (lmask), "d" (hmask)
334		:   "memory");
335
336  handler (signum, si, vucontext, xsave_buf);
337}
338
339static bool
340check_mpx_support (void)
341{
342  unsigned int eax, ebx, ecx, edx;
343  unsigned int max_level = __get_cpuid_max (0, NULL);
344
345  if (max_level < 13)
346    {
347      __mpxrt_print (VERB_DEBUG, "No required CPUID level support.\n");
348      return false;
349    }
350
351  __cpuid_count (0, 0, eax, ebx, ecx, edx);
352  if (!(ecx & bit_XSAVE))
353    {
354      __mpxrt_print (VERB_DEBUG, "No XSAVE support.\n");
355      return false;
356    }
357
358  if (!(ecx & bit_OSXSAVE))
359    {
360      __mpxrt_print (VERB_DEBUG, "No OSXSAVE support.\n");
361      return false;
362    }
363
364  __cpuid_count (7, 0, eax, ebx, ecx, edx);
365  if (!(ebx & bit_MPX))
366    {
367      __mpxrt_print (VERB_DEBUG, "No MPX support.\n");
368      return false;
369    }
370
371  __cpuid_count (13, 0, eax, ebx, ecx, edx);
372  if (!(eax & bit_BNDREGS))
373    {
374      __mpxrt_print (VERB_DEBUG, "No BNDREGS support.\n");
375      return false;
376    }
377
378  if (!(eax & bit_BNDCSR))
379    {
380      __mpxrt_print (VERB_DEBUG, "No BNDCSR support.\n");
381      return false;
382    }
383
384  return true;
385}
386
387static void
388enable_mpx (void)
389{
390  uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
391  struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
392
393  memset (buffer, 0, sizeof (buffer));
394  xrstor_state (xsave_buf, 0x18);
395
396  __mpxrt_print (VERB_DEBUG, "Initalizing MPX...\n");
397  __mpxrt_print (VERB_DEBUG, "  Enable bit: %d\n", enable);
398  __mpxrt_print (VERB_DEBUG, "  BNDPRESERVE bit: %d\n", bndpreserve);
399
400  /* Enable MPX.  */
401  xsave_buf->xsave_hdr.xstate_bv = 0x10;
402  xsave_buf->bndcsr.cfg_reg_u = (unsigned long)l1base;
403  xsave_buf->bndcsr.cfg_reg_u |= enable << MPX_ENABLE_BIT_NO;
404  xsave_buf->bndcsr.cfg_reg_u |= bndpreserve << BNDPRESERVE_BIT_NO;
405  xsave_buf->bndcsr.status_reg = 0;
406
407  xrstor_state (xsave_buf, 0x10);
408}
409
410static void
411disable_mpx (void)
412{
413  uint8_t __attribute__ ((__aligned__ (64))) buffer[4096];
414  struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
415
416  memset(buffer, 0, sizeof(buffer));
417  xrstor_state(xsave_buf, 0x18);
418
419  /* Disable MPX.  */
420  xsave_buf->xsave_hdr.xstate_bv = 0x10;
421  xsave_buf->bndcsr.cfg_reg_u = 0;
422  xsave_buf->bndcsr.status_reg = 0;
423
424  xrstor_state(xsave_buf, 0x10);
425}
426
427static bool
428process_specific_init (void)
429{
430  if (!check_mpx_support ())
431    return false;
432
433  l1base = mmap (NULL, MPX_L1_SIZE, PROT_READ | PROT_WRITE,
434		 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
435  if (l1base == MAP_FAILED)
436    {
437      perror ("mmap");
438      exit (EXIT_FAILURE);
439    }
440
441  enable_mpx ();
442
443  if (prctl (43, 0, 0, 0, 0))
444    {
445      __mpxrt_print (VERB_ERROR, "No MPX support\n");
446      disable_mpx ();
447      return false;
448    }
449
450  return true;
451}
452
453static bool
454process_specific_finish (void)
455{
456  if (!check_mpx_support ())
457    return false;
458
459  if (prctl (44, 0, 0, 0, 0))
460    {
461      __mpxrt_print (VERB_ERROR, "No MPX support\n");
462      return false;
463    }
464
465  munmap (l1base, MPX_L1_SIZE);
466
467  return true;
468}
469
470static void
471setup_handler (void)
472{
473  int r,rs;
474  struct sigaction newact;
475
476  /* #BR is mapped to sigsegv  */
477  int signum  = SIGSEGV;
478
479  newact.sa_handler = 0;
480  newact.sa_sigaction = handler_wrap;
481
482  /* sigset_t - signals to block while in the handler
483     get the old signal mask.  */
484  rs = sigprocmask (SIG_SETMASK, 0, &newact.sa_mask);
485  assert (rs == 0);
486
487  /* Call sa_sigaction, not sa_handler.  */
488  newact.sa_flags = SA_SIGINFO;
489  /* In case we call user's handler on SIGSEGV (not bound
490     violation exception) we want to allow bound checking
491     inside the user handler -> nested exception.  */
492  newact.sa_flags |= SA_NODEFER;
493
494  newact.sa_restorer = 0;
495  r = sigaction (signum, &newact, 0);
496  assert (r == 0);
497}
498
499/* Set constructor priority to two to make it run after the
500   constructor in sigaction.c.  */
501static void __attribute__ ((constructor (1005)))
502mpxrt_prepare (void)
503{
504  __mpxrt_init_env_vars (&bndpreserve);
505  setup_handler ();
506  process_specific_init ();
507}
508
509static void __attribute__ ((destructor))
510mpxrt_cleanup (void)
511{
512  __mpxrt_print_summary (num_bnd_chk, MPX_L1_SIZE);
513  __mpxrt_utils_free ();
514  process_specific_finish ();
515}
516