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 }