github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/sentry/devices/accel/gasket.go (about)

     1  // Copyright 2023 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  package accel
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"golang.org/x/sys/unix"
    21  	"github.com/MerlinKodo/gvisor/pkg/abi/gasket"
    22  	"github.com/MerlinKodo/gvisor/pkg/abi/linux"
    23  	"github.com/MerlinKodo/gvisor/pkg/cleanup"
    24  	"github.com/MerlinKodo/gvisor/pkg/context"
    25  	"github.com/MerlinKodo/gvisor/pkg/errors/linuxerr"
    26  	"github.com/MerlinKodo/gvisor/pkg/hostarch"
    27  	"github.com/MerlinKodo/gvisor/pkg/sentry/fsimpl/eventfd"
    28  	"github.com/MerlinKodo/gvisor/pkg/sentry/kernel"
    29  	"github.com/MerlinKodo/gvisor/pkg/sentry/memmap"
    30  	"github.com/MerlinKodo/gvisor/pkg/sentry/mm"
    31  )
    32  
    33  func gasketMapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd *accelFD, paramsAddr hostarch.Addr) (uintptr, error) {
    34  	var userIoctlParams gasket.GasketPageTableIoctl
    35  	if _, err := userIoctlParams.CopyIn(t, paramsAddr); err != nil {
    36  		return 0, err
    37  	}
    38  
    39  	tmm := t.MemoryManager()
    40  	ar, ok := tmm.CheckIORange(hostarch.Addr(userIoctlParams.HostAddress), int64(userIoctlParams.Size))
    41  	if !ok {
    42  		return 0, linuxerr.EFAULT
    43  	}
    44  
    45  	if !ar.IsPageAligned() || (userIoctlParams.Size/hostarch.PageSize) == 0 {
    46  		return 0, linuxerr.EINVAL
    47  	}
    48  	// Reserve a range in our address space.
    49  	m, _, errno := unix.RawSyscall6(unix.SYS_MMAP, 0 /* addr */, uintptr(ar.Length()), unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANONYMOUS, ^uintptr(0) /* fd */, 0 /* offset */)
    50  	if errno != 0 {
    51  		return 0, errno
    52  	}
    53  	cu := cleanup.Make(func() {
    54  		unix.RawSyscall(unix.SYS_MUNMAP, m, uintptr(ar.Length()), 0)
    55  	})
    56  	defer cu.Clean()
    57  	// Mirror application mappings into the reserved range.
    58  	prs, err := t.MemoryManager().Pin(ctx, ar, hostarch.ReadWrite, false /* ignorePermissions */)
    59  	cu.Add(func() {
    60  		mm.Unpin(prs)
    61  	})
    62  	if err != nil {
    63  		return 0, err
    64  	}
    65  	sentryAddr := uintptr(m)
    66  	for _, pr := range prs {
    67  		ims, err := pr.File.MapInternal(memmap.FileRange{pr.Offset, pr.Offset + uint64(pr.Source.Length())}, hostarch.ReadWrite)
    68  		if err != nil {
    69  			return 0, err
    70  		}
    71  		for !ims.IsEmpty() {
    72  			im := ims.Head()
    73  			if _, _, errno := unix.RawSyscall6(unix.SYS_MREMAP, im.Addr(), 0 /* old_size */, uintptr(im.Len()), linux.MREMAP_MAYMOVE|linux.MREMAP_FIXED, sentryAddr, 0); errno != 0 {
    74  				return 0, errno
    75  			}
    76  			sentryAddr += uintptr(im.Len())
    77  			ims = ims.Tail()
    78  		}
    79  	}
    80  	sentryIoctlParams := userIoctlParams
    81  	sentryIoctlParams.HostAddress = uint64(m)
    82  	n, err := ioctlInvokePtrArg(hostFd, gasket.GASKET_IOCTL_MAP_BUFFER, &sentryIoctlParams)
    83  	if err != nil {
    84  		return n, err
    85  	}
    86  	cu.Release()
    87  	// Unmap the reserved range, which is no longer required.
    88  	unix.RawSyscall(unix.SYS_MUNMAP, m, uintptr(ar.Length()), 0)
    89  
    90  	fd.device.mu.Lock()
    91  	defer fd.device.mu.Unlock()
    92  	devAddr := userIoctlParams.DeviceAddress
    93  	for _, pr := range prs {
    94  		rlen := uint64(pr.Source.Length())
    95  		if !fd.device.devAddrSet.Add(DevAddrRange{
    96  			devAddr,
    97  			devAddr + rlen,
    98  		}, pinnedAccelMem{pinnedRange: pr, pageTableIndex: userIoctlParams.PageTableIndex}) {
    99  			panic(fmt.Sprintf("unexpected overlap of devaddr range [%#x-%#x)", devAddr, devAddr+rlen))
   100  		}
   101  		devAddr += rlen
   102  	}
   103  	return n, nil
   104  }
   105  
   106  func gasketUnmapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd *accelFD, paramsAddr hostarch.Addr) (uintptr, error) {
   107  	var userIoctlParams gasket.GasketPageTableIoctl
   108  	if _, err := userIoctlParams.CopyIn(t, paramsAddr); err != nil {
   109  		return 0, err
   110  	}
   111  	sentryIoctlParams := userIoctlParams
   112  	sentryIoctlParams.HostAddress = 0 // clobber this value, it's unused.
   113  	n, err := ioctlInvokePtrArg(hostFd, gasket.GASKET_IOCTL_UNMAP_BUFFER, &sentryIoctlParams)
   114  	if err != nil {
   115  		return n, err
   116  	}
   117  	fd.device.mu.Lock()
   118  	defer fd.device.mu.Unlock()
   119  	s := &fd.device.devAddrSet
   120  	r := DevAddrRange{userIoctlParams.DeviceAddress, userIoctlParams.DeviceAddress + userIoctlParams.Size}
   121  	seg := s.LowerBoundSegment(r.Start)
   122  	for seg.Ok() && seg.Start() < r.End {
   123  		seg = s.Isolate(seg, r)
   124  		v := seg.Value()
   125  		mm.Unpin([]mm.PinnedRange{v.pinnedRange})
   126  		gap := s.Remove(seg)
   127  		seg = gap.NextSegment()
   128  	}
   129  	return n, nil
   130  }
   131  
   132  func gasketInterruptMappingIoctl(ctx context.Context, t *kernel.Task, hostFd int32, paramsAddr hostarch.Addr) (uintptr, error) {
   133  	var userIoctlParams gasket.GasketInterruptMapping
   134  	if _, err := userIoctlParams.CopyIn(t, paramsAddr); err != nil {
   135  		return 0, err
   136  	}
   137  
   138  	// Check that 'userEventFD.Eventfd' is an eventfd.
   139  	eventFileGeneric, _ := t.FDTable().Get(int32(userIoctlParams.EventFD))
   140  	if eventFileGeneric == nil {
   141  		return 0, linuxerr.EBADF
   142  	}
   143  	defer eventFileGeneric.DecRef(ctx)
   144  	eventFile, ok := eventFileGeneric.Impl().(*eventfd.EventFileDescription)
   145  	if !ok {
   146  		return 0, linuxerr.EINVAL
   147  	}
   148  
   149  	eventfd, err := eventFile.HostFD()
   150  	if err != nil {
   151  		return 0, err
   152  	}
   153  
   154  	sentryIoctlParams := userIoctlParams
   155  	sentryIoctlParams.EventFD = uint64(eventfd)
   156  	n, err := ioctlInvokePtrArg(hostFd, gasket.GASKET_IOCTL_REGISTER_INTERRUPT, &sentryIoctlParams)
   157  	if err != nil {
   158  		return n, err
   159  	}
   160  
   161  	outIoctlParams := sentryIoctlParams
   162  	outIoctlParams.EventFD = userIoctlParams.EventFD
   163  	if _, err := outIoctlParams.CopyOut(t, paramsAddr); err != nil {
   164  		return n, err
   165  	}
   166  	return n, nil
   167  }