diff options
author | Robert Mustacchi <rm@joyent.com> | 2018-06-13 18:40:29 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2018-06-19 19:34:37 +0000 |
commit | a32a1f376ed9360264e4a374608fdcc5c4927d63 (patch) | |
tree | 2c9b2e15ee4317197810c88f06b08035873e6f16 | |
parent | d0158222a5936ac26b7a03241b7d2df18cc544c8 (diff) | |
download | illumos-joyent-a32a1f376ed9360264e4a374608fdcc5c4927d63.tar.gz |
9597 Want hypervisor API for FPU management
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: John Levon <john.levon@joyent.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
-rw-r--r-- | usr/src/uts/i86pc/Makefile.files | 1 | ||||
-rw-r--r-- | usr/src/uts/i86pc/os/hma_fpu.c | 225 | ||||
-rw-r--r-- | usr/src/uts/i86pc/sys/hma.h | 103 | ||||
-rw-r--r-- | usr/src/uts/intel/ia32/os/archdep.c | 5 | ||||
-rw-r--r-- | usr/src/uts/intel/sys/fp.h | 3 |
5 files changed, 333 insertions, 4 deletions
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files index d6eee2b807..b63d059c06 100644 --- a/usr/src/uts/i86pc/Makefile.files +++ b/usr/src/uts/i86pc/Makefile.files @@ -64,6 +64,7 @@ CORE_OBJS += \ hardclk.o \ hat_i86.o \ hat_kdi.o \ + hma_fpu.o \ hment.o \ hold_page.o \ hrtimers.o \ diff --git a/usr/src/uts/i86pc/os/hma_fpu.c b/usr/src/uts/i86pc/os/hma_fpu.c new file mode 100644 index 0000000000..14cfa8baed --- /dev/null +++ b/usr/src/uts/i86pc/os/hma_fpu.c @@ -0,0 +1,225 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2018, Joyent, Inc. + */ + +/* + * This implements the hypervisor multiplexor FPU API. Its purpose is to make it + * easy to switch between the host and guest hypervisor while hiding all the + * details about CR0.TS and how to save the host's state as required. + */ + +#include <sys/pcb.h> +#include <sys/kmem.h> +#include <sys/debug.h> +#include <sys/cmn_err.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/hma.h> +#include <sys/x86_archext.h> +#include <sys/archsystm.h> + +struct hma_fpu { + fpu_ctx_t hf_guest_fpu; + kthread_t *hf_curthread; + boolean_t hf_inguest; +}; + +int +hma_fpu_init(hma_fpu_t *fpu) +{ + struct xsave_state *xs; + + ASSERT0(fpu->hf_inguest); + + switch (fp_save_mech) { + case FP_FXSAVE: + bcopy(&sse_initial, fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_fx, + sizeof (struct fxsave_state)); + fpu->hf_guest_fpu.fpu_xsave_mask = 0; + break; + case FP_XSAVE: + /* + * Zero everything in the xsave case as we may have data in + * the structure that's not part of the initial value (which + * only really deals with a small portion of the xsave state). + */ + xs = fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_xs; + bzero(xs, cpuid_get_xsave_size()); + bcopy(&avx_initial, xs, sizeof (*xs)); + xs->xs_xstate_bv = XFEATURE_LEGACY_FP | XFEATURE_SSE; + fpu->hf_guest_fpu.fpu_xsave_mask = XFEATURE_FP_ALL; + break; + default: + panic("Invalid fp_save_mech"); + } + + fpu->hf_guest_fpu.fpu_flags = FPU_EN | FPU_VALID; + + return (0); +} + +void +hma_fpu_free(hma_fpu_t *fpu) +{ + if (fpu == NULL) + return; + + ASSERT3P(fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_generic, !=, NULL); + kmem_cache_free(fpsave_cachep, + fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_generic); + kmem_free(fpu, sizeof (*fpu)); +} + +hma_fpu_t * +hma_fpu_alloc(int kmflag) +{ + hma_fpu_t *fpu; + + fpu = kmem_zalloc(sizeof (hma_fpu_t), kmflag); + if (fpu == NULL) + return (NULL); + + fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_generic = + kmem_cache_alloc(fpsave_cachep, kmflag); + if (fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_generic == NULL) { + kmem_free(fpu, sizeof (hma_fpu_t)); + return (NULL); + } + fpu->hf_inguest = B_FALSE; + + /* + * Make sure the entire structure is zero. + */ + switch (fp_save_mech) { + case FP_FXSAVE: + bzero(fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_generic, + sizeof (struct fxsave_state)); + break; + case FP_XSAVE: + bzero(fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_generic, + cpuid_get_xsave_size()); + break; + default: + panic("Invalid fp_save_mech"); + } + + return (fpu); +} + +void +hma_fpu_start_guest(hma_fpu_t *fpu) +{ + /* + * Note, we don't check / assert whether or not t_prempt is true because + * there are contexts where this is safe to call (from a context op) + * where t_preempt may not be set. + */ + ASSERT3S(fpu->hf_inguest, ==, B_FALSE); + ASSERT3P(fpu->hf_curthread, ==, NULL); + ASSERT3P(curthread->t_lwp, !=, NULL); + ASSERT3U(fpu->hf_guest_fpu.fpu_flags & FPU_EN, !=, 0); + ASSERT3U(fpu->hf_guest_fpu.fpu_flags & FPU_VALID, !=, 0); + + fpu->hf_inguest = B_TRUE; + fpu->hf_curthread = curthread; + + + fp_save(&curthread->t_lwp->lwp_pcb.pcb_fpu); + fp_restore(&fpu->hf_guest_fpu); + fpu->hf_guest_fpu.fpu_flags &= ~FPU_VALID; +} + +void +hma_fpu_stop_guest(hma_fpu_t *fpu) +{ + ASSERT3S(fpu->hf_inguest, ==, B_TRUE); + ASSERT3P(fpu->hf_curthread, ==, curthread); + ASSERT3U(fpu->hf_guest_fpu.fpu_flags & FPU_EN, !=, 0); + ASSERT3U(fpu->hf_guest_fpu.fpu_flags & FPU_VALID, ==, 0); + + /* + * Note, we can't use fp_save because it assumes that we're saving to + * the thread's PCB and not somewhere else. Because this is a different + * FPU context, we instead have to do this ourselves. + */ + switch (fp_save_mech) { + case FP_FXSAVE: + fpxsave(fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_fx); + break; + case FP_XSAVE: + xsavep(fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_xs, + fpu->hf_guest_fpu.fpu_xsave_mask); + break; + default: + panic("Invalid fp_save_mech"); + /*NOTREACHED*/ + } + fpu->hf_guest_fpu.fpu_flags |= FPU_VALID; + + fp_restore(&curthread->t_lwp->lwp_pcb.pcb_fpu); + + fpu->hf_inguest = B_FALSE; + fpu->hf_curthread = NULL; +} + +void +hma_fpu_get_fxsave_state(const hma_fpu_t *fpu, struct fxsave_state *fx) +{ + const struct fxsave_state *guest; + + ASSERT3S(fpu->hf_inguest, ==, B_FALSE); + + guest = fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_fx; + bcopy(guest, fx, sizeof (*fx)); +} + +int +hma_fpu_set_fxsave_state(hma_fpu_t *fpu, const struct fxsave_state *fx) +{ + struct fxsave_state *gfx; + struct xsave_state *gxs; + + ASSERT3S(fpu->hf_inguest, ==, B_FALSE); + + /* + * If reserved bits are set in fx_mxcsr, then we will take a #GP when + * we restore them. Reject this outright. + * + * We do not need to check if we are dealing with state that has pending + * exceptions. This was only the case with the original FPU save and + * restore mechanisms (fsave/frstor). When using fxsave/fxrstor and + * xsave/xrstor they will be deferred to the user using the FPU, which + * is what we'd want here (they'd be used in guest context). + */ + if ((fx->fx_mxcsr & ~sse_mxcsr_mask) != 0) + return (EINVAL); + + switch (fp_save_mech) { + case FP_FXSAVE: + gfx = fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_fx; + bcopy(fx, gfx, sizeof (*fx)); + break; + case FP_XSAVE: + gxs = fpu->hf_guest_fpu.fpu_regs.kfpu_u.kfpu_xs; + bzero(gxs, cpuid_get_xsave_size()); + bcopy(fx, &gxs->xs_fxsave, sizeof (*fx)); + gxs->xs_xstate_bv = XFEATURE_LEGACY_FP | XFEATURE_SSE; + break; + default: + panic("Invalid fp_save_mech"); + /* NOTREACHED */ + } + + return (0); +} diff --git a/usr/src/uts/i86pc/sys/hma.h b/usr/src/uts/i86pc/sys/hma.h new file mode 100644 index 0000000000..00009cf439 --- /dev/null +++ b/usr/src/uts/i86pc/sys/hma.h @@ -0,0 +1,103 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2018, Joyent, Inc. + */ + +#ifndef _SYS_HMA_H +#define _SYS_HMA_H + +/* + * Hypervisor Multiplexor API + * + * This provides a set of APIs that are usable by hypervisor implementations + * that allows them to coexist and to make sure that they are all in a + * consistent state. + */ + +#include <sys/fp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * FPU related management. These functions provide a set of APIs to manage the + * FPU state and switch between host and guest management of this state. + */ + +typedef struct hma_fpu hma_fpu_t; + +/* + * Allocate and free FPU state management structures. + */ +extern hma_fpu_t *hma_fpu_alloc(int); +extern void hma_fpu_free(hma_fpu_t *); + +/* + * Resets the FPU to the standard x86 default state. This should be called after + * allocation and whenever the guest needs to logically reset the state (when + * the CPU is reset, etc.). If the system supports xsave, then the xbv state + * will be set to have the x87 and SSE portions as valid and the rest will be + * set to their initial states (regardless of whether or not they will be + * advertised in the host). + */ +extern int hma_fpu_init(hma_fpu_t *); + +/* + * Save the current host's FPU state and restore the guest's state in the FPU. + * At this point, CR0.TS will not be set. The caller must not use the FPU in any + * way before entering the guest. + * + * This should be used in normal operation before entering the guest. It should + * also be used in a thread context operation when the thread is being scheduled + * again. This interface has an implicit assumption that a given guest state + * will be mapped to only one specific OS thread at any given time. + * + * This must be called with preemption disabled. + */ +extern void hma_fpu_start_guest(hma_fpu_t *); + +/* + * Save the current guest's FPU state and restore the host's state in the FPU. + * By the time the thread returns to userland, the FPU will be in a usable + * state; however, the FPU will not be usable while inside the kernel (CR0.TS + * will be set). + * + * This should be used in normal operation after leaving the guest and returning + * to user land. It should also be used in a thread context operation when the + * thread is being descheduled. Like the hma_fpu_start_guest() interface, this + * interface has an implicit assumption that a given guest state will be mapped + * to only a single OS thread at any given time. + * + * This must be called with preemption disabled. + */ +extern void hma_fpu_stop_guest(hma_fpu_t *); + +/* + * Get and set the contents of the FPU save area. This sets the fxsave style + * information. In all cases when this is in use, if an XSAVE state is actually + * used by the host, then this will end up zeroing all of the non-fxsave state + * and it will reset the xbv to indicate that the legacy x87 and SSE portions + * are valid. + * + * These functions cannot be called while the FPU is in use by the guest. It is + * up to callers to guarantee this fact. + */ +extern void hma_fpu_get_fxsave_state(const hma_fpu_t *, struct fxsave_state *); +extern int hma_fpu_set_fxsave_state(hma_fpu_t *, const struct fxsave_state *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_HMA_H */ diff --git a/usr/src/uts/intel/ia32/os/archdep.c b/usr/src/uts/intel/ia32/os/archdep.c index fb4267665b..6f99571a5a 100644 --- a/usr/src/uts/intel/ia32/os/archdep.c +++ b/usr/src/uts/intel/ia32/os/archdep.c @@ -25,7 +25,7 @@ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* - * Copyright 2017 Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ @@ -68,9 +68,6 @@ #include <sys/machbrand.h> #include <sys/cmn_err.h> -extern const struct fnsave_state x87_initial; -extern const struct fxsave_state sse_initial; - /* * Map an fnsave-formatted save area into an fxsave-formatted save area. * diff --git a/usr/src/uts/intel/sys/fp.h b/usr/src/uts/intel/sys/fp.h index 7f08f8c8f8..fe5471e855 100644 --- a/usr/src/uts/intel/sys/fp.h +++ b/usr/src/uts/intel/sys/fp.h @@ -345,6 +345,9 @@ extern void fp_lwp_init(struct _klwp *); extern void fp_lwp_cleanup(struct _klwp *); extern void fp_lwp_dup(struct _klwp *); +extern const struct fxsave_state sse_initial; +extern const struct xsave_state avx_initial; + #endif /* _KERNEL */ #ifdef __cplusplus |