github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/platform/kvm/bluepill_arm64_unsafe.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build arm64
    16  
    17  package kvm
    18  
    19  import (
    20  	"unsafe"
    21  
    22  	"golang.org/x/sys/unix"
    23  	"github.com/SagerNet/gvisor/pkg/ring0"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    25  )
    26  
    27  // fpsimdPtr returns a fpsimd64 for the given address.
    28  //
    29  //go:nosplit
    30  func fpsimdPtr(addr *byte) *arch.FpsimdContext {
    31  	return (*arch.FpsimdContext)(unsafe.Pointer(addr))
    32  }
    33  
    34  // dieArchSetup initialies the state for dieTrampoline.
    35  //
    36  // The arm64 dieTrampoline requires the vCPU to be set in R1, and the last PC
    37  // to be in R0. The trampoline then simulates a call to dieHandler from the
    38  // provided PC.
    39  //
    40  //go:nosplit
    41  func dieArchSetup(c *vCPU, context *arch.SignalContext64, guestRegs *userRegs) {
    42  	// If the vCPU is in user mode, we set the stack to the stored stack
    43  	// value in the vCPU itself. We don't want to unwind the user stack.
    44  	if guestRegs.Regs.Pstate&ring0.PsrModeMask == ring0.UserFlagsSet {
    45  		regs := c.CPU.Registers()
    46  		context.Regs[0] = regs.Regs[0]
    47  		context.Sp = regs.Sp
    48  		context.Regs[29] = regs.Regs[29] // stack base address
    49  	} else {
    50  		context.Regs[0] = guestRegs.Regs.Pc
    51  		context.Sp = guestRegs.Regs.Sp
    52  		context.Regs[29] = guestRegs.Regs.Regs[29]
    53  		context.Pstate = guestRegs.Regs.Pstate
    54  	}
    55  	context.Regs[1] = uint64(uintptr(unsafe.Pointer(c)))
    56  	context.Pc = uint64(dieTrampolineAddr)
    57  }
    58  
    59  // bluepillArchFpContext returns the arch-specific fpsimd context.
    60  //
    61  //go:nosplit
    62  func bluepillArchFpContext(context unsafe.Pointer) *arch.FpsimdContext {
    63  	return &((*arch.SignalContext64)(context).Fpsimd64)
    64  }
    65  
    66  // getHypercallID returns hypercall ID.
    67  //
    68  // On Arm64, the MMIO address should be 64-bit aligned.
    69  //
    70  //go:nosplit
    71  func getHypercallID(addr uintptr) int {
    72  	if addr < arm64HypercallMMIOBase || addr >= (arm64HypercallMMIOBase+_AARCH64_HYPERCALL_MMIO_SIZE) {
    73  		return _KVM_HYPERCALL_MAX
    74  	} else {
    75  		return int(((addr) - arm64HypercallMMIOBase) >> 3)
    76  	}
    77  }
    78  
    79  // bluepillStopGuest is reponsible for injecting sError.
    80  //
    81  //go:nosplit
    82  func bluepillStopGuest(c *vCPU) {
    83  	// vcpuSErrBounce is the event of system error for bouncing KVM.
    84  	vcpuSErrBounce := &kvmVcpuEvents{
    85  		exception: exception{
    86  			sErrPending: 1,
    87  		},
    88  	}
    89  
    90  	if _, _, errno := unix.RawSyscall( // escapes: no.
    91  		unix.SYS_IOCTL,
    92  		uintptr(c.fd),
    93  		_KVM_SET_VCPU_EVENTS,
    94  		uintptr(unsafe.Pointer(vcpuSErrBounce))); errno != 0 {
    95  		throw("bounce sErr injection failed")
    96  	}
    97  }
    98  
    99  // bluepillSigBus is reponsible for injecting sError to trigger sigbus.
   100  //
   101  //go:nosplit
   102  func bluepillSigBus(c *vCPU) {
   103  	// vcpuSErrNMI is the event of system error to trigger sigbus.
   104  	vcpuSErrNMI := &kvmVcpuEvents{
   105  		exception: exception{
   106  			sErrPending: 1,
   107  			sErrHasEsr:  1,
   108  			sErrEsr:     _ESR_ELx_SERR_NMI,
   109  		},
   110  	}
   111  
   112  	// Host must support ARM64_HAS_RAS_EXTN.
   113  	if _, _, errno := unix.RawSyscall( // escapes: no.
   114  		unix.SYS_IOCTL,
   115  		uintptr(c.fd),
   116  		_KVM_SET_VCPU_EVENTS,
   117  		uintptr(unsafe.Pointer(vcpuSErrNMI))); errno != 0 {
   118  		if errno == unix.EINVAL {
   119  			throw("No ARM64_HAS_RAS_EXTN feature in host.")
   120  		}
   121  		throw("nmi sErr injection failed")
   122  	}
   123  }
   124  
   125  // bluepillExtDabt is reponsible for injecting external data abort.
   126  //
   127  //go:nosplit
   128  func bluepillExtDabt(c *vCPU) {
   129  	// vcpuExtDabt is the event of ext_dabt.
   130  	vcpuExtDabt := &kvmVcpuEvents{
   131  		exception: exception{
   132  			extDabtPending: 1,
   133  		},
   134  	}
   135  
   136  	if _, _, errno := unix.RawSyscall( // escapes: no.
   137  		unix.SYS_IOCTL,
   138  		uintptr(c.fd),
   139  		_KVM_SET_VCPU_EVENTS,
   140  		uintptr(unsafe.Pointer(vcpuExtDabt))); errno != 0 {
   141  		throw("ext_dabt injection failed")
   142  	}
   143  }
   144  
   145  // bluepillHandleEnosys is reponsible for handling enosys error.
   146  //
   147  //go:nosplit
   148  func bluepillHandleEnosys(c *vCPU) {
   149  	bluepillExtDabt(c)
   150  }
   151  
   152  // bluepillReadyStopGuest checks whether the current vCPU is ready for sError injection.
   153  //
   154  //go:nosplit
   155  func bluepillReadyStopGuest(c *vCPU) bool {
   156  	return true
   157  }
   158  
   159  // bluepillArchHandleExit checks architecture specific exitcode.
   160  //
   161  //go:nosplit
   162  func bluepillArchHandleExit(c *vCPU, context unsafe.Pointer) {
   163  	switch c.runData.exitReason {
   164  	case _KVM_EXIT_ARM_NISV:
   165  		bluepillExtDabt(c)
   166  	default:
   167  		c.die(bluepillArchContext(context), "unknown")
   168  	}
   169  }