Merge pull request #5767 from avagin:mxcsr

PiperOrigin-RevId: 367730917
This commit is contained in:
gVisor bot
2021-04-09 17:15:06 -07:00
9 changed files with 164 additions and 0 deletions
+19
View File
@@ -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)
}
+6
View File
@@ -89,3 +89,9 @@ func (c *CPU) SwitchToUser(switchOpts SwitchOpts) (vector Vector) {
return
}
// RestoreKernelFPState restores the Sentry floating point state.
//
//go:nosplit
func RestoreKernelFPState() {
}
+6
View File
@@ -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
+12
View File
@@ -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
+5
View File
@@ -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
+1
View File
@@ -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",
+37
View File
@@ -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
})
}
+21
View File
@@ -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
+57
View File
@@ -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