kvm: add the extended state information into fpu states

Otherwise, rt_sigreturn rejects it.

PiperOrigin-RevId: 537445737
This commit is contained in:
Andrei Vagin
2023-06-02 17:18:20 -07:00
committed by gVisor bot
parent 4bda0e5af3
commit ea84ec9a17
6 changed files with 120 additions and 41 deletions
+2
View File
@@ -11,9 +11,11 @@ go_library(
"fpu.go",
"fpu_amd64.go",
"fpu_amd64.s",
"fpu_amd64_unsafe.go",
"fpu_arm64.go",
"fpu_unsafe.go",
],
marshal = True,
visibility = ["//:sandbox"],
deps = [
"//pkg/cpuid",
+59 -2
View File
@@ -27,12 +27,46 @@ import (
"gvisor.dev/gvisor/pkg/sync"
)
// FPSoftwareFrame is equivalent to struct _fpx_sw_bytes, the data stored by
// Linux in bytes 464:511 of the fxsave/xsave frame.
//
// +marshal
type FPSoftwareFrame struct {
Magic1 uint32
ExtendedSize uint32
Xfeatures uint64
XstateSize uint32
Padding [7]uint32
}
// From Linux's arch/x86/include/uapi/asm/sigcontext.h.
const (
// FP_XSTATE_MAGIC1 is the value of FPSoftwareFrame.Magic1.
FP_XSTATE_MAGIC1 = 0x46505853
// FP_SW_FRAME_OFFSET is the offset of FPSoftwareFrame in the
// fxsave/xsave area.
FP_SW_FRAME_OFFSET = 464
// FP_XSTATE_MAGIC2 is the value written to the 4 bytes inserted by
// Linux after the fxsave/xsave area in the signal frame.
FP_XSTATE_MAGIC2 = 0x46505845
// FP_XSTATE_MAGIC2_SIZE is the size of FP_XSTATE_MAGIC2.
FP_XSTATE_MAGIC2_SIZE = 4
)
// From Linux's arch/x86/include/asm/fpu/types.h.
const (
// XFEATURE_MASK_FPSSE is xsave features that are always enabled in
// signal frame fpstate.
XFEATURE_MASK_FPSSE = 0x3
)
// initX86FPState (defined in asm files) sets up initial state.
func initX86FPState(data *byte, useXsave bool)
func newX86FPStateSlice() State {
size, align := cpuid.HostFeatureSet().ExtendedStateSize()
capacity := size
capacity := size + FP_XSTATE_MAGIC2_SIZE
// Always use at least 4096 bytes.
//
// For the KVM platform, this state is a fixed 4096 bytes, so make sure
@@ -41,7 +75,13 @@ func newX86FPStateSlice() State {
if capacity < 4096 {
capacity = 4096
}
return alignedBytes(capacity, align)[:size]
return alignedBytes(capacity, align)[:size+FP_XSTATE_MAGIC2_SIZE]
}
// Slice returns the byte array that contains only the fpu state. `s` has the
// fpu state and FP_XSTATE_MAGIC2.
func (s State) Slice() []byte {
return s[:len(s)-FP_XSTATE_MAGIC2_SIZE]
}
// NewState returns an initialized floating point state.
@@ -72,6 +112,23 @@ func (s *State) Reset() {
initX86FPState(&f[0], cpuid.HostFeatureSet().UseXsave())
}
var (
hostXCR0Mask uint64
hostFPSize uint
hostUseXsave bool
initHostStateOnce sync.Once
)
// InitHostState initializes host parameters.
func InitHostState() {
initHostStateOnce.Do(func() {
featureSet := cpuid.HostFeatureSet()
hostXCR0Mask = featureSet.ValidXCR0Mask()
hostUseXsave = featureSet.UseXsave()
hostFPSize, _ = featureSet.ExtendedStateSize()
})
}
// ptraceFPRegsSize is the size in bytes of Linux's user_i387_struct, the type
// manipulated by PTRACE_GETFPREGS and PTRACE_SETFPREGS on x86. Equivalently,
// ptraceFPRegsSize is the size in bytes of the x86 FXSAVE area.
+43
View File
@@ -0,0 +1,43 @@
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build amd64 || i386
// +build amd64 i386
package fpu
import (
"unsafe"
)
// PrepForHostSigframe prepare the SW reserved portion of the fxsave memory
// layout and adds FP_XSTATE_MAGIC2. It has to be called if the state is
// restored by rt_sigreturn.
//
// Look at save_xstate_epilog in the kernel sources for more details.
//
//go:nosplit
func (s State) PrepForHostSigframe() {
fpsw := (*FPSoftwareFrame)(unsafe.Pointer(&s[FP_SW_FRAME_OFFSET]))
fpsw.Magic1 = FP_XSTATE_MAGIC1
fpsw.ExtendedSize = uint32(hostFPSize) + FP_XSTATE_MAGIC2_SIZE
fpsw.Xfeatures = XFEATURE_MASK_FPSSE | hostXCR0Mask
fpsw.XstateSize = uint32(hostFPSize)
if !hostUseXsave {
return
}
*(*uint32)(unsafe.Pointer(&s[hostFPSize])) = FP_XSTATE_MAGIC2
}
+13 -39
View File
@@ -26,6 +26,7 @@ import (
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -83,35 +84,6 @@ type UContext64 struct {
Sigset linux.SignalSet
}
// FPSoftwareFrame is equivalent to struct _fpx_sw_bytes, the data stored by
// Linux in bytes 464:511 of the fxsave/xsave frame.
//
// +marshal
type FPSoftwareFrame struct {
Magic1 uint32
ExtendedSize uint32
Xfeatures uint64
XstateSize uint32
Padding [7]uint32
}
// From Linux's arch/x86/include/uapi/asm/sigcontext.h.
const (
// Value of FPSoftwareFrame.Magic1.
_FP_XSTATE_MAGIC1 = 0x46505853
// Value written to the 4 bytes inserted by Linux after the fxsave/xsave
// area in the signal frame.
_FP_XSTATE_MAGIC2 = 0x46505845
_FP_XSTATE_MAGIC2_SIZE = 4
)
// From Linux's arch/x86/include/asm/fpu/types.h.
const (
// xsave features that are always enabled in signal frame fpstate.
_XFEATURE_MASK_FPSSE = 0x3
)
// SignalSetup implements Context.SignalSetup. (Compare to Linux's
// arch/x86/kernel/signal.c:__setup_rt_frame().)
func (c *Context64) SignalSetup(st *Stack, act *linux.SigAction, info *linux.SignalInfo, alt *linux.SignalStack, sigset linux.SignalSet, featureSet cpuid.FeatureSet) error {
@@ -134,7 +106,7 @@ func (c *Context64) SignalSetup(st *Stack, act *linux.SigAction, info *linux.Sig
// We expect support for at least FXSAVE.
fpSize = 512
}
fpSize += _FP_XSTATE_MAGIC2_SIZE
fpSize += fpu.FP_XSTATE_MAGIC2_SIZE
fpStart := (sp - hostarch.Addr(fpSize)) & ^hostarch.Addr(fpAlign-1)
// Construct the UContext64 now since we need its size.
@@ -195,28 +167,29 @@ func (c *Context64) SignalSetup(st *Stack, act *linux.SigAction, info *linux.Sig
return unix.EFAULT
}
fpState := c.fpState.Slice()
// Set up floating point state on the stack. Compare Linux's
// arch/x86/kernel/fpu/signal.c:copy_fpstate_to_sigframe().
if _, err := st.IO.CopyOut(context.Background(), fpStart, c.fpState[:464], usermem.IOOpts{}); err != nil {
if _, err := st.IO.CopyOut(context.Background(), fpStart, fpState[:fpu.FP_SW_FRAME_OFFSET], usermem.IOOpts{}); err != nil {
return err
}
fpsw := FPSoftwareFrame{
Magic1: _FP_XSTATE_MAGIC1,
fpsw := fpu.FPSoftwareFrame{
Magic1: fpu.FP_XSTATE_MAGIC1,
ExtendedSize: uint32(fpSize),
Xfeatures: _XFEATURE_MASK_FPSSE | featureSet.ValidXCR0Mask(),
XstateSize: uint32(fpSize) - _FP_XSTATE_MAGIC2_SIZE,
Xfeatures: fpu.XFEATURE_MASK_FPSSE | featureSet.ValidXCR0Mask(),
XstateSize: uint32(fpSize) - fpu.FP_XSTATE_MAGIC2_SIZE,
}
st.Bottom = fpStart + 512
if _, err := fpsw.CopyOut(st, StackBottomMagic); err != nil {
return err
}
if len(c.fpState) > 512 {
if _, err := st.IO.CopyOut(context.Background(), fpStart+512, c.fpState[512:], usermem.IOOpts{}); err != nil {
if len(fpState) > 512 {
if _, err := st.IO.CopyOut(context.Background(), fpStart+512, fpState[512:], usermem.IOOpts{}); err != nil {
return err
}
}
st.Bottom = fpStart + hostarch.Addr(fpSize)
if _, err := primitive.CopyUint32Out(st, StackBottomMagic, _FP_XSTATE_MAGIC2); err != nil {
if _, err := primitive.CopyUint32Out(st, StackBottomMagic, fpu.FP_XSTATE_MAGIC2); err != nil {
return err
}
@@ -304,7 +277,8 @@ func (c *Context64) SignalRestore(st *Stack, rt bool, featureSet cpuid.FeatureSe
if uc.MContext.Fpstate == 0 {
c.fpState.Reset()
} else {
if _, err := st.IO.CopyIn(context.Background(), hostarch.Addr(uc.MContext.Fpstate), c.fpState, usermem.IOOpts{}); err != nil {
fpState := c.fpState.Slice()
if _, err := st.IO.CopyIn(context.Background(), hostarch.Addr(uc.MContext.Fpstate), fpState, usermem.IOOpts{}); err != nil {
c.fpState.Reset()
return 0, linux.SignalStack{}, err
}
@@ -162,6 +162,7 @@ func bluepillArchExit(c *vCPU, context *arch.SignalContext64) {
context.Rip = regs.Rip
context.Eflags = regs.Eflags
c.FloatingPointState().PrepForHostSigframe()
// Set the context pointer to the saved floating point state. This is
// where the guest data has been serialized, the kernel will restore
// from this new pointer value.
+2
View File
@@ -20,6 +20,7 @@ package kvm
import (
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
)
// userRegs represents KVM user registers.
@@ -219,6 +220,7 @@ func (c *cpuidEntries) Set(in cpuid.In, out cpuid.Out) {
// updateGlobalOnce does global initialization. It has to be called only once.
func updateGlobalOnce(fd int) error {
fpu.InitHostState()
bitsForScaling = getBitsForScaling()
if err := updateSystemValues(int(fd)); err != nil {
return err