Files
Konstantin BogomolovandgVisor bot d5bf6cb7e4 Fix ring0 compatibility with Go toolchain ABI wrappers.
Initialize FPU and XCR0 in assembly before entering Go code, switch %rip
to the lower half before calling kernel syscall and exception
trampolines.

In recent Go toolchains, the compiler-generated ABI0 wrappers that bridge
assembly and internal ABI Go code zero X15. The wrapper performs a
RIP-relative load of runtime.x86HasAVX to see if AVX is available on the
host, and executes VXORPS.

This introduces two requirements in ring0:
1. When booting a vCPU, XCR0 only enables x87 state by default. Executing
   VXORPS in startGo.abi0 triggers a #UD fault if AVX is not enabled in
   XCR0. Moving fninit and xsetbv into assembly before calling startGo
   ensures AVX state is enabled before the wrapper runs.
2. The syscall and exception entrypoints execute in the upper half of the
   virtual address space where data pages are not mapped. The RIP-relative
   load of runtime.x86HasAVX faults unless we transition %rip to the lower
   half via jumpToUser before calling into Go trampolines.

PiperOrigin-RevId: 974188468
2026-08-31 18:21:37 -07:00

146 lines
4.4 KiB
Go

// Copyright 2018 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
// +build amd64
package ring0
import (
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/hostarch"
)
// fxrstor restores floating point state.
func fxrstor(addr uintptr)
// xrstor restores floating point state.
func xrstor(addr uintptr)
// fxsave saves floating point state.
func fxsave(addr uintptr)
// xsave saves floating point state.
func xsave(addr uintptr)
// xsaveopt saves floating point state.
func xsaveopt(addr uintptr)
// writeFS sets the FS base address (selects one of wrfsbase or wrfsmsr).
func writeFS(addr uintptr)
// wrfsbase writes to the GS base address.
func wrfsbase(addr uintptr)
// wrfsmsr writes to the GS_BASE MSR.
func wrfsmsr(addr uintptr)
// writeGS sets the GS address (selects one of wrgsbase or wrgsmsr).
func writeGS(addr uintptr)
// wrgsbase writes to the GS base address.
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
// xgetbv reads an extended control register.
func xgetbv(reg uintptr) uintptr
// wrmsr reads to the given MSR.
func wrmsr(reg, value uintptr)
// rdmsr reads the given MSR.
func rdmsr(reg uintptr) uintptr
// Mostly-constants set by Init.
var (
hasSMEP bool
hasSMAP bool
hasUMIP bool
hasPCID bool
hasXSAVEOPT bool
hasXSAVE bool
hasFSGSBASE bool
hasLA57 bool
validXCR0Mask uintptr
localXCR0 uintptr
)
// Init sets function pointers based on architectural features.
//
// This must be called prior to using ring0. It may be called with the
// auto-detected feature set using InitDefault. It may also be called at
// another time with a different FeatureSet.
func Init(fs cpuid.FeatureSet) {
// Initialize all sizes.
VirtualAddressBits = uintptr(fs.VirtualAddressBits())
if !fs.HasFeature(cpuid.X86FeatureLA57) && VirtualAddressBits > 48 {
VirtualAddressBits = 48
}
if PhysicalAddressBits == 0 {
PhysicalAddressBits = uintptr(fs.PhysicalAddressBits())
}
UserspaceSize = uintptr(1) << (VirtualAddressBits - 1)
MaximumUserAddress = (UserspaceSize - 1) & ^uintptr(hostarch.PageSize-1)
KernelStartAddress = ^uintptr(0) - (UserspaceSize - 1)
// Initialize all functions.
hasSMEP = fs.HasFeature(cpuid.X86FeatureSMEP)
hasSMAP = fs.HasFeature(cpuid.X86FeatureSMAP)
hasUMIP = fs.HasFeature(cpuid.X86FeatureUMIP)
hasPCID = fs.HasFeature(cpuid.X86FeaturePCID)
hasXSAVEOPT = fs.UseXsaveopt()
hasXSAVE = fs.UseXsave()
hasFSGSBASE = fs.HasFeature(cpuid.X86FeatureFSGSBase)
hasLA57 = fs.HasFeature(cpuid.X86FeatureLA57)
validXCR0Mask = uintptr(fs.ValidXCR0Mask())
if hasXSAVE {
XCR0DisabledMask := uintptr((1 << 9) | (1 << 17) | (1 << 18))
localXCR0 = xgetbv(0) &^ XCR0DisabledMask
}
}
// InitDefault initializes ring0 with the auto-detected host feature set.
func InitDefault() {
cpuid.Initialize()
Init(cpuid.HostFeatureSet())
}
// DisableLA57 forces ring0 to behave as if the host CPU did not advertise
// 5-level paging: hasLA57 is cleared so CR4.LA57 stays 0, and the address-
// space sizes are clamped to a 4-level layout (48-bit VA, 2^47 userspace).
//
// Must be called after Init/InitDefault and before any vCPU loads CR4 or
// any PageTables are created. Use this if the platform's hardware-
// virtualization layer cannot follow a 5-level page table walk (e.g. an
// EPT implementation limited to 4 levels) regardless of host CPUID.
func DisableLA57() {
hasLA57 = false
if VirtualAddressBits > 48 {
VirtualAddressBits = 48
UserspaceSize = uintptr(1) << (VirtualAddressBits - 1)
MaximumUserAddress = (UserspaceSize - 1) &^ uintptr(hostarch.PageSize-1)
KernelStartAddress = ^uintptr(0) - (UserspaceSize - 1)
}
}