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 }