mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
enable kvm to support arm64
There are 4 jobs were finished in this package: 1, Virtual machine initialization. 2, Bluepill implementation. 3, Move ring0.Vectors() into the address with 11-bits alignment. 4, Basic support for "SwitchToUser". Signed-off-by: Bin Lu <bin.lu@arm.com> COPYBARA_INTEGRATE_REVIEW=https://github.com/google/gvisor/pull/919 from lubinszARM:pr_kvm eedea52db451bf62722759009a9f14c54a69c55f PiperOrigin-RevId: 285501256
This commit is contained in:
@@ -12,18 +12,26 @@ go_library(
|
||||
"bluepill_amd64.go",
|
||||
"bluepill_amd64.s",
|
||||
"bluepill_amd64_unsafe.go",
|
||||
"bluepill_arm64.go",
|
||||
"bluepill_arm64.s",
|
||||
"bluepill_arm64_unsafe.go",
|
||||
"bluepill_fault.go",
|
||||
"bluepill_unsafe.go",
|
||||
"context.go",
|
||||
"filters.go",
|
||||
"filters_amd64.go",
|
||||
"filters_arm64.go",
|
||||
"kvm.go",
|
||||
"kvm_amd64.go",
|
||||
"kvm_amd64_unsafe.go",
|
||||
"kvm_arm64.go",
|
||||
"kvm_arm64_unsafe.go",
|
||||
"kvm_const.go",
|
||||
"kvm_const_arm64.go",
|
||||
"machine.go",
|
||||
"machine_amd64.go",
|
||||
"machine_amd64_unsafe.go",
|
||||
"machine_arm64.go",
|
||||
"machine_arm64_unsafe.go",
|
||||
"machine_unsafe.go",
|
||||
"physical_map.go",
|
||||
"virtual_map.go",
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/ring0"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/safecopy"
|
||||
)
|
||||
|
||||
@@ -36,6 +37,18 @@ func sighandler()
|
||||
func dieTrampoline()
|
||||
|
||||
var (
|
||||
// bounceSignal is the signal used for bouncing KVM.
|
||||
//
|
||||
// We use SIGCHLD because it is not masked by the runtime, and
|
||||
// it will be ignored properly by other parts of the kernel.
|
||||
bounceSignal = syscall.SIGCHLD
|
||||
|
||||
// bounceSignalMask has only bounceSignal set.
|
||||
bounceSignalMask = uint64(1 << (uint64(bounceSignal) - 1))
|
||||
|
||||
// bounce is the interrupt vector used to return to the kernel.
|
||||
bounce = uint32(ring0.VirtualizationException)
|
||||
|
||||
// savedHandler is a pointer to the previous handler.
|
||||
//
|
||||
// This is called by bluepillHandler.
|
||||
@@ -45,6 +58,13 @@ var (
|
||||
dieTrampolineAddr uintptr
|
||||
)
|
||||
|
||||
// redpill invokes a syscall with -1.
|
||||
//
|
||||
//go:nosplit
|
||||
func redpill() {
|
||||
syscall.RawSyscall(^uintptr(0), 0, 0, 0)
|
||||
}
|
||||
|
||||
// dieHandler is called by dieTrampoline.
|
||||
//
|
||||
//go:nosplit
|
||||
@@ -73,8 +93,8 @@ func (c *vCPU) die(context *arch.SignalContext64, msg string) {
|
||||
|
||||
func init() {
|
||||
// Install the handler.
|
||||
if err := safecopy.ReplaceSignalHandler(syscall.SIGSEGV, reflect.ValueOf(sighandler).Pointer(), &savedHandler); err != nil {
|
||||
panic(fmt.Sprintf("Unable to set handler for signal %d: %v", syscall.SIGSEGV, err))
|
||||
if err := safecopy.ReplaceSignalHandler(bluepillSignal, reflect.ValueOf(sighandler).Pointer(), &savedHandler); err != nil {
|
||||
panic(fmt.Sprintf("Unable to set handler for signal %d: %v", bluepillSignal, err))
|
||||
}
|
||||
|
||||
// Extract the address for the trampoline.
|
||||
|
||||
@@ -24,26 +24,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// bounceSignal is the signal used for bouncing KVM.
|
||||
//
|
||||
// We use SIGCHLD because it is not masked by the runtime, and
|
||||
// it will be ignored properly by other parts of the kernel.
|
||||
bounceSignal = syscall.SIGCHLD
|
||||
|
||||
// bounceSignalMask has only bounceSignal set.
|
||||
bounceSignalMask = uint64(1 << (uint64(bounceSignal) - 1))
|
||||
|
||||
// bounce is the interrupt vector used to return to the kernel.
|
||||
bounce = uint32(ring0.VirtualizationException)
|
||||
// The action for bluepillSignal is changed by sigaction().
|
||||
bluepillSignal = syscall.SIGSEGV
|
||||
)
|
||||
|
||||
// redpill on amd64 invokes a syscall with -1.
|
||||
//
|
||||
//go:nosplit
|
||||
func redpill() {
|
||||
syscall.RawSyscall(^uintptr(0), 0, 0, 0)
|
||||
}
|
||||
|
||||
// bluepillArchEnter is called during bluepillEnter.
|
||||
//
|
||||
//go:nosplit
|
||||
|
||||
@@ -23,13 +23,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/ring0"
|
||||
)
|
||||
|
||||
// bluepillArchContext returns the arch-specific context.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillArchContext(context unsafe.Pointer) *arch.SignalContext64 {
|
||||
return &((*arch.UContext64)(context).MContext)
|
||||
}
|
||||
|
||||
// dieArchSetup initializes the state for dieTrampoline.
|
||||
//
|
||||
// The amd64 dieTrampoline requires the vCPU to be set in BX, and the last RIP
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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 (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/ring0"
|
||||
)
|
||||
|
||||
var (
|
||||
// The action for bluepillSignal is changed by sigaction().
|
||||
bluepillSignal = syscall.SIGILL
|
||||
)
|
||||
|
||||
// bluepillArchEnter is called during bluepillEnter.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillArchEnter(context *arch.SignalContext64) (c *vCPU) {
|
||||
c = vCPUPtr(uintptr(context.Regs[8]))
|
||||
regs := c.CPU.Registers()
|
||||
regs.Regs = context.Regs
|
||||
regs.Sp = context.Sp
|
||||
regs.Pc = context.Pc
|
||||
regs.Pstate = context.Pstate
|
||||
regs.Pstate &^= uint64(ring0.KernelFlagsClear)
|
||||
regs.Pstate |= ring0.KernelFlagsSet
|
||||
return
|
||||
}
|
||||
|
||||
// bluepillArchExit is called during bluepillEnter.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillArchExit(c *vCPU, context *arch.SignalContext64) {
|
||||
regs := c.CPU.Registers()
|
||||
context.Regs = regs.Regs
|
||||
context.Sp = regs.Sp
|
||||
context.Pc = regs.Pc
|
||||
context.Pstate = regs.Pstate
|
||||
context.Pstate &^= uint64(ring0.UserFlagsClear)
|
||||
context.Pstate |= ring0.UserFlagsSet
|
||||
}
|
||||
|
||||
// KernelSyscall handles kernel syscalls.
|
||||
//
|
||||
//go:nosplit
|
||||
func (c *vCPU) KernelSyscall() {
|
||||
regs := c.Registers()
|
||||
if regs.Regs[8] != ^uint64(0) {
|
||||
regs.Pc -= 4 // Rewind.
|
||||
}
|
||||
ring0.Halt()
|
||||
}
|
||||
|
||||
// KernelException handles kernel exceptions.
|
||||
//
|
||||
//go:nosplit
|
||||
func (c *vCPU) KernelException(vector ring0.Vector) {
|
||||
regs := c.Registers()
|
||||
if vector == ring0.Vector(bounce) {
|
||||
regs.Pc = 0
|
||||
}
|
||||
ring0.Halt()
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// VCPU_CPU is the location of the CPU in the vCPU struct.
|
||||
//
|
||||
// This is guaranteed to be zero.
|
||||
#define VCPU_CPU 0x0
|
||||
|
||||
// CPU_SELF is the self reference in ring0's percpu.
|
||||
//
|
||||
// This is guaranteed to be zero.
|
||||
#define CPU_SELF 0x0
|
||||
|
||||
// Context offsets.
|
||||
//
|
||||
// Only limited use of the context is done in the assembly stub below, most is
|
||||
// done in the Go handlers.
|
||||
#define SIGINFO_SIGNO 0x0
|
||||
#define CONTEXT_PC 0x1B8
|
||||
#define CONTEXT_R0 0xB8
|
||||
|
||||
// See bluepill.go.
|
||||
TEXT ·bluepill(SB),NOSPLIT,$0
|
||||
begin:
|
||||
MOVD vcpu+0(FP), R8
|
||||
MOVD $VCPU_CPU(R8), R9
|
||||
ORR $0xffff000000000000, R9, R9
|
||||
// Trigger sigill.
|
||||
// In ring0.Start(), the value of R8 will be stored into tpidr_el1.
|
||||
// When the context was loaded into vcpu successfully,
|
||||
// we will check if the value of R10 and R9 are the same.
|
||||
WORD $0xd538d08a // MRS TPIDR_EL1, R10
|
||||
check_vcpu:
|
||||
CMP R10, R9
|
||||
BEQ right_vCPU
|
||||
wrong_vcpu:
|
||||
CALL ·redpill(SB)
|
||||
B begin
|
||||
right_vCPU:
|
||||
RET
|
||||
|
||||
// sighandler: see bluepill.go for documentation.
|
||||
//
|
||||
// The arguments are the following:
|
||||
//
|
||||
// R0 - The signal number.
|
||||
// R1 - Pointer to siginfo_t structure.
|
||||
// R2 - Pointer to ucontext structure.
|
||||
//
|
||||
TEXT ·sighandler(SB),NOSPLIT,$0
|
||||
// si_signo should be sigill.
|
||||
MOVD SIGINFO_SIGNO(R1), R7
|
||||
CMPW $4, R7
|
||||
BNE fallback
|
||||
|
||||
MOVD CONTEXT_PC(R2), R7
|
||||
CMPW $0, R7
|
||||
BEQ fallback
|
||||
|
||||
MOVD R2, 8(RSP)
|
||||
BL ·bluepillHandler(SB) // Call the handler.
|
||||
|
||||
RET
|
||||
|
||||
fallback:
|
||||
// Jump to the previous signal handler.
|
||||
MOVD ·savedHandler(SB), R7
|
||||
B (R7)
|
||||
|
||||
// dieTrampoline: see bluepill.go, bluepill_arm64_unsafe.go for documentation.
|
||||
TEXT ·dieTrampoline(SB),NOSPLIT,$0
|
||||
// TODO(gvisor.dev/issue/1249): dieTrampoline supporting for Arm64.
|
||||
MOVD R9, 8(RSP)
|
||||
BL ·dieHandler(SB)
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 (
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
)
|
||||
|
||||
//go:nosplit
|
||||
func dieArchSetup(c *vCPU, context *arch.SignalContext64, guestRegs *userRegs) {
|
||||
// TODO(gvisor.dev/issue/1249): dieTrampoline supporting for Arm64.
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
)
|
||||
|
||||
//go:linkname throw runtime.throw
|
||||
@@ -49,6 +51,13 @@ func uintptrValue(addr *byte) uintptr {
|
||||
return (uintptr)(unsafe.Pointer(addr))
|
||||
}
|
||||
|
||||
// bluepillArchContext returns the UContext64.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillArchContext(context unsafe.Pointer) *arch.SignalContext64 {
|
||||
return &((*arch.UContext64)(context).MContext)
|
||||
}
|
||||
|
||||
// bluepillHandler is called from the signal stub.
|
||||
//
|
||||
// The world may be stopped while this is executing, and it executes on the
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/seccomp"
|
||||
)
|
||||
|
||||
// SyscallFilters returns syscalls made exclusively by the KVM platform.
|
||||
func (*KVM) SyscallFilters() seccomp.SyscallRules {
|
||||
return seccomp.SyscallRules{
|
||||
syscall.SYS_IOCTL: {},
|
||||
syscall.SYS_MMAP: {},
|
||||
syscall.SYS_RT_SIGSUSPEND: {},
|
||||
syscall.SYS_RT_SIGTIMEDWAIT: {},
|
||||
0xffffffffffffffff: {}, // KVM uses syscall -1 to transition to host.
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/cpuid"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/ring0"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/ring0/pagetables"
|
||||
@@ -56,9 +55,7 @@ func New(deviceFile *os.File) (*KVM, error) {
|
||||
|
||||
// Ensure global initialization is done.
|
||||
globalOnce.Do(func() {
|
||||
physicalInit()
|
||||
globalErr = updateSystemValues(int(fd))
|
||||
ring0.Init(cpuid.HostFeatureSet())
|
||||
globalErr = updateGlobalOnce(int(fd))
|
||||
})
|
||||
if globalErr != nil {
|
||||
return nil, globalErr
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/cpuid"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/ring0"
|
||||
)
|
||||
|
||||
@@ -211,3 +212,11 @@ type cpuidEntries struct {
|
||||
_ uint32
|
||||
entries [_KVM_NR_CPUID_ENTRIES]cpuidEntry
|
||||
}
|
||||
|
||||
// updateGlobalOnce does global initialization. It has to be called only once.
|
||||
func updateGlobalOnce(fd int) error {
|
||||
physicalInit()
|
||||
err := updateSystemValues(int(fd))
|
||||
ring0.Init(cpuid.HostFeatureSet())
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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 (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// userMemoryRegion is a region of physical memory.
|
||||
//
|
||||
// This mirrors kvm_memory_region.
|
||||
type userMemoryRegion struct {
|
||||
slot uint32
|
||||
flags uint32
|
||||
guestPhysAddr uint64
|
||||
memorySize uint64
|
||||
userspaceAddr uint64
|
||||
}
|
||||
|
||||
type kvmOneReg struct {
|
||||
id uint64
|
||||
addr uint64
|
||||
}
|
||||
|
||||
const KVM_NR_SPSR = 5
|
||||
|
||||
type userFpsimdState struct {
|
||||
vregs [64]uint64
|
||||
fpsr uint32
|
||||
fpcr uint32
|
||||
reserved [2]uint32
|
||||
}
|
||||
|
||||
type userRegs struct {
|
||||
Regs syscall.PtraceRegs
|
||||
sp_el1 uint64
|
||||
elr_el1 uint64
|
||||
spsr [KVM_NR_SPSR]uint64
|
||||
fpRegs userFpsimdState
|
||||
}
|
||||
|
||||
// runData is the run structure. This may be mapped for synchronous register
|
||||
// access (although that doesn't appear to be supported by my kernel at least).
|
||||
//
|
||||
// This mirrors kvm_run.
|
||||
type runData struct {
|
||||
requestInterruptWindow uint8
|
||||
_ [7]uint8
|
||||
|
||||
exitReason uint32
|
||||
readyForInterruptInjection uint8
|
||||
ifFlag uint8
|
||||
_ [2]uint8
|
||||
|
||||
cr8 uint64
|
||||
apicBase uint64
|
||||
|
||||
// This is the union data for exits. Interpretation depends entirely on
|
||||
// the exitReason above (see vCPU code for more information).
|
||||
data [32]uint64
|
||||
}
|
||||
|
||||
// updateGlobalOnce does global initialization. It has to be called only once.
|
||||
func updateGlobalOnce(fd int) error {
|
||||
physicalInit()
|
||||
err := updateSystemValues(int(fd))
|
||||
updateVectorTable()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
runDataSize int
|
||||
)
|
||||
|
||||
func updateSystemValues(fd int) error {
|
||||
// Extract the mmap size.
|
||||
sz, _, errno := syscall.RawSyscall(syscall.SYS_IOCTL, uintptr(fd), _KVM_GET_VCPU_MMAP_SIZE, 0)
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("getting VCPU mmap size: %v", errno)
|
||||
}
|
||||
// Save the data.
|
||||
runDataSize = int(sz)
|
||||
|
||||
// Success.
|
||||
return nil
|
||||
}
|
||||
@@ -49,11 +49,13 @@ const (
|
||||
_KVM_EXIT_SHUTDOWN = 0x8
|
||||
_KVM_EXIT_FAIL_ENTRY = 0x9
|
||||
_KVM_EXIT_INTERNAL_ERROR = 0x11
|
||||
_KVM_EXIT_SYSTEM_EVENT = 0x18
|
||||
)
|
||||
|
||||
// KVM capability options.
|
||||
const (
|
||||
_KVM_CAP_MAX_VCPUS = 0x42
|
||||
_KVM_CAP_MAX_VCPUS = 0x42
|
||||
_KVM_CAP_ARM_VM_IPA_SIZE = 0xa5
|
||||
)
|
||||
|
||||
// KVM limits.
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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.
|
||||
|
||||
package kvm
|
||||
|
||||
// KVM ioctls for Arm64.
|
||||
const (
|
||||
_KVM_GET_ONE_REG = 0x4010aeab
|
||||
_KVM_SET_ONE_REG = 0x4010aeac
|
||||
|
||||
_KVM_ARM_PREFERRED_TARGET = 0x8020aeaf
|
||||
_KVM_ARM_VCPU_INIT = 0x4020aeae
|
||||
_KVM_ARM64_REGS_PSTATE = 0x6030000000100042
|
||||
_KVM_ARM64_REGS_SP_EL1 = 0x6030000000100044
|
||||
_KVM_ARM64_REGS_R0 = 0x6030000000100000
|
||||
_KVM_ARM64_REGS_R1 = 0x6030000000100002
|
||||
_KVM_ARM64_REGS_R2 = 0x6030000000100004
|
||||
_KVM_ARM64_REGS_R3 = 0x6030000000100006
|
||||
_KVM_ARM64_REGS_R8 = 0x6030000000100010
|
||||
_KVM_ARM64_REGS_R18 = 0x6030000000100024
|
||||
_KVM_ARM64_REGS_PC = 0x6030000000100040
|
||||
_KVM_ARM64_REGS_MAIR_EL1 = 0x603000000013c510
|
||||
_KVM_ARM64_REGS_TCR_EL1 = 0x603000000013c102
|
||||
_KVM_ARM64_REGS_TTBR0_EL1 = 0x603000000013c100
|
||||
_KVM_ARM64_REGS_TTBR1_EL1 = 0x603000000013c101
|
||||
_KVM_ARM64_REGS_SCTLR_EL1 = 0x603000000013c080
|
||||
_KVM_ARM64_REGS_CPACR_EL1 = 0x603000000013c082
|
||||
_KVM_ARM64_REGS_VBAR_EL1 = 0x603000000013c600
|
||||
)
|
||||
|
||||
// Arm64: Architectural Feature Access Control Register EL1.
|
||||
const (
|
||||
_FPEN_NOTRAP = 0x3
|
||||
_FPEN_SHIFT = 0x20
|
||||
)
|
||||
|
||||
// Arm64: System Control Register EL1.
|
||||
const (
|
||||
_SCTLR_M = 1 << 0
|
||||
_SCTLR_C = 1 << 2
|
||||
_SCTLR_I = 1 << 12
|
||||
)
|
||||
|
||||
// Arm64: Translation Control Register EL1.
|
||||
const (
|
||||
_TCR_IPS_40BITS = 2 << 32 // PA=40
|
||||
_TCR_IPS_48BITS = 5 << 32 // PA=48
|
||||
|
||||
_TCR_T0SZ_OFFSET = 0
|
||||
_TCR_T1SZ_OFFSET = 16
|
||||
_TCR_IRGN0_SHIFT = 8
|
||||
_TCR_IRGN1_SHIFT = 24
|
||||
_TCR_ORGN0_SHIFT = 10
|
||||
_TCR_ORGN1_SHIFT = 26
|
||||
_TCR_SH0_SHIFT = 12
|
||||
_TCR_SH1_SHIFT = 28
|
||||
_TCR_TG0_SHIFT = 14
|
||||
_TCR_TG1_SHIFT = 30
|
||||
|
||||
_TCR_T0SZ_VA48 = 64 - 48 // VA=48
|
||||
_TCR_T1SZ_VA48 = 64 - 48 // VA=48
|
||||
|
||||
_TCR_ASID16 = 1 << 36
|
||||
_TCR_TBI0 = 1 << 37
|
||||
|
||||
_TCR_TXSZ_VA48 = (_TCR_T0SZ_VA48 << _TCR_T0SZ_OFFSET) | (_TCR_T1SZ_VA48 << _TCR_T1SZ_OFFSET)
|
||||
|
||||
_TCR_TG0_4K = 0 << _TCR_TG0_SHIFT // 4K
|
||||
_TCR_TG0_64K = 1 << _TCR_TG0_SHIFT // 64K
|
||||
|
||||
_TCR_TG1_4K = 2 << _TCR_TG1_SHIFT
|
||||
|
||||
_TCR_TG_FLAGS = _TCR_TG0_4K | _TCR_TG1_4K
|
||||
|
||||
_TCR_IRGN0_WBWA = 1 << _TCR_IRGN0_SHIFT
|
||||
_TCR_IRGN1_WBWA = 1 << _TCR_IRGN1_SHIFT
|
||||
_TCR_IRGN_WBWA = _TCR_IRGN0_WBWA | _TCR_IRGN1_WBWA
|
||||
|
||||
_TCR_ORGN0_WBWA = 1 << _TCR_ORGN0_SHIFT
|
||||
_TCR_ORGN1_WBWA = 1 << _TCR_ORGN1_SHIFT
|
||||
|
||||
_TCR_ORGN_WBWA = _TCR_ORGN0_WBWA | _TCR_ORGN1_WBWA
|
||||
|
||||
_TCR_SHARED = (3 << _TCR_SH0_SHIFT) | (3 << _TCR_SH1_SHIFT)
|
||||
|
||||
_TCR_CACHE_FLAGS = _TCR_IRGN_WBWA | _TCR_ORGN_WBWA
|
||||
)
|
||||
|
||||
// Arm64: Memory Attribute Indirection Register EL1.
|
||||
const (
|
||||
_MT_DEVICE_nGnRnE = 0
|
||||
_MT_DEVICE_nGnRE = 1
|
||||
_MT_DEVICE_GRE = 2
|
||||
_MT_NORMAL_NC = 3
|
||||
_MT_NORMAL = 4
|
||||
_MT_NORMAL_WT = 5
|
||||
_MT_EL1_INIT = (0 << _MT_DEVICE_nGnRnE) | (0x4 << _MT_DEVICE_nGnRE * 8) | (0xc << _MT_DEVICE_GRE * 8) | (0x44 << _MT_NORMAL_NC * 8) | (0xff << _MT_NORMAL * 8) | (0xbb << _MT_NORMAL_WT * 8)
|
||||
)
|
||||
|
||||
const (
|
||||
_KVM_ARM_VCPU_POWER_OFF = 0 // CPU is started in OFF state
|
||||
_KVM_ARM_VCPU_PSCI_0_2 = 2 // CPU uses PSCI v0.2
|
||||
)
|
||||
|
||||
// Arm64: Exception Syndrome Register EL1.
|
||||
const (
|
||||
_ESR_ELx_FSC = 0x3F
|
||||
|
||||
_ESR_SEGV_MAPERR_L0 = 0x4
|
||||
_ESR_SEGV_MAPERR_L1 = 0x5
|
||||
_ESR_SEGV_MAPERR_L2 = 0x6
|
||||
_ESR_SEGV_MAPERR_L3 = 0x7
|
||||
|
||||
_ESR_SEGV_ACCERR_L1 = 0x9
|
||||
_ESR_SEGV_ACCERR_L2 = 0xa
|
||||
_ESR_SEGV_ACCERR_L3 = 0xb
|
||||
|
||||
_ESR_SEGV_PEMERR_L1 = 0xd
|
||||
_ESR_SEGV_PEMERR_L2 = 0xe
|
||||
_ESR_SEGV_PEMERR_L3 = 0xf
|
||||
)
|
||||
@@ -135,3 +135,43 @@ func (c *vCPU) setSignalMask() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setUserRegisters sets user registers in the vCPU.
|
||||
func (c *vCPU) setUserRegisters(uregs *userRegs) error {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_SET_REGS,
|
||||
uintptr(unsafe.Pointer(uregs))); errno != 0 {
|
||||
return fmt.Errorf("error setting user registers: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUserRegisters reloads user registers in the vCPU.
|
||||
//
|
||||
// This is safe to call from a nosplit context.
|
||||
//
|
||||
//go:nosplit
|
||||
func (c *vCPU) getUserRegisters(uregs *userRegs) syscall.Errno {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_GET_REGS,
|
||||
uintptr(unsafe.Pointer(uregs))); errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// setSystemRegisters sets system registers.
|
||||
func (c *vCPU) setSystemRegisters(sregs *systemRegs) error {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_SET_SREGS,
|
||||
uintptr(unsafe.Pointer(sregs))); errno != 0 {
|
||||
return fmt.Errorf("error setting system registers: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,8 +12,38 @@
|
||||
// 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/sentry/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
|
||||
@@ -59,3 +89,95 @@ func availableRegionsForSetMem() (phyRegions []physicalRegion) {
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform/ring0"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
)
|
||||
|
||||
// setMemoryRegion initializes a region.
|
||||
//
|
||||
// This may be called from bluepillHandler, and therefore returns an errno
|
||||
// directly (instead of wrapping in an error) to avoid allocations.
|
||||
//
|
||||
//go:nosplit
|
||||
func (m *machine) setMemoryRegion(slot int, physical, length, virtual uintptr) syscall.Errno {
|
||||
userRegion := userMemoryRegion{
|
||||
slot: uint32(slot),
|
||||
flags: 0,
|
||||
guestPhysAddr: uint64(physical),
|
||||
memorySize: uint64(length),
|
||||
userspaceAddr: uint64(virtual),
|
||||
}
|
||||
|
||||
// Set the region.
|
||||
_, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(m.fd),
|
||||
_KVM_SET_USER_MEMORY_REGION,
|
||||
uintptr(unsafe.Pointer(&userRegion)))
|
||||
return errno
|
||||
}
|
||||
|
||||
type kvmVcpuInit struct {
|
||||
target uint32
|
||||
features [7]uint32
|
||||
}
|
||||
|
||||
var vcpuInit kvmVcpuInit
|
||||
|
||||
// initArchState initializes architecture-specific state.
|
||||
func (m *machine) initArchState() error {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(m.fd),
|
||||
_KVM_ARM_PREFERRED_TARGET,
|
||||
uintptr(unsafe.Pointer(&vcpuInit))); errno != 0 {
|
||||
panic(fmt.Sprintf("error setting KVM_ARM_PREFERRED_TARGET failed: %v", errno))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPageWithReflect(p uintptr) []byte {
|
||||
return (*(*[0xFFFFFF]byte)(unsafe.Pointer(p & ^uintptr(syscall.Getpagesize()-1))))[:syscall.Getpagesize()]
|
||||
}
|
||||
|
||||
// Work around: move ring0.Vectors() into a specific address with 11-bits alignment.
|
||||
//
|
||||
// According to the design documentation of Arm64,
|
||||
// the start address of exception vector table should be 11-bits aligned.
|
||||
// Please see the code in linux kernel as reference: arch/arm64/kernel/entry.S
|
||||
// But, we can't align a function's start address to a specific address by using golang.
|
||||
// We have raised this question in golang community:
|
||||
// https://groups.google.com/forum/m/#!topic/golang-dev/RPj90l5x86I
|
||||
// This function will be removed when golang supports this feature.
|
||||
//
|
||||
// There are 2 jobs were implemented in this function:
|
||||
// 1, move the start address of exception vector table into the specific address.
|
||||
// 2, modify the offset of each instruction.
|
||||
func updateVectorTable() {
|
||||
fromLocation := reflect.ValueOf(ring0.Vectors).Pointer()
|
||||
offset := fromLocation & (1<<11 - 1)
|
||||
if offset != 0 {
|
||||
offset = 1<<11 - offset
|
||||
}
|
||||
|
||||
toLocation := fromLocation + offset
|
||||
page := getPageWithReflect(toLocation)
|
||||
if err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
page = getPageWithReflect(toLocation + 4096)
|
||||
if err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Move exception-vector-table into the specific address.
|
||||
var entry *uint32
|
||||
var entryFrom *uint32
|
||||
for i := 1; i <= 0x800; i++ {
|
||||
entry = (*uint32)(unsafe.Pointer(toLocation + 0x800 - uintptr(i)))
|
||||
entryFrom = (*uint32)(unsafe.Pointer(fromLocation + 0x800 - uintptr(i)))
|
||||
*entry = *entryFrom
|
||||
}
|
||||
|
||||
// The offset from the address of each unconditionally branch is changed.
|
||||
// We should modify the offset of each instruction.
|
||||
nums := []uint32{0x0, 0x80, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, 0x400, 0x480, 0x500, 0x580, 0x600, 0x680, 0x700, 0x780}
|
||||
for _, num := range nums {
|
||||
entry = (*uint32)(unsafe.Pointer(toLocation + uintptr(num)))
|
||||
*entry = *entry - (uint32)(offset/4)
|
||||
}
|
||||
|
||||
page = getPageWithReflect(toLocation)
|
||||
if err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
page = getPageWithReflect(toLocation + 4096)
|
||||
if err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// initArchState initializes architecture-specific state.
|
||||
func (c *vCPU) initArchState() error {
|
||||
var (
|
||||
reg kvmOneReg
|
||||
data uint64
|
||||
regGet kvmOneReg
|
||||
dataGet uint64
|
||||
)
|
||||
|
||||
reg.addr = uint64(reflect.ValueOf(&data).Pointer())
|
||||
regGet.addr = uint64(reflect.ValueOf(&dataGet).Pointer())
|
||||
|
||||
vcpuInit.features[0] |= (1 << _KVM_ARM_VCPU_PSCI_0_2)
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_ARM_VCPU_INIT,
|
||||
uintptr(unsafe.Pointer(&vcpuInit))); errno != 0 {
|
||||
panic(fmt.Sprintf("error setting KVM_ARM_VCPU_INIT failed: %v", errno))
|
||||
}
|
||||
|
||||
// cpacr_el1
|
||||
reg.id = _KVM_ARM64_REGS_CPACR_EL1
|
||||
data = (_FPEN_NOTRAP << _FPEN_SHIFT)
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sctlr_el1
|
||||
regGet.id = _KVM_ARM64_REGS_SCTLR_EL1
|
||||
if err := c.getOneRegister(®Get); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataGet |= (_SCTLR_M | _SCTLR_C | _SCTLR_I)
|
||||
data = dataGet
|
||||
reg.id = _KVM_ARM64_REGS_SCTLR_EL1
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// tcr_el1
|
||||
data = _TCR_TXSZ_VA48 | _TCR_CACHE_FLAGS | _TCR_SHARED | _TCR_TG_FLAGS | _TCR_ASID16 | _TCR_IPS_40BITS
|
||||
reg.id = _KVM_ARM64_REGS_TCR_EL1
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// mair_el1
|
||||
data = _MT_EL1_INIT
|
||||
reg.id = _KVM_ARM64_REGS_MAIR_EL1
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ttbr0_el1
|
||||
data = c.machine.kernel.PageTables.TTBR0_EL1(false, 0)
|
||||
|
||||
reg.id = _KVM_ARM64_REGS_TTBR0_EL1
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.SetTtbr0Kvm(uintptr(data))
|
||||
|
||||
// ttbr1_el1
|
||||
data = c.machine.kernel.PageTables.TTBR1_EL1(false, 0)
|
||||
|
||||
reg.id = _KVM_ARM64_REGS_TTBR1_EL1
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sp_el1
|
||||
data = c.CPU.StackTop()
|
||||
reg.id = _KVM_ARM64_REGS_SP_EL1
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// pc
|
||||
reg.id = _KVM_ARM64_REGS_PC
|
||||
data = uint64(reflect.ValueOf(ring0.Start).Pointer())
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// r8
|
||||
reg.id = _KVM_ARM64_REGS_R8
|
||||
data = uint64(reflect.ValueOf(&c.CPU).Pointer())
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// vbar_el1
|
||||
reg.id = _KVM_ARM64_REGS_VBAR_EL1
|
||||
|
||||
fromLocation := reflect.ValueOf(ring0.Vectors).Pointer()
|
||||
offset := fromLocation & (1<<11 - 1)
|
||||
if offset != 0 {
|
||||
offset = 1<<11 - offset
|
||||
}
|
||||
|
||||
toLocation := fromLocation + offset
|
||||
data = uint64(ring0.KernelStartAddress | toLocation)
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data = ring0.PsrDefaultSet | ring0.KernelFlagsSet
|
||||
reg.id = _KVM_ARM64_REGS_PSTATE
|
||||
if err := c.setOneRegister(®); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func (c *vCPU) loadSegments(tid uint64) {
|
||||
// TODO(gvisor.dev/issue/1238): TLS is not supported.
|
||||
// Get TLS from tpidr_el0.
|
||||
atomic.StoreUint64(&c.tid, tid)
|
||||
}
|
||||
|
||||
func (c *vCPU) setOneRegister(reg *kvmOneReg) error {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_SET_ONE_REG,
|
||||
uintptr(unsafe.Pointer(reg))); errno != 0 {
|
||||
return fmt.Errorf("error setting one register: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *vCPU) getOneRegister(reg *kvmOneReg) error {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_GET_ONE_REG,
|
||||
uintptr(unsafe.Pointer(reg))); errno != 0 {
|
||||
return fmt.Errorf("error setting one register: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setCPUID sets the CPUID to be used by the guest.
|
||||
func (c *vCPU) setCPUID() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setSystemTime sets the TSC for the vCPU.
|
||||
func (c *vCPU) setSystemTime() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setSignalMask sets the vCPU signal mask.
|
||||
//
|
||||
// This must be called prior to running the vCPU.
|
||||
func (c *vCPU) setSignalMask() error {
|
||||
// The layout of this structure implies that it will not necessarily be
|
||||
// the same layout chosen by the Go compiler. It gets fudged here.
|
||||
var data struct {
|
||||
length uint32
|
||||
mask1 uint32
|
||||
mask2 uint32
|
||||
_ uint32
|
||||
}
|
||||
data.length = 8 // Fixed sigset size.
|
||||
data.mask1 = ^uint32(bounceSignalMask & 0xffffffff)
|
||||
data.mask2 = ^uint32(bounceSignalMask >> 32)
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_SET_SIGNAL_MASK,
|
||||
uintptr(unsafe.Pointer(&data))); errno != 0 {
|
||||
return fmt.Errorf("error setting signal mask: %v", errno)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SwitchToUser unpacks architectural-details.
|
||||
func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo) (usermem.AccessType, error) {
|
||||
// Check for canonical addresses.
|
||||
if regs := switchOpts.Registers; !ring0.IsCanonical(regs.Pc) {
|
||||
return nonCanonical(regs.Pc, int32(syscall.SIGSEGV), info)
|
||||
} else if !ring0.IsCanonical(regs.Sp) {
|
||||
return nonCanonical(regs.Sp, int32(syscall.SIGBUS), info)
|
||||
}
|
||||
|
||||
var vector ring0.Vector
|
||||
ttbr0App := switchOpts.PageTables.TTBR0_EL1(false, 0)
|
||||
c.SetTtbr0App(uintptr(ttbr0App))
|
||||
|
||||
// TODO(gvisor.dev/issue/1238): full context-switch supporting for Arm64.
|
||||
// The Arm64 user-mode execution state consists of:
|
||||
// x0-x30
|
||||
// PC, SP, PSTATE
|
||||
// V0-V31: 32 128-bit registers for floating point, and simd
|
||||
// FPSR
|
||||
// TPIDR_EL0, used for TLS
|
||||
appRegs := switchOpts.Registers
|
||||
c.SetAppAddr(ring0.KernelStartAddress | uintptr(unsafe.Pointer(appRegs)))
|
||||
|
||||
entersyscall()
|
||||
bluepill(c)
|
||||
vector = c.CPU.SwitchToUser(switchOpts)
|
||||
exitsyscall()
|
||||
|
||||
switch vector {
|
||||
case ring0.Syscall:
|
||||
// Fast path: system call executed.
|
||||
return usermem.NoAccess, nil
|
||||
|
||||
case ring0.PageFault:
|
||||
return c.fault(int32(syscall.SIGSEGV), info)
|
||||
case 0xaa:
|
||||
return usermem.NoAccess, nil
|
||||
default:
|
||||
return usermem.NoAccess, platform.ErrContextSignal
|
||||
}
|
||||
|
||||
}
|
||||
@@ -87,46 +87,6 @@ func unmapRunData(r *runData) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setUserRegisters sets user registers in the vCPU.
|
||||
func (c *vCPU) setUserRegisters(uregs *userRegs) error {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_SET_REGS,
|
||||
uintptr(unsafe.Pointer(uregs))); errno != 0 {
|
||||
return fmt.Errorf("error setting user registers: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUserRegisters reloads user registers in the vCPU.
|
||||
//
|
||||
// This is safe to call from a nosplit context.
|
||||
//
|
||||
//go:nosplit
|
||||
func (c *vCPU) getUserRegisters(uregs *userRegs) syscall.Errno {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_GET_REGS,
|
||||
uintptr(unsafe.Pointer(uregs))); errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// setSystemRegisters sets system registers.
|
||||
func (c *vCPU) setSystemRegisters(sregs *systemRegs) error {
|
||||
if _, _, errno := syscall.RawSyscall(
|
||||
syscall.SYS_IOCTL,
|
||||
uintptr(c.fd),
|
||||
_KVM_SET_SREGS,
|
||||
uintptr(unsafe.Pointer(sregs))); errno != 0 {
|
||||
return fmt.Errorf("error setting system registers: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// atomicAddressSpace is an atomic address space pointer.
|
||||
type atomicAddressSpace struct {
|
||||
pointer unsafe.Pointer
|
||||
|
||||
Reference in New Issue
Block a user