Files
Konstantin BogomolovandgVisor bot f1a883bdde Detect if vvar is not mappable into the guest on ARM.
This adds a check if vvar cannot be mapped into the guest in a throw-away VM on
potentially problematic host kernels.

Linux commits 2a8dfab and 0c67288 make our mapping of vvar into the guest fail
as soon as we try to create it, because it is a VM_PFNMAP region.

Not mapping vvar into the guest severely hinders gVisor performance, because if
we don't map vvar then any time-related host syscall will always force a VM
exit, for example. However, given the alternative if/until a backport is merged
into the affected kernels is to just crash, we don't have much choice.

Note that this also revealed two errors:
- in KERNEL_ENTRY_FROM_EL1, which assumed R18 would not be relevant because
  Go never uses it -- this is no longer the case with the sentry faulting while
  in the VDSO code.
- in the interrupt stack size which was sized too small and thus corrupted
  errorCode and errorType variables when it overflowed. This is a problem with
  this patch specifically, because we now generate a data abort exception every
  time we use vvar, which exposes the bug more frequently.

  This also led to a closer inspection of the EXCEPTION_EL1 macro, which
  revealed a relatively harmless bug of vector not being saved properly.

PiperOrigin-RevId: 968804355
2026-08-21 18:23:37 -07:00

102 lines
2.6 KiB
Go

// Copyright 2019 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 arm64
// +build arm64
package kvm
import (
"fmt"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/hostsyscall"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/sentry/arch"
)
type kvmOneReg struct {
id uint64
addr uint64
}
// arm64HypercallMMIOBase is MMIO base address used to dispatch hypercalls.
var arm64HypercallMMIOBase uintptr
const KVM_NR_SPSR = 5
type userFpsimdState struct {
vregs [64]uint64
fpsr uint32
fpcr uint32
reserved [2]uint32
}
type userRegs struct {
Regs arch.Registers
sp_el1 uint64
elr_el1 uint64
spsr [KVM_NR_SPSR]uint64
fpRegs userFpsimdState
}
type exception struct {
sErrPending uint8
sErrHasEsr uint8
extDabtPending uint8
pad [5]uint8
sErrEsr uint64
}
type kvmVcpuEvents struct {
exception
rsvd [12]uint32
}
// updateGlobalOnce does global initialization. It has to be called only once.
func updateGlobalOnce(fd int) error {
err := updateSystemValues(int(fd))
maxIPA, errno := hostsyscall.RawSyscall(unix.SYS_IOCTL, uintptr(fd), KVM_CHECK_EXTENSION, _KVM_CAP_ARM_VM_IPA_SIZE)
if errno == 0 && maxIPA > 0 {
if maxIPA < 40 {
ring0.PhysicalAddressBits = uintptr(maxIPA)
} else {
ring0.PhysicalAddressBits = 40
}
}
ring0.Init()
initFaultBlocks()
excludeVvarRegion = vvarMemslotUnusable(fd)
if excludeVvarRegion {
// Not a warning to make tests pass that match on not getting any warnings.
log.Infof("[vvar] region will not be mapped into the KVM guest! Performance will suffer!")
}
physicalInit()
// Explicitly configure address space for 48-bit VA.
// KVM only supports 48-bit page tables (ring0.VirtualAddressBits == 48).
arch.ConfigureAddressSpace(1 << 48)
// The linux.Task represents the possible largest task size, which the UserspaceSize shouldn't be larger than.
if linux.TaskSize < ring0.UserspaceSize {
return fmt.Errorf("gVisor doesn't support 3-level page tables on KVM platform. Try to recompile the kernel with CONFIG_ARM64_VA_BITS_48")
}
return err
}