mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Merge pull request #5767 from avagin:mxcsr
PiperOrigin-RevId: 367730917
This commit is contained in:
@@ -250,6 +250,7 @@ func (c *CPU) SwitchToUser(switchOpts SwitchOpts) (vector Vector) {
|
||||
}
|
||||
SaveFloatingPoint(switchOpts.FloatingPointState.BytePointer()) // escapes: no. Copy out floating point.
|
||||
WriteFS(uintptr(c.registers.Fs_base)) // escapes: no. Restore kernel FS.
|
||||
RestoreKernelFPState() // escapes: no. Restore kernel MXCSR.
|
||||
return
|
||||
}
|
||||
|
||||
@@ -321,3 +322,21 @@ func SetCPUIDFaulting(on bool) bool {
|
||||
func ReadCR2() uintptr {
|
||||
return readCR2()
|
||||
}
|
||||
|
||||
// kernelMXCSR is the value of the mxcsr register in the Sentry.
|
||||
//
|
||||
// The MXCSR control configuration is initialized once and never changed. Look
|
||||
// at src/cmd/compile/abi-internal.md in the golang sources for more details.
|
||||
var kernelMXCSR uint32
|
||||
|
||||
// RestoreKernelFPState restores the Sentry floating point state.
|
||||
//
|
||||
//go:nosplit
|
||||
func RestoreKernelFPState() {
|
||||
// Restore the MXCSR control configuration.
|
||||
ldmxcsr(&kernelMXCSR)
|
||||
}
|
||||
|
||||
func init() {
|
||||
stmxcsr(&kernelMXCSR)
|
||||
}
|
||||
|
||||
@@ -89,3 +89,9 @@ func (c *CPU) SwitchToUser(switchOpts SwitchOpts) (vector Vector) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RestoreKernelFPState restores the Sentry floating point state.
|
||||
//
|
||||
//go:nosplit
|
||||
func RestoreKernelFPState() {
|
||||
}
|
||||
|
||||
@@ -61,6 +61,12 @@ func wrgsbase(addr uintptr)
|
||||
// wrgsmsr writes to the GS_BASE MSR.
|
||||
func wrgsmsr(addr uintptr)
|
||||
|
||||
// stmxcsr reads the MXCSR control and status register.
|
||||
func stmxcsr(addr *uint32)
|
||||
|
||||
// ldmxcsr writes to the MXCSR control and status register.
|
||||
func ldmxcsr(addr *uint32)
|
||||
|
||||
// readCR2 reads the current CR2 value.
|
||||
func readCR2() uintptr
|
||||
|
||||
|
||||
@@ -198,3 +198,15 @@ TEXT ·rdmsr(SB),NOSPLIT,$0-16
|
||||
MOVL AX, ret+8(FP)
|
||||
MOVL DX, ret+12(FP)
|
||||
RET
|
||||
|
||||
// stmxcsr reads the MXCSR control and status register.
|
||||
TEXT ·stmxcsr(SB),NOSPLIT,$0-8
|
||||
MOVQ addr+0(FP), SI
|
||||
STMXCSR (SI)
|
||||
RET
|
||||
|
||||
// ldmxcsr writes to the MXCSR control and status register.
|
||||
TEXT ·ldmxcsr(SB),NOSPLIT,$0-8
|
||||
MOVQ addr+0(FP), SI
|
||||
LDMXCSR (SI)
|
||||
RET
|
||||
|
||||
@@ -219,6 +219,11 @@ func (s *State) PtraceSetXstateRegs(src io.Reader, maxlen int, featureSet *cpuid
|
||||
return copy(*s, f), nil
|
||||
}
|
||||
|
||||
// SetMXCSR sets the MXCSR control/status register in the state.
|
||||
func (s *State) SetMXCSR(mxcsr uint32) {
|
||||
hostarch.ByteOrder.PutUint32((*s)[mxcsrOffset:], mxcsr)
|
||||
}
|
||||
|
||||
// BytePointer returns a pointer to the first byte of the state.
|
||||
//
|
||||
//go:nosplit
|
||||
|
||||
@@ -65,6 +65,7 @@ go_test(
|
||||
name = "kvm_test",
|
||||
srcs = [
|
||||
"kvm_amd64_test.go",
|
||||
"kvm_amd64_test.s",
|
||||
"kvm_arm64_test.go",
|
||||
"kvm_test.go",
|
||||
"virtual_map_test.go",
|
||||
|
||||
@@ -49,3 +49,40 @@ func TestSegments(t *testing.T) {
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// stmxcsr reads the MXCSR control and status register.
|
||||
func stmxcsr(addr *uint32)
|
||||
|
||||
func TestMXCSR(t *testing.T) {
|
||||
applicationTest(t, true, testutil.SyscallLoop, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
|
||||
var si arch.SignalInfo
|
||||
switchOpts := ring0.SwitchOpts{
|
||||
Registers: regs,
|
||||
FloatingPointState: &dummyFPState,
|
||||
PageTables: pt,
|
||||
FullRestore: true,
|
||||
}
|
||||
|
||||
const mxcsrControllMask = uint32(0x1f80)
|
||||
mxcsrBefore := uint32(0)
|
||||
mxcsrAfter := uint32(0)
|
||||
stmxcsr(&mxcsrBefore)
|
||||
if mxcsrBefore == 0 {
|
||||
// goruntime sets mxcsr to 0x1f80 and it never changes
|
||||
// the control configuration.
|
||||
panic("mxcsr is zero")
|
||||
}
|
||||
switchOpts.FloatingPointState.SetMXCSR(0)
|
||||
if _, err := c.SwitchToUser(
|
||||
switchOpts, &si); err == platform.ErrContextInterrupt {
|
||||
return true // Retry.
|
||||
} else if err != nil {
|
||||
t.Errorf("application syscall failed: %v", err)
|
||||
}
|
||||
stmxcsr(&mxcsrAfter)
|
||||
if mxcsrAfter&mxcsrControllMask != mxcsrBefore&mxcsrControllMask {
|
||||
t.Errorf("mxcsr = %x (expected %x)", mxcsrBefore, mxcsrAfter)
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// stmxcsr reads the MXCSR control and status register.
|
||||
TEXT ·stmxcsr(SB),NOSPLIT,$0-8
|
||||
MOVQ addr+0(FP), SI
|
||||
STMXCSR (SI)
|
||||
RET
|
||||
@@ -44,6 +44,8 @@ namespace {
|
||||
#define SET_FP0(var) SET_FPREG(var, d0)
|
||||
#endif
|
||||
|
||||
#define DEFAULT_MXCSR 0x1f80
|
||||
|
||||
int parent, child;
|
||||
|
||||
void sigusr1(int s, siginfo_t* siginfo, void* _uc) {
|
||||
@@ -57,6 +59,12 @@ void sigusr1(int s, siginfo_t* siginfo, void* _uc) {
|
||||
uint64_t got;
|
||||
GET_FP0(got);
|
||||
TEST_CHECK_MSG(val == got, "Basic FP check failed in sigusr1()");
|
||||
|
||||
#ifdef __x86_64
|
||||
uint32_t mxcsr;
|
||||
__asm__("STMXCSR %0" : "=m"(mxcsr));
|
||||
TEST_CHECK_MSG(mxcsr == DEFAULT_MXCSR, "Unexpected mxcsr");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(FPSigTest, Fork) {
|
||||
@@ -125,6 +133,55 @@ TEST(FPSigTest, Fork) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
TEST(FPSigTest, ForkWithZeroMxcsr) {
|
||||
parent = getpid();
|
||||
pid_t parent_tid = gettid();
|
||||
|
||||
struct sigaction sa = {};
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_sigaction = sigusr1;
|
||||
ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds());
|
||||
|
||||
// The control bits of the MXCSR register are callee-saved (preserved across
|
||||
// calls), while the status bits are caller-saved (not preserved).
|
||||
uint32_t expected = 0, origin;
|
||||
__asm__("STMXCSR %0" : "=m"(origin));
|
||||
__asm__("LDMXCSR %0" : : "m"(expected));
|
||||
|
||||
asm volatile(
|
||||
"movl %[killnr], %%eax;"
|
||||
"movl %[parent], %%edi;"
|
||||
"movl %[tid], %%esi;"
|
||||
"movl %[sig], %%edx;"
|
||||
"syscall;"
|
||||
:
|
||||
: [killnr] "i"(__NR_tgkill), [parent] "rm"(parent),
|
||||
[tid] "rm"(parent_tid), [sig] "i"(SIGUSR1)
|
||||
: "rax", "rdi", "rsi", "rdx",
|
||||
// Clobbered by syscall.
|
||||
"rcx", "r11");
|
||||
|
||||
uint32_t got;
|
||||
__asm__("STMXCSR %0" : "=m"(got));
|
||||
__asm__("LDMXCSR %0" : : "m"(origin));
|
||||
|
||||
if (getpid() == parent) { // Parent.
|
||||
int status;
|
||||
ASSERT_THAT(waitpid(child, &status, 0), SyscallSucceedsWithValue(child));
|
||||
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
}
|
||||
|
||||
// TEST_CHECK_MSG since this may run in the child.
|
||||
TEST_CHECK_MSG(expected == got, "Bad mxcsr value");
|
||||
|
||||
if (getpid() != parent) { // Child.
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
|
||||
Reference in New Issue
Block a user