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  }