gvisor/pkg/sentry/platform/kvm/machine_arm64.go

186 lines
4.7 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.
// +build arm64
package kvm
import (
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/platform"
"gvisor.dev/gvisor/pkg/sentry/platform/ring0/pagetables"
"gvisor.dev/gvisor/pkg/usermem"
)
type vCPUArchState struct {
// PCIDs is the set of PCIDs for this vCPU.
//
// This starts above fixedKernelPCID.
PCIDs *pagetables.PCIDs
}
const (
// fixedKernelPCID is a fixed kernel PCID used for the kernel page
// tables. We must start allocating user PCIDs above this in order to
// avoid any conflict (see below).
fixedKernelPCID = 1
// poolPCIDs is the number of PCIDs to record in the database. As this
// grows, assignment can take longer, since it is a simple linear scan.
// Beyond a relatively small number, there are likely few perform
// benefits, since the TLB has likely long since lost any translations
// from more than a few PCIDs past.
poolPCIDs = 8
)
// Get all read-only physicalRegions.
func rdonlyRegionsForSetMem() (phyRegions []physicalRegion) {
var rdonlyRegions []region
applyVirtualRegions(func(vr virtualRegion) {
if excludeVirtualRegion(vr) {
return
}
if !vr.accessType.Write && vr.accessType.Read {
rdonlyRegions = append(rdonlyRegions, vr.region)
}
})
for _, r := range rdonlyRegions {
physical, _, ok := translateToPhysical(r.virtual)
if !ok {
continue
}
phyRegions = append(phyRegions, physicalRegion{
region: region{
virtual: r.virtual,
length: r.length,
},
physical: physical,
})
}
return phyRegions
}
// Get all available physicalRegions.
func availableRegionsForSetMem() (phyRegions []physicalRegion) {
var excludeRegions []region
applyVirtualRegions(func(vr virtualRegion) {
if !vr.accessType.Write {
excludeRegions = append(excludeRegions, vr.region)
}
})
phyRegions = computePhysicalRegions(excludeRegions)
return phyRegions
}
// dropPageTables drops cached page table entries.
func (m *machine) dropPageTables(pt *pagetables.PageTables) {
m.mu.Lock()
defer m.mu.Unlock()
// Clear from all PCIDs.
for _, c := range m.vCPUs {
if c.PCIDs != nil {
c.PCIDs.Drop(pt)
}
}
}
// nonCanonical generates a canonical address return.
//
//go:nosplit
func nonCanonical(addr uint64, signal int32, info *arch.SignalInfo) (usermem.AccessType, error) {
*info = arch.SignalInfo{
Signo: signal,
Code: arch.SignalInfoKernel,
}
info.SetAddr(addr) // Include address.
return usermem.NoAccess, platform.ErrContextSignal
}
// fault generates an appropriate fault return.
//
//go:nosplit
func (c *vCPU) fault(signal int32, info *arch.SignalInfo) (usermem.AccessType, error) {
faultAddr := c.GetFaultAddr()
code, user := c.ErrorCode()
// Reset the pointed SignalInfo.
*info = arch.SignalInfo{Signo: signal}
info.SetAddr(uint64(faultAddr))
read := true
write := false
execute := true
ret := code & _ESR_ELx_FSC
switch ret {
case _ESR_SEGV_MAPERR_L0, _ESR_SEGV_MAPERR_L1, _ESR_SEGV_MAPERR_L2, _ESR_SEGV_MAPERR_L3:
info.Code = 1 //SEGV_MAPERR
read = false
write = true
execute = false
case _ESR_SEGV_ACCERR_L1, _ESR_SEGV_ACCERR_L2, _ESR_SEGV_ACCERR_L3, _ESR_SEGV_PEMERR_L1, _ESR_SEGV_PEMERR_L2, _ESR_SEGV_PEMERR_L3:
info.Code = 2 // SEGV_ACCERR.
read = true
write = false
execute = false
default:
info.Code = 2
}
if !user {
read = true
write = false
execute = true
}
accessType := usermem.AccessType{
Read: read,
Write: write,
Execute: execute,
}
return accessType, platform.ErrContextSignal
}
// retryInGuest runs the given function in guest mode.
//
// If the function does not complete in guest mode (due to execution of a
// system call due to a GC stall, for example), then it will be retried. The
// given function must be idempotent as a result of the retry mechanism.
func (m *machine) retryInGuest(fn func()) {
c := m.Get()
defer m.Put(c)
for {
c.ClearErrorCode() // See below.
bluepill(c) // Force guest mode.
fn() // Execute the given function.
_, user := c.ErrorCode()
if user {
// If user is set, then we haven't bailed back to host
// mode via a kernel exception or system call. We
// consider the full function to have executed in guest
// mode and we can return.
break
}
}
}