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