mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
kvm: add the extended state information into fpu states
Otherwise, rt_sigreturn rejects it. PiperOrigin-RevId: 537445737
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user