mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
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
146 lines
4.4 KiB
Go
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)
|
|
}
|
|
}
|