github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go (about) 1 // Copyright 2018 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 amd64 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 // dieArchSetup initializes the state for dieTrampoline. 28 // 29 // The amd64 dieTrampoline requires the vCPU to be set in BX, and the last RIP 30 // to be in AX. The trampoline then simulates a call to dieHandler from the 31 // provided RIP. 32 // 33 //go:nosplit 34 func dieArchSetup(c *vCPU, context *arch.SignalContext64, guestRegs *userRegs) { 35 // Reload all registers to have an accurate stack trace when we return 36 // to host mode. This means that the stack should be unwound correctly. 37 if errno := c.getUserRegisters(&c.dieState.guestRegs); errno != 0 { 38 throw(c.dieState.message) 39 } 40 41 // If the vCPU is in user mode, we set the stack to the stored stack 42 // value in the vCPU itself. We don't want to unwind the user stack. 43 if guestRegs.RFLAGS&ring0.UserFlagsSet == ring0.UserFlagsSet { 44 regs := c.CPU.Registers() 45 context.Rax = regs.Rax 46 context.Rsp = regs.Rsp 47 context.Rbp = regs.Rbp 48 } else { 49 context.Rax = guestRegs.RIP 50 context.Rsp = guestRegs.RSP 51 context.Rbp = guestRegs.RBP 52 context.Eflags = guestRegs.RFLAGS 53 } 54 context.Rbx = uint64(uintptr(unsafe.Pointer(c))) 55 context.Rip = uint64(dieTrampolineAddr) 56 } 57 58 // getHypercallID returns hypercall ID. 59 // 60 //go:nosplit 61 func getHypercallID(addr uintptr) int { 62 return _KVM_HYPERCALL_MAX 63 } 64 65 // bluepillStopGuest is reponsible for injecting interrupt. 66 // 67 //go:nosplit 68 func bluepillStopGuest(c *vCPU) { 69 // Interrupt: we must have requested an interrupt 70 // window; set the interrupt line. 71 if _, _, errno := unix.RawSyscall( 72 unix.SYS_IOCTL, 73 uintptr(c.fd), 74 _KVM_INTERRUPT, 75 uintptr(unsafe.Pointer(&bounce))); errno != 0 { 76 throw("interrupt injection failed") 77 } 78 // Clear previous injection request. 79 c.runData.requestInterruptWindow = 0 80 } 81 82 // bluepillSigBus is reponsible for injecting NMI to trigger sigbus. 83 // 84 //go:nosplit 85 func bluepillSigBus(c *vCPU) { 86 if _, _, errno := unix.RawSyscall( // escapes: no. 87 unix.SYS_IOCTL, 88 uintptr(c.fd), 89 _KVM_NMI, 0); errno != 0 { 90 throw("NMI injection failed") 91 } 92 } 93 94 // bluepillHandleEnosys is reponsible for handling enosys error. 95 // 96 //go:nosplit 97 func bluepillHandleEnosys(c *vCPU) { 98 throw("run failed: ENOSYS") 99 } 100 101 // bluepillReadyStopGuest checks whether the current vCPU is ready for interrupt injection. 102 // 103 //go:nosplit 104 func bluepillReadyStopGuest(c *vCPU) bool { 105 if c.runData.readyForInterruptInjection == 0 { 106 return false 107 } 108 109 if c.runData.ifFlag == 0 { 110 // This is impossible if readyForInterruptInjection is 1. 111 throw("interrupts are disabled") 112 } 113 114 // Disable interrupts if we are in the kernel space. 115 // 116 // When the Sentry switches into the kernel mode, it disables 117 // interrupts. But when goruntime switches on a goroutine which has 118 // been saved in the host mode, it restores flags and this enables 119 // interrupts. See the comment of UserFlagsSet for more details. 120 uregs := userRegs{} 121 err := c.getUserRegisters(&uregs) 122 if err != 0 { 123 throw("failed to get user registers") 124 } 125 126 if ring0.IsKernelFlags(uregs.RFLAGS) { 127 uregs.RFLAGS &^= ring0.KernelFlagsClear 128 err = c.setUserRegisters(&uregs) 129 if err != 0 { 130 throw("failed to set user registers") 131 } 132 return false 133 } 134 return true 135 } 136 137 // bluepillArchHandleExit checks architecture specific exitcode. 138 // 139 //go:nosplit 140 func bluepillArchHandleExit(c *vCPU, context unsafe.Pointer) { 141 c.die(bluepillArchContext(context), "unknown") 142 }