github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/platform/kvm/machine_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 go1.12
    16  // +build !go1.18
    17  
    18  // Check go:linkname function signatures when updating Go version.
    19  
    20  package kvm
    21  
    22  import (
    23  	"fmt"
    24  	"math"
    25  	"sync/atomic"
    26  	"unsafe"
    27  
    28  	"golang.org/x/sys/unix"
    29  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    30  )
    31  
    32  //go:linkname entersyscall runtime.entersyscall
    33  func entersyscall()
    34  
    35  //go:linkname exitsyscall runtime.exitsyscall
    36  func exitsyscall()
    37  
    38  // setMemoryRegion initializes a region.
    39  //
    40  // This may be called from bluepillHandler, and therefore returns an errno
    41  // directly (instead of wrapping in an error) to avoid allocations.
    42  //
    43  //go:nosplit
    44  func (m *machine) setMemoryRegion(slot int, physical, length, virtual uintptr, flags uint32) unix.Errno {
    45  	userRegion := userMemoryRegion{
    46  		slot:          uint32(slot),
    47  		flags:         uint32(flags),
    48  		guestPhysAddr: uint64(physical),
    49  		memorySize:    uint64(length),
    50  		userspaceAddr: uint64(virtual),
    51  	}
    52  
    53  	// Set the region.
    54  	_, _, errno := unix.RawSyscall(
    55  		unix.SYS_IOCTL,
    56  		uintptr(m.fd),
    57  		_KVM_SET_USER_MEMORY_REGION,
    58  		uintptr(unsafe.Pointer(&userRegion)))
    59  	return errno
    60  }
    61  
    62  // mapRunData maps the vCPU run data.
    63  func mapRunData(fd int) (*runData, error) {
    64  	r, _, errno := unix.RawSyscall6(
    65  		unix.SYS_MMAP,
    66  		0,
    67  		uintptr(runDataSize),
    68  		unix.PROT_READ|unix.PROT_WRITE,
    69  		unix.MAP_SHARED,
    70  		uintptr(fd),
    71  		0)
    72  	if errno != 0 {
    73  		return nil, fmt.Errorf("error mapping runData: %v", errno)
    74  	}
    75  	return (*runData)(unsafe.Pointer(r)), nil
    76  }
    77  
    78  // unmapRunData unmaps the vCPU run data.
    79  func unmapRunData(r *runData) error {
    80  	if _, _, errno := unix.RawSyscall(
    81  		unix.SYS_MUNMAP,
    82  		uintptr(unsafe.Pointer(r)),
    83  		uintptr(runDataSize),
    84  		0); errno != 0 {
    85  		return fmt.Errorf("error unmapping runData: %v", errno)
    86  	}
    87  	return nil
    88  }
    89  
    90  // atomicAddressSpace is an atomic address space pointer.
    91  type atomicAddressSpace struct {
    92  	pointer unsafe.Pointer
    93  }
    94  
    95  // set sets the address space value.
    96  //
    97  //go:nosplit
    98  func (a *atomicAddressSpace) set(as *addressSpace) {
    99  	atomic.StorePointer(&a.pointer, unsafe.Pointer(as))
   100  }
   101  
   102  // get gets the address space value.
   103  //
   104  // Note that this should be considered best-effort, and may have changed by the
   105  // time this function returns.
   106  //
   107  //go:nosplit
   108  func (a *atomicAddressSpace) get() *addressSpace {
   109  	return (*addressSpace)(atomic.LoadPointer(&a.pointer))
   110  }
   111  
   112  // notify notifies that the vCPU has transitioned modes.
   113  //
   114  // This may be called by a signal handler and therefore throws on error.
   115  //
   116  //go:nosplit
   117  func (c *vCPU) notify() {
   118  	_, _, errno := unix.RawSyscall6( // escapes: no.
   119  		unix.SYS_FUTEX,
   120  		uintptr(unsafe.Pointer(&c.state)),
   121  		linux.FUTEX_WAKE|linux.FUTEX_PRIVATE_FLAG,
   122  		math.MaxInt32, // Number of waiters.
   123  		0, 0, 0)
   124  	if errno != 0 {
   125  		throw("futex wake error")
   126  	}
   127  }
   128  
   129  // waitUntilNot waits for the vCPU to transition modes.
   130  //
   131  // The state should have been previously set to vCPUWaiter after performing an
   132  // appropriate action to cause a transition (e.g. interrupt injection).
   133  //
   134  // This panics on error.
   135  func (c *vCPU) waitUntilNot(state uint32) {
   136  	_, _, errno := unix.Syscall6(
   137  		unix.SYS_FUTEX,
   138  		uintptr(unsafe.Pointer(&c.state)),
   139  		linux.FUTEX_WAIT|linux.FUTEX_PRIVATE_FLAG,
   140  		uintptr(state),
   141  		0, 0, 0)
   142  	if errno != 0 && errno != unix.EINTR && errno != unix.EAGAIN {
   143  		panic("futex wait error")
   144  	}
   145  }
   146  
   147  // setSignalMask sets the vCPU signal mask.
   148  //
   149  // This must be called prior to running the vCPU.
   150  func (c *vCPU) setSignalMask() error {
   151  	// The layout of this structure implies that it will not necessarily be
   152  	// the same layout chosen by the Go compiler. It gets fudged here.
   153  	var data struct {
   154  		length uint32
   155  		mask1  uint32
   156  		mask2  uint32
   157  		_      uint32
   158  	}
   159  	data.length = 8 // Fixed sigset size.
   160  	data.mask1 = ^uint32(bounceSignalMask & 0xffffffff)
   161  	data.mask2 = ^uint32(bounceSignalMask >> 32)
   162  	if _, _, errno := unix.RawSyscall(
   163  		unix.SYS_IOCTL,
   164  		uintptr(c.fd),
   165  		_KVM_SET_SIGNAL_MASK,
   166  		uintptr(unsafe.Pointer(&data))); errno != 0 {
   167  		return fmt.Errorf("error setting signal mask: %v", errno)
   168  	}
   169  
   170  	return nil
   171  }