gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "gvisor.dev/gvisor/pkg/ring0" 25 "gvisor.dev/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 initializes 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 }