gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/devices/nvproxy/frontend.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 nvproxy
    16  
    17  import (
    18  	"fmt"
    19  	"sync/atomic"
    20  
    21  	"golang.org/x/sys/unix"
    22  	"gvisor.dev/gvisor/pkg/abi/linux"
    23  	"gvisor.dev/gvisor/pkg/abi/nvgpu"
    24  	"gvisor.dev/gvisor/pkg/cleanup"
    25  	"gvisor.dev/gvisor/pkg/context"
    26  	"gvisor.dev/gvisor/pkg/devutil"
    27  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    28  	"gvisor.dev/gvisor/pkg/fdnotifier"
    29  	"gvisor.dev/gvisor/pkg/hostarch"
    30  	"gvisor.dev/gvisor/pkg/log"
    31  	"gvisor.dev/gvisor/pkg/sentry/arch"
    32  	"gvisor.dev/gvisor/pkg/sentry/kernel"
    33  	"gvisor.dev/gvisor/pkg/sentry/memmap"
    34  	"gvisor.dev/gvisor/pkg/sentry/mm"
    35  	"gvisor.dev/gvisor/pkg/sentry/vfs"
    36  	"gvisor.dev/gvisor/pkg/usermem"
    37  	"gvisor.dev/gvisor/pkg/waiter"
    38  )
    39  
    40  // frontendDevice implements vfs.Device for /dev/nvidia# and /dev/nvidiactl.
    41  //
    42  // +stateify savable
    43  type frontendDevice struct {
    44  	nvp   *nvproxy
    45  	minor uint32
    46  }
    47  
    48  func (dev *frontendDevice) basename() string {
    49  	if dev.minor == nvgpu.NV_CONTROL_DEVICE_MINOR {
    50  		return "nvidiactl"
    51  	}
    52  	return fmt.Sprintf("nvidia%d", dev.minor)
    53  }
    54  
    55  // Open implements vfs.Device.Open.
    56  func (dev *frontendDevice) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
    57  	devClient := devutil.GoferClientFromContext(ctx)
    58  	if devClient == nil {
    59  		log.Warningf("devutil.CtxDevGoferClient is not set")
    60  		return nil, linuxerr.ENOENT
    61  	}
    62  	basename := dev.basename()
    63  	hostFD, err := devClient.OpenAt(ctx, basename, opts.Flags)
    64  	if err != nil {
    65  		ctx.Warningf("nvproxy: failed to open host %s: %v", basename, err)
    66  		return nil, err
    67  	}
    68  	fd := &frontendFD{
    69  		dev:           dev,
    70  		containerName: devClient.ContainerName(),
    71  		hostFD:        int32(hostFD),
    72  	}
    73  	if err := fd.vfsfd.Init(fd, opts.Flags, mnt, vfsd, &vfs.FileDescriptionOptions{
    74  		UseDentryMetadata: true,
    75  	}); err != nil {
    76  		unix.Close(hostFD)
    77  		return nil, err
    78  	}
    79  	if err := fdnotifier.AddFD(int32(hostFD), &fd.queue); err != nil {
    80  		unix.Close(hostFD)
    81  		return nil, err
    82  	}
    83  	fd.memmapFile.fd = fd
    84  	fd.dev.nvp.fdsMu.Lock()
    85  	defer fd.dev.nvp.fdsMu.Unlock()
    86  	fd.dev.nvp.frontendFDs[fd] = struct{}{}
    87  	return &fd.vfsfd, nil
    88  }
    89  
    90  // frontendFD implements vfs.FileDescriptionImpl for /dev/nvidia# and
    91  // /dev/nvidiactl.
    92  //
    93  // +stateify savable
    94  type frontendFD struct {
    95  	vfsfd vfs.FileDescription
    96  	vfs.FileDescriptionDefaultImpl
    97  	vfs.DentryMetadataFileDescriptionImpl
    98  	vfs.NoLockFD
    99  
   100  	dev           *frontendDevice
   101  	containerName string
   102  	hostFD        int32
   103  	memmapFile    frontendFDMemmapFile
   104  
   105  	queue           waiter.Queue
   106  	haveMmapContext atomic.Bool `state:"nosave"`
   107  
   108  	// clients are handles of clients owned by this frontendFD. clients is
   109  	// protected by dev.nvp.objsMu.
   110  	clients map[nvgpu.Handle]struct{}
   111  }
   112  
   113  // Release implements vfs.FileDescriptionImpl.Release.
   114  func (fd *frontendFD) Release(ctx context.Context) {
   115  	fdnotifier.RemoveFD(fd.hostFD)
   116  	fd.queue.Notify(waiter.EventHUp)
   117  
   118  	fd.dev.nvp.fdsMu.Lock()
   119  	delete(fd.dev.nvp.frontendFDs, fd)
   120  	fd.dev.nvp.fdsMu.Unlock()
   121  
   122  	fd.dev.nvp.objsLock()
   123  	defer fd.dev.nvp.objsUnlock()
   124  	unix.Close(int(fd.hostFD))
   125  	// src/nvidia/arch/nvalloc/unix/src/osapi.c:rm_cleanup_file_private() =>
   126  	// RmFreeUnusedClients()
   127  	for h := range fd.clients {
   128  		fd.dev.nvp.objFree(ctx, h, h)
   129  	}
   130  }
   131  
   132  // EventRegister implements waiter.Waitable.EventRegister.
   133  func (fd *frontendFD) EventRegister(e *waiter.Entry) error {
   134  	fd.queue.EventRegister(e)
   135  	if err := fdnotifier.UpdateFD(fd.hostFD); err != nil {
   136  		fd.queue.EventUnregister(e)
   137  		return err
   138  	}
   139  	return nil
   140  }
   141  
   142  // EventUnregister implements waiter.Waitable.EventUnregister.
   143  func (fd *frontendFD) EventUnregister(e *waiter.Entry) {
   144  	fd.queue.EventUnregister(e)
   145  	if err := fdnotifier.UpdateFD(fd.hostFD); err != nil {
   146  		panic(fmt.Sprint("UpdateFD:", err))
   147  	}
   148  }
   149  
   150  // Readiness implements waiter.Waitable.Readiness.
   151  func (fd *frontendFD) Readiness(mask waiter.EventMask) waiter.EventMask {
   152  	return fdnotifier.NonBlockingPoll(fd.hostFD, mask)
   153  }
   154  
   155  // Epollable implements vfs.FileDescriptionImpl.Epollable.
   156  func (fd *frontendFD) Epollable() bool {
   157  	return true
   158  }
   159  
   160  // Ioctl implements vfs.FileDescriptionImpl.Ioctl.
   161  func (fd *frontendFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
   162  	cmd := args[1].Uint()
   163  	nr := linux.IOC_NR(cmd)
   164  	argPtr := args[2].Pointer()
   165  	argSize := linux.IOC_SIZE(cmd)
   166  
   167  	t := kernel.TaskFromContext(ctx)
   168  	if t == nil {
   169  		panic("Ioctl should be called from a task context")
   170  	}
   171  
   172  	if ctx.IsLogging(log.Debug) {
   173  		ctx.Debugf("nvproxy: frontend ioctl: nr = %d = %#x, argSize = %d", nr, nr, argSize)
   174  	}
   175  
   176  	fi := frontendIoctlState{
   177  		fd:              fd,
   178  		ctx:             ctx,
   179  		t:               t,
   180  		nr:              nr,
   181  		ioctlParamsAddr: argPtr,
   182  		ioctlParamsSize: argSize,
   183  	}
   184  
   185  	// nr determines the argument type.
   186  	// Implementors:
   187  	// - To map nr to a symbol, look in
   188  	// src/nvidia/arch/nvalloc/unix/include/nv_escape.h,
   189  	// kernel-open/common/inc/nv-ioctl-numbers.h, and
   190  	// kernel-open/common/inc/nv-ioctl-numa.h.
   191  	// - To determine the parameter type, find the implementation in
   192  	// kernel-open/nvidia/nv.c:nvidia_ioctl() or
   193  	// src/nvidia/arch/nvalloc/unix/src/escape.c:RmIoctl().
   194  	// - Add symbol and parameter type definitions to //pkg/abi/nvgpu.
   195  	// - Add filter to seccomp_filters.go.
   196  	// - Add handling below.
   197  	handler := fd.dev.nvp.abi.frontendIoctl[nr]
   198  	if handler == nil {
   199  		ctx.Warningf("nvproxy: unknown frontend ioctl %d == %#x (argSize=%d, cmd=%#x)", nr, nr, argSize, cmd)
   200  		return 0, linuxerr.EINVAL
   201  	}
   202  	return handler(&fi)
   203  }
   204  
   205  func frontendIoctlCmd(nr, argSize uint32) uintptr {
   206  	return uintptr(linux.IOWR(nvgpu.NV_IOCTL_MAGIC, nr, argSize))
   207  }
   208  
   209  // frontendIoctlState holds the state of a call to frontendFD.Ioctl().
   210  type frontendIoctlState struct {
   211  	fd              *frontendFD
   212  	ctx             context.Context
   213  	t               *kernel.Task
   214  	nr              uint32
   215  	ioctlParamsAddr hostarch.Addr
   216  	ioctlParamsSize uint32
   217  }
   218  
   219  // frontendIoctlSimple implements a frontend ioctl whose parameters don't
   220  // contain any pointers requiring translation, file descriptors, or special
   221  // cases or effects, and consequently don't need to be typed by the sentry.
   222  func frontendIoctlSimple(fi *frontendIoctlState) (uintptr, error) {
   223  	if fi.ioctlParamsSize == 0 {
   224  		return frontendIoctlInvoke[byte](fi, nil)
   225  	}
   226  
   227  	ioctlParams := make([]byte, fi.ioctlParamsSize)
   228  	if _, err := fi.t.CopyInBytes(fi.ioctlParamsAddr, ioctlParams); err != nil {
   229  		return 0, err
   230  	}
   231  	n, err := frontendIoctlInvoke(fi, &ioctlParams[0])
   232  	if err != nil {
   233  		return n, err
   234  	}
   235  	if _, err := fi.t.CopyOutBytes(fi.ioctlParamsAddr, ioctlParams); err != nil {
   236  		return n, err
   237  	}
   238  	return n, nil
   239  }
   240  
   241  func rmNumaInfo(fi *frontendIoctlState) (uintptr, error) {
   242  	// The CPU topology seen by the host driver differs from the CPU
   243  	// topology presented by the sentry to the application, so reject this
   244  	// ioctl; doing so is non-fatal.
   245  	log.Debugf("nvproxy: ignoring NV_ESC_NUMA_INFO")
   246  	return 0, linuxerr.EINVAL
   247  }
   248  
   249  func frontendRegisterFD(fi *frontendIoctlState) (uintptr, error) {
   250  	var ioctlParams nvgpu.IoctlRegisterFD
   251  	if fi.ioctlParamsSize != nvgpu.SizeofIoctlRegisterFD {
   252  		return 0, linuxerr.EINVAL
   253  	}
   254  	if _, err := ioctlParams.CopyIn(fi.t, fi.ioctlParamsAddr); err != nil {
   255  		return 0, err
   256  	}
   257  	ctlFileGeneric, _ := fi.t.FDTable().Get(ioctlParams.CtlFD)
   258  	if ctlFileGeneric == nil {
   259  		return 0, linuxerr.EINVAL
   260  	}
   261  	defer ctlFileGeneric.DecRef(fi.ctx)
   262  	ctlFile, ok := ctlFileGeneric.Impl().(*frontendFD)
   263  	if !ok {
   264  		return 0, linuxerr.EINVAL
   265  	}
   266  	ioctlParams.CtlFD = ctlFile.hostFD
   267  	// The returned ctl_fd can't change, so skip copying out.
   268  	return frontendIoctlInvoke(fi, &ioctlParams)
   269  }
   270  
   271  func frontendIoctHasFD[Params any, PtrParams hasFrontendFDPtr[Params]](fi *frontendIoctlState) (uintptr, error) {
   272  	var ioctlParamsValue Params
   273  	ioctlParams := PtrParams(&ioctlParamsValue)
   274  	if int(fi.ioctlParamsSize) != ioctlParams.SizeBytes() {
   275  		return 0, linuxerr.EINVAL
   276  	}
   277  	if _, err := ioctlParams.CopyIn(fi.t, fi.ioctlParamsAddr); err != nil {
   278  		return 0, err
   279  	}
   280  
   281  	origFD := ioctlParams.GetFrontendFD()
   282  	eventFileGeneric, _ := fi.t.FDTable().Get(origFD)
   283  	if eventFileGeneric == nil {
   284  		return 0, linuxerr.EINVAL
   285  	}
   286  	defer eventFileGeneric.DecRef(fi.ctx)
   287  	eventFile, ok := eventFileGeneric.Impl().(*frontendFD)
   288  	if !ok {
   289  		return 0, linuxerr.EINVAL
   290  	}
   291  
   292  	ioctlParams.SetFrontendFD(eventFile.hostFD)
   293  	n, err := frontendIoctlInvoke(fi, ioctlParams)
   294  	ioctlParams.SetFrontendFD(origFD)
   295  	if err != nil {
   296  		return n, err
   297  	}
   298  	if _, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr); err != nil {
   299  		return n, err
   300  	}
   301  	return n, nil
   302  }
   303  
   304  func rmAllocMemory(fi *frontendIoctlState) (uintptr, error) {
   305  	var ioctlParams nvgpu.IoctlNVOS02ParametersWithFD
   306  	if fi.ioctlParamsSize != nvgpu.SizeofIoctlNVOS02ParametersWithFD {
   307  		return 0, linuxerr.EINVAL
   308  	}
   309  	if _, err := ioctlParams.CopyIn(fi.t, fi.ioctlParamsAddr); err != nil {
   310  		return 0, err
   311  	}
   312  
   313  	if log.IsLogging(log.Debug) {
   314  		fi.ctx.Debugf("nvproxy: NV_ESC_RM_ALLOC_MEMORY class %v", ioctlParams.Params.HClass)
   315  	}
   316  	// See src/nvidia/arch/nvalloc/unix/src/escape.c:RmIoctl() and
   317  	// src/nvidia/interface/deprecated/rmapi_deprecated_allocmemory.c:rmAllocMemoryTable
   318  	// for implementation.
   319  	switch ioctlParams.Params.HClass {
   320  	case nvgpu.NV01_MEMORY_SYSTEM_OS_DESCRIPTOR:
   321  		return rmAllocOSDescriptor(fi, &ioctlParams)
   322  	default:
   323  		fi.ctx.Warningf("nvproxy: unknown NV_ESC_RM_ALLOC_MEMORY class %v", ioctlParams.Params.HClass)
   324  		return 0, linuxerr.EINVAL
   325  	}
   326  }
   327  
   328  func rmAllocOSDescriptor(fi *frontendIoctlState, ioctlParams *nvgpu.IoctlNVOS02ParametersWithFD) (uintptr, error) {
   329  	// Compare src/nvidia/arch/nvalloc/unix/src/escape.c:RmAllocOsDescriptor()
   330  	// => RmCreateOsDescriptor().
   331  	failWithStatus := func(status uint32) error {
   332  		ioctlParams.Params.Status = status
   333  		_, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr)
   334  		return err
   335  	}
   336  	appAddr := addrFromP64(ioctlParams.Params.PMemory)
   337  	if !appAddr.IsPageAligned() {
   338  		return 0, failWithStatus(nvgpu.NV_ERR_NOT_SUPPORTED)
   339  	}
   340  	arLen := ioctlParams.Params.Limit + 1
   341  	if arLen == 0 { // integer overflow
   342  		return 0, failWithStatus(nvgpu.NV_ERR_INVALID_LIMIT)
   343  	}
   344  	var ok bool
   345  	arLen, ok = hostarch.PageRoundUp(arLen)
   346  	if !ok {
   347  		return 0, failWithStatus(nvgpu.NV_ERR_INVALID_ADDRESS)
   348  	}
   349  	appAR, ok := appAddr.ToRange(arLen)
   350  	if !ok {
   351  		return 0, failWithStatus(nvgpu.NV_ERR_INVALID_ADDRESS)
   352  	}
   353  
   354  	// The host driver will collect pages from our address space starting at
   355  	// PMemory, so we must assemble a contiguous mapping equivalent to the
   356  	// application's.
   357  	at := hostarch.Read
   358  	if ((ioctlParams.Params.Flags >> 21) & 0x1) == 0 /* NVOS02_FLAGS_ALLOC_USER_READ_ONLY_NO */ {
   359  		at.Write = true
   360  	}
   361  	// Reserve a range in our address space.
   362  	m, _, errno := unix.RawSyscall6(unix.SYS_MMAP, 0 /* addr */, uintptr(arLen), unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANONYMOUS, ^uintptr(0) /* fd */, 0 /* offset */)
   363  	if errno != 0 {
   364  		return 0, errno
   365  	}
   366  	defer unix.RawSyscall(unix.SYS_MUNMAP, m, uintptr(arLen), 0)
   367  	// Mirror application mappings into the reserved range.
   368  	prs, err := fi.t.MemoryManager().Pin(fi.ctx, appAR, at, false /* ignorePermissions */)
   369  	unpinCleanup := cleanup.Make(func() {
   370  		mm.Unpin(prs)
   371  	})
   372  	defer unpinCleanup.Clean()
   373  	if err != nil {
   374  		return 0, err
   375  	}
   376  	sentryAddr := uintptr(m)
   377  	for _, pr := range prs {
   378  		ims, err := pr.File.MapInternal(memmap.FileRange{pr.Offset, pr.Offset + uint64(pr.Source.Length())}, at)
   379  		if err != nil {
   380  			return 0, err
   381  		}
   382  		for !ims.IsEmpty() {
   383  			im := ims.Head()
   384  			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 {
   385  				return 0, errno
   386  			}
   387  			sentryAddr += uintptr(im.Len())
   388  			ims = ims.Tail()
   389  		}
   390  	}
   391  	origPMemory := ioctlParams.Params.PMemory
   392  	ioctlParams.Params.PMemory = nvgpu.P64(uint64(m))
   393  	// NV01_MEMORY_SYSTEM_OS_DESCRIPTOR shouldn't use ioctlParams.FD; clobber
   394  	// it to be sure.
   395  	origFD := ioctlParams.FD
   396  	ioctlParams.FD = -1
   397  
   398  	fi.fd.dev.nvp.objsLock()
   399  	n, err := frontendIoctlInvoke(fi, ioctlParams)
   400  	if err == nil && ioctlParams.Params.Status == nvgpu.NV_OK {
   401  		// Transfer ownership of pinned pages to an osDescMem object, to be
   402  		// unpinned when the driver OsDescMem is freed.
   403  		fi.fd.dev.nvp.objAdd(fi.ctx, ioctlParams.Params.HRoot, ioctlParams.Params.HObjectNew, nvgpu.NV01_MEMORY_SYSTEM_OS_DESCRIPTOR, &osDescMem{
   404  			pinnedRanges: prs,
   405  		}, ioctlParams.Params.HObjectParent)
   406  		unpinCleanup.Release()
   407  		if fi.ctx.IsLogging(log.Debug) {
   408  			fi.ctx.Debugf("nvproxy: pinned %d bytes for OS descriptor with handle %v", arLen, ioctlParams.Params.HObjectNew)
   409  		}
   410  	}
   411  	fi.fd.dev.nvp.objsUnlock()
   412  	ioctlParams.Params.PMemory = origPMemory
   413  	ioctlParams.FD = origFD
   414  	if err != nil {
   415  		return n, err
   416  	}
   417  
   418  	if _, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr); err != nil {
   419  		return n, err
   420  	}
   421  
   422  	return n, nil
   423  }
   424  
   425  func rmFree(fi *frontendIoctlState) (uintptr, error) {
   426  	var ioctlParams nvgpu.NVOS00Parameters
   427  	if fi.ioctlParamsSize != nvgpu.SizeofNVOS00Parameters {
   428  		return 0, linuxerr.EINVAL
   429  	}
   430  	if _, err := ioctlParams.CopyIn(fi.t, fi.ioctlParamsAddr); err != nil {
   431  		return 0, err
   432  	}
   433  
   434  	fi.fd.dev.nvp.objsLock()
   435  	n, err := frontendIoctlInvoke(fi, &ioctlParams)
   436  	if err == nil && ioctlParams.Status == nvgpu.NV_OK {
   437  		fi.fd.dev.nvp.objFree(fi.ctx, ioctlParams.HRoot, ioctlParams.HObjectOld)
   438  	}
   439  	fi.fd.dev.nvp.objsUnlock()
   440  	if err != nil {
   441  		return n, err
   442  	}
   443  
   444  	if _, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr); err != nil {
   445  		return n, err
   446  	}
   447  	return n, nil
   448  }
   449  
   450  func rmControl(fi *frontendIoctlState) (uintptr, error) {
   451  	var ioctlParams nvgpu.NVOS54Parameters
   452  	if fi.ioctlParamsSize != nvgpu.SizeofNVOS54Parameters {
   453  		return 0, linuxerr.EINVAL
   454  	}
   455  	if _, err := ioctlParams.CopyIn(fi.t, fi.ioctlParamsAddr); err != nil {
   456  		return 0, err
   457  	}
   458  
   459  	// Cmd determines the type of Params.
   460  	if log.IsLogging(log.Debug) {
   461  		fi.ctx.Debugf("nvproxy: control command %#x, object %#x", ioctlParams.Cmd, ioctlParams.HObject.Val)
   462  	}
   463  	if ioctlParams.Cmd&nvgpu.RM_GSS_LEGACY_MASK != 0 {
   464  		// This is a "legacy GSS control" that is implemented by the GPU System
   465  		// Processor (GSP). Conseqeuently, its parameters cannot reasonably
   466  		// contain application pointers, and the control is in any case
   467  		// undocumented.
   468  		// See
   469  		// src/nvidia/src/kernel/rmapi/entry_points.c:_nv04ControlWithSecInfo()
   470  		// =>
   471  		// src/nvidia/interface/deprecated/rmapi_deprecated_control.c:RmDeprecatedGetControlHandler()
   472  		// =>
   473  		// src/nvidia/interface/deprecated/rmapi_gss_legacy_control.c:RmGssLegacyRpcCmd().
   474  		return rmControlSimple(fi, &ioctlParams)
   475  	}
   476  	// Implementors:
   477  	// - Top two bytes of Cmd specifies class; third byte specifies category;
   478  	// fourth byte specifies "message ID" (command within class/category).
   479  	//   e.g. 0x800288:
   480  	//   - Class 0x0080 => look in
   481  	//   src/common/sdk/nvidia/inc/ctrl/ctrl0080/ctrl0080base.h for categories.
   482  	//   - Category 0x02 => NV0080_CTRL_GPU => look in
   483  	//   src/common/sdk/nvidia/inc/ctrl/ctrl0080/ctrl0080gpu.h for
   484  	//   `#define NV0080_CTRL_CMD_GPU_QUERY_SW_STATE_PERSISTENCE (0x800288)`
   485  	//   and accompanying documentation, parameter type.
   486  	// - If this fails, or to find implementation, grep for `methodId=.*0x<Cmd
   487  	// in lowercase hex without leading 0s>` to find entry in g_*_nvoc.c;
   488  	// implementing function is is "pFunc".
   489  	// - Add symbol definition to //pkg/abi/nvgpu. Parameter type definition is
   490  	// only required for non-simple commands.
   491  	// - Add handling below.
   492  	handler := fi.fd.dev.nvp.abi.controlCmd[ioctlParams.Cmd]
   493  	if handler == nil {
   494  		fi.ctx.Warningf("nvproxy: unknown control command %#x (paramsSize=%d)", ioctlParams.Cmd, ioctlParams.ParamsSize)
   495  		return 0, linuxerr.EINVAL
   496  	}
   497  	return handler(fi, &ioctlParams)
   498  }
   499  
   500  func rmControlSimple(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters) (uintptr, error) {
   501  	if ioctlParams.ParamsSize == 0 {
   502  		if ioctlParams.Params != 0 {
   503  			return 0, linuxerr.EINVAL
   504  		}
   505  		return rmControlInvoke[byte](fi, ioctlParams, nil)
   506  	}
   507  	if ioctlParams.Params == 0 {
   508  		return 0, linuxerr.EINVAL
   509  	}
   510  
   511  	ctrlParams := make([]byte, ioctlParams.ParamsSize)
   512  	if _, err := fi.t.CopyInBytes(addrFromP64(ioctlParams.Params), ctrlParams); err != nil {
   513  		return 0, err
   514  	}
   515  	n, err := rmControlInvoke(fi, ioctlParams, &ctrlParams[0])
   516  	if err != nil {
   517  		return n, err
   518  	}
   519  	if _, err := fi.t.CopyOutBytes(addrFromP64(ioctlParams.Params), ctrlParams); err != nil {
   520  		return n, err
   521  	}
   522  	return n, nil
   523  }
   524  
   525  func ctrlCmdFailWithStatus(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters, status uint32) error {
   526  	ioctlParams.Status = status
   527  	_, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr)
   528  	return err
   529  }
   530  
   531  func ctrlHasFrontendFD[Params any, PtrParams hasFrontendFDPtr[Params]](fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters) (uintptr, error) {
   532  	var ctrlParamsValue Params
   533  	ctrlParams := PtrParams(&ctrlParamsValue)
   534  	if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
   535  		return 0, linuxerr.EINVAL
   536  	}
   537  	if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
   538  		return 0, err
   539  	}
   540  
   541  	origFD := ctrlParams.GetFrontendFD()
   542  	ctlFileGeneric, _ := fi.t.FDTable().Get(origFD)
   543  	if ctlFileGeneric == nil {
   544  		return 0, linuxerr.EINVAL
   545  	}
   546  	defer ctlFileGeneric.DecRef(fi.ctx)
   547  	ctlFile, ok := ctlFileGeneric.Impl().(*frontendFD)
   548  	if !ok {
   549  		return 0, linuxerr.EINVAL
   550  	}
   551  
   552  	ctrlParams.SetFrontendFD(ctlFile.hostFD)
   553  	n, err := rmControlInvoke(fi, ioctlParams, ctrlParams)
   554  	ctrlParams.SetFrontendFD(origFD)
   555  	if err != nil {
   556  		return n, err
   557  	}
   558  	if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
   559  		return n, err
   560  	}
   561  	return n, nil
   562  }
   563  
   564  func ctrlClientSystemGetBuildVersion(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters) (uintptr, error) {
   565  	var ctrlParams nvgpu.NV0000_CTRL_SYSTEM_GET_BUILD_VERSION_PARAMS
   566  	if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
   567  		return 0, linuxerr.EINVAL
   568  	}
   569  	if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
   570  		return 0, err
   571  	}
   572  
   573  	if ctrlParams.PDriverVersionBuffer == 0 || ctrlParams.PVersionBuffer == 0 || ctrlParams.PTitleBuffer == 0 {
   574  		// No strings are written if any are null. See
   575  		// src/nvidia/interface/deprecated/rmapi_deprecated_control.c:V2_CONVERTER(_NV0000_CTRL_CMD_SYSTEM_GET_BUILD_VERSION).
   576  		return ctrlClientSystemGetBuildVersionInvoke(fi, ioctlParams, &ctrlParams, nil, nil, nil)
   577  	}
   578  
   579  	// Need to buffer strings for copy-out.
   580  	if ctrlParams.SizeOfStrings == 0 {
   581  		return 0, linuxerr.EINVAL
   582  	}
   583  	driverVersionBuf := make([]byte, ctrlParams.SizeOfStrings)
   584  	versionBuf := make([]byte, ctrlParams.SizeOfStrings)
   585  	titleBuf := make([]byte, ctrlParams.SizeOfStrings)
   586  	n, err := ctrlClientSystemGetBuildVersionInvoke(fi, ioctlParams, &ctrlParams, &driverVersionBuf[0], &versionBuf[0], &titleBuf[0])
   587  	if err != nil {
   588  		return n, err
   589  	}
   590  	if _, err := fi.t.CopyOutBytes(addrFromP64(ctrlParams.PDriverVersionBuffer), driverVersionBuf); err != nil {
   591  		return n, err
   592  	}
   593  	if _, err := fi.t.CopyOutBytes(addrFromP64(ctrlParams.PVersionBuffer), versionBuf); err != nil {
   594  		return n, err
   595  	}
   596  	if _, err := fi.t.CopyOutBytes(addrFromP64(ctrlParams.PTitleBuffer), titleBuf); err != nil {
   597  		return n, err
   598  	}
   599  	return n, nil
   600  }
   601  
   602  func ctrlDevGpuGetClasslist(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters) (uintptr, error) {
   603  	var ctrlParams nvgpu.NV0080_CTRL_GPU_GET_CLASSLIST_PARAMS
   604  	if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
   605  		return 0, linuxerr.EINVAL
   606  	}
   607  	if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
   608  		return 0, err
   609  	}
   610  
   611  	// This command has two modes. If the classList pointer is NULL, only simple command handling
   612  	// is required; see src/common/sdk/nvidia/inc/ctrl/ctrl0080gpu.h.
   613  	if ctrlParams.ClassList == 0 {
   614  		return rmControlSimple(fi, ioctlParams)
   615  	}
   616  
   617  	// classList pointer is not NULL. Check classList size against limit. See
   618  	// src/nvidia/src/kernel/rmapi/embedded_param_copy.c:embeddedParamCopyIn() =>
   619  	// case NV0080_CTRL_CMD_GPU_GET_CLASSLIST => RMAPI_PARAM_COPY_INIT().
   620  	// paramCopy.paramsSize is initialized as numClasses * sizeof(NvU32).
   621  	if ctrlParams.NumClasses*4 > nvgpu.RMAPI_PARAM_COPY_MAX_PARAMS_SIZE {
   622  		return 0, ctrlCmdFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_ARGUMENT)
   623  	}
   624  
   625  	classList := make([]uint32, ctrlParams.NumClasses)
   626  	n, err := ctrlDevGpuGetClasslistInvoke(fi, ioctlParams, &ctrlParams, classList)
   627  	if err != nil {
   628  		return n, err
   629  	}
   630  	return n, nil
   631  }
   632  
   633  func ctrlRegisterVASpace(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters) (uintptr, error) {
   634  	var ctrlParams nvgpu.NV503C_CTRL_REGISTER_VA_SPACE_PARAMS
   635  	if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
   636  		return 0, linuxerr.EINVAL
   637  	}
   638  	if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
   639  		return 0, err
   640  	}
   641  	fi.fd.dev.nvp.objsLock()
   642  	n, err := rmControlInvoke(fi, ioctlParams, &ctrlParams)
   643  	if err == nil && ioctlParams.Status == nvgpu.NV_OK {
   644  		// src/nvidia/src/kernel/gpu/bus/third_party_p2p.c:CliAddThirdPartyP2PVASpace()
   645  		// => refAddDependant()
   646  		fi.fd.dev.nvp.objAddDep(ioctlParams.HClient, ioctlParams.HObject, ctrlParams.HVASpace)
   647  	}
   648  	fi.fd.dev.nvp.objsUnlock()
   649  	if err != nil {
   650  		return n, err
   651  	}
   652  	if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
   653  		return n, err
   654  	}
   655  	return n, nil
   656  }
   657  
   658  func ctrlSubdevFIFODisableChannels(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters) (uintptr, error) {
   659  	var ctrlParams nvgpu.NV2080_CTRL_FIFO_DISABLE_CHANNELS_PARAMS
   660  	if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
   661  		return 0, linuxerr.EINVAL
   662  	}
   663  	if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
   664  		return 0, err
   665  	}
   666  	// This pointer must be NULL; see
   667  	// src/nvidia/src/kernel/gpu/fifo/kernel_fifo_ctrl.c:subdeviceCtrlCmdFifoDisableChannels_IMPL().
   668  	// Consequently, we don't need to translate it, but we do want to ensure
   669  	// that it actually is NULL.
   670  	if ctrlParams.PRunlistPreemptEvent != 0 {
   671  		return 0, linuxerr.EINVAL
   672  	}
   673  	n, err := rmControlInvoke(fi, ioctlParams, &ctrlParams)
   674  	if err != nil {
   675  		return n, err
   676  	}
   677  	if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
   678  		return n, err
   679  	}
   680  	return n, nil
   681  }
   682  
   683  func rmAlloc(fi *frontendIoctlState) (uintptr, error) {
   684  	var isNVOS64 bool
   685  	switch fi.ioctlParamsSize {
   686  	case nvgpu.SizeofNVOS21Parameters:
   687  	case nvgpu.SizeofNVOS64Parameters:
   688  		isNVOS64 = true
   689  	default:
   690  		return 0, linuxerr.EINVAL
   691  	}
   692  	// Copy in parameters and convert to NVOS64Parameters, which is a super
   693  	// set of all parameter types we support.
   694  	buf := nvgpu.GetRmAllocParamObj(isNVOS64)
   695  	if _, err := buf.CopyIn(fi.t, fi.ioctlParamsAddr); err != nil {
   696  		return 0, err
   697  	}
   698  	ioctlParams := buf.ToOS64()
   699  
   700  	// hClass determines the type of pAllocParms.
   701  	if log.IsLogging(log.Debug) {
   702  		fi.ctx.Debugf("nvproxy: allocation class %v", ioctlParams.HClass)
   703  	}
   704  	// Implementors:
   705  	// - To map hClass to a symbol, look in
   706  	// src/nvidia/generated/g_allclasses.h.
   707  	// - See src/nvidia/src/kernel/rmapi/resource_list.h for table mapping class
   708  	// ("External Class") to the type of pAllocParms ("Alloc Param Info") and
   709  	// the class whose constructor interprets it ("Internal Class").
   710  	// - Add symbol and parameter type definitions to //pkg/abi/nvgpu.
   711  	// - Check constructor for calls to refAddDependant(),
   712  	// sessionAddDependant(), or sessionAddDependency(), which need to be
   713  	// mirrored by dependencies in the call to nvproxy.objAddLocked().
   714  	// - Add handling below.
   715  	handler := fi.fd.dev.nvp.abi.allocationClass[ioctlParams.HClass]
   716  	if handler == nil {
   717  		fi.ctx.Warningf("nvproxy: unknown allocation class %v", ioctlParams.HClass)
   718  		// Compare
   719  		// src/nvidia/src/kernel/rmapi/alloc_free.c:serverAllocResourceUnderLock(),
   720  		// when RsResInfoByExternalClassId() is null.
   721  		ioctlParams.Status = nvgpu.NV_ERR_INVALID_CLASS
   722  		outIoctlParams := nvgpu.GetRmAllocParamObj(isNVOS64)
   723  		outIoctlParams.FromOS64(ioctlParams)
   724  		// Any copy-out error from
   725  		// src/nvidia/src/kernel/rmapi/alloc_free.c:serverAllocApiCopyOut() is
   726  		// discarded.
   727  		outIoctlParams.CopyOut(fi.t, fi.ioctlParamsAddr)
   728  		return 0, nil
   729  	}
   730  	return handler(fi, &ioctlParams, isNVOS64)
   731  }
   732  
   733  // rmAllocSimple implements NV_ESC_RM_ALLOC for classes whose parameters don't
   734  // contain any pointers or file descriptors requiring translation, and whose
   735  // objects require no special handling and depend only on their parents.
   736  //
   737  // Unlike frontendIoctlSimple and rmControlSimple, rmAllocSimple requires the
   738  // parameter type since the parameter's size is otherwise unknown.
   739  func rmAllocSimple[Params any, PtrParams marshalPtr[Params]](fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) {
   740  	return rmAllocSimpleParams[Params, PtrParams](fi, ioctlParams, isNVOS64, addSimpleObjDepParentLocked)
   741  }
   742  
   743  // addSimpleObjDepParentLocked implements rmAllocInvoke.addObjLocked for
   744  // classes that require no special handling and depend only on their parents.
   745  func addSimpleObjDepParentLocked[Params any](fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *Params) {
   746  	fi.fd.dev.nvp.objAdd(fi.ctx, ioctlParams.HRoot, ioctlParams.HObjectNew, ioctlParams.HClass, newRmAllocObject(fi.fd, ioctlParams, rightsRequested, allocParams), ioctlParams.HObjectParent)
   747  }
   748  
   749  func rmAllocSimpleParams[Params any, PtrParams marshalPtr[Params]](fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool, objAddLocked func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *Params)) (uintptr, error) {
   750  	if ioctlParams.PAllocParms == 0 {
   751  		return rmAllocInvoke[Params](fi, ioctlParams, nil, isNVOS64, objAddLocked)
   752  	}
   753  
   754  	var allocParamsValue Params
   755  	allocParams := PtrParams(&allocParamsValue)
   756  	if _, err := allocParams.CopyIn(fi.t, addrFromP64(ioctlParams.PAllocParms)); err != nil {
   757  		return 0, err
   758  	}
   759  	n, err := rmAllocInvoke(fi, ioctlParams, allocParams, isNVOS64, objAddLocked)
   760  	if err != nil {
   761  		return n, err
   762  	}
   763  	if _, err := allocParams.CopyOut(fi.t, addrFromP64(ioctlParams.PAllocParms)); err != nil {
   764  		return n, err
   765  	}
   766  	return n, nil
   767  }
   768  
   769  func rmAllocNoParams(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) {
   770  	return rmAllocInvoke[byte](fi, ioctlParams, nil, isNVOS64, addSimpleObjDepParentLocked)
   771  }
   772  
   773  func rmAllocRootClient(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) {
   774  	return rmAllocSimpleParams(fi, ioctlParams, isNVOS64, func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *nvgpu.Handle) {
   775  		fi.fd.dev.nvp.objAdd(fi.ctx, ioctlParams.HRoot, ioctlParams.HObjectNew, ioctlParams.HClass, newRootClient(fi.fd, ioctlParams, rightsRequested, allocParams))
   776  		if fi.fd.clients == nil {
   777  			fi.fd.clients = make(map[nvgpu.Handle]struct{})
   778  		}
   779  		fi.fd.clients[ioctlParams.HObjectNew] = struct{}{}
   780  	})
   781  }
   782  
   783  func rmAllocEventOSEvent(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) {
   784  	var allocParams nvgpu.NV0005_ALLOC_PARAMETERS
   785  	if _, err := allocParams.CopyIn(fi.t, addrFromP64(ioctlParams.PAllocParms)); err != nil {
   786  		return 0, err
   787  	}
   788  	eventFileGeneric, _ := fi.t.FDTable().Get(int32(allocParams.Data))
   789  	if eventFileGeneric == nil {
   790  		return 0, linuxerr.EINVAL
   791  	}
   792  	defer eventFileGeneric.DecRef(fi.ctx)
   793  	eventFile, ok := eventFileGeneric.Impl().(*frontendFD)
   794  	if !ok {
   795  		return 0, linuxerr.EINVAL
   796  	}
   797  	origData := allocParams.Data
   798  	allocParams.Data = nvgpu.P64(uint64(eventFile.hostFD))
   799  
   800  	n, err := rmAllocInvoke(fi, ioctlParams, &allocParams, isNVOS64, func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *nvgpu.NV0005_ALLOC_PARAMETERS) {
   801  		fi.fd.dev.nvp.objAdd(fi.ctx, ioctlParams.HRoot, ioctlParams.HObjectNew, ioctlParams.HClass, &osEvent{}, ioctlParams.HObjectParent)
   802  	})
   803  	if err != nil {
   804  		return n, err
   805  	}
   806  
   807  	allocParams.Data = origData
   808  	if _, err := allocParams.CopyOut(fi.t, addrFromP64(ioctlParams.PAllocParms)); err != nil {
   809  		return n, err
   810  	}
   811  	return n, nil
   812  }
   813  
   814  func rmAllocSMDebuggerSession(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) {
   815  	return rmAllocSimpleParams(fi, ioctlParams, isNVOS64, func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *nvgpu.NV83DE_ALLOC_PARAMETERS) {
   816  		// Compare
   817  		// src/nvidia/src/kernel/gpu/gr/kernel_sm_debugger_session.c:ksmdbgssnConstruct_IMPL()
   818  		// => _ShareDebugger() => sessionAddDependency/sessionAddDependant();
   819  		// the driver indirects through a per-KernelGraphicsObject
   820  		// RmDebuggerSession, which we elide for dependency tracking.
   821  		fi.fd.dev.nvp.objAdd(fi.ctx, ioctlParams.HRoot, ioctlParams.HObjectNew, ioctlParams.HClass, newRmAllocObject(fi.fd, ioctlParams, rightsRequested, allocParams), ioctlParams.HObjectParent, allocParams.HClass3DObject)
   822  	})
   823  }
   824  
   825  func rmAllocChannelGroup(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) {
   826  	return rmAllocSimpleParams(fi, ioctlParams, isNVOS64, func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *nvgpu.NV_CHANNEL_GROUP_ALLOCATION_PARAMETERS) {
   827  		// See
   828  		// src/nvidia/src/kernel/gpu/fifo/kernel_channel_group_api.c:kchangrpapiConstruct_IMPL()
   829  		// => refAddDependant().
   830  		fi.fd.dev.nvp.objAdd(fi.ctx, ioctlParams.HRoot, ioctlParams.HObjectNew, ioctlParams.HClass, newRmAllocObject(fi.fd, ioctlParams, rightsRequested, allocParams), ioctlParams.HObjectParent, allocParams.HVASpace)
   831  		// Note: When the channel group's engine type is GR, which is always
   832  		// true unless MIG is enabled, kchangrpapiConstruct_IMPL() constructs a
   833  		// KERNEL_GRAPHICS_CONTEXT whose lifetime is the same as the channel
   834  		// group's (the graphics context is freed when the channel group is).
   835  		// Channels, context shares, and graphics objects depend on this
   836  		// graphics context rather than the channel group. Consequently, if MIG
   837  		// is enabled, these might not depend on the channel group at all.
   838  		// Since nvproxy currently does not support MIG, we represent these
   839  		// dependencies as unconditionally on the channel group instead.
   840  	})
   841  }
   842  
   843  func rmAllocChannel(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) {
   844  	return rmAllocSimpleParams(fi, ioctlParams, isNVOS64, func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *nvgpu.NV_CHANNEL_ALLOC_PARAMS) {
   845  		// See
   846  		// src/nvidia/src/kernel/gpu/fifo/kernel_channel.c:kchannelConstruct_IMPL()
   847  		// => refAddDependant(). The channel's parent may be a device or
   848  		// channel group; if it is a channel group then the channel depends on
   849  		// it via the parent relationship, and if it is not a channel group
   850  		// then kchannelConstruct_IMPL() constructs one internally and frees it
   851  		// when the channel is destroyed, so either way no separate dependency
   852  		// is required.
   853  		fi.fd.dev.nvp.objAdd(fi.ctx, ioctlParams.HRoot, ioctlParams.HObjectNew, ioctlParams.HClass, newRmAllocObject(fi.fd, ioctlParams, rightsRequested, allocParams), ioctlParams.HObjectParent, allocParams.HVASpace, allocParams.HContextShare)
   854  	})
   855  }
   856  
   857  func rmAllocContextShare(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) {
   858  	return rmAllocSimpleParams(fi, ioctlParams, isNVOS64, func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *nvgpu.NV_CTXSHARE_ALLOCATION_PARAMETERS) {
   859  		// See
   860  		// src/nvidia/src/kernel/gpu/fifo/kernel_ctxshare.c:kctxshareapiConstruct_IMPL()
   861  		// => refAddDependant(). The context share's parent is the channel
   862  		// group, so (given that we are representing graphics context
   863  		// dependencies as channel group dependencies) no separate dependency
   864  		// is required.
   865  		fi.fd.dev.nvp.objAdd(fi.ctx, ioctlParams.HRoot, ioctlParams.HObjectNew, ioctlParams.HClass, newRmAllocObject(fi.fd, ioctlParams, rightsRequested, allocParams), ioctlParams.HObjectParent, allocParams.HVASpace)
   866  	})
   867  }
   868  
   869  func rmVidHeapControl(fi *frontendIoctlState) (uintptr, error) {
   870  	var ioctlParams nvgpu.NVOS32Parameters
   871  	if fi.ioctlParamsSize != nvgpu.SizeofNVOS32Parameters {
   872  		return 0, linuxerr.EINVAL
   873  	}
   874  	if _, err := ioctlParams.CopyIn(fi.t, fi.ioctlParamsAddr); err != nil {
   875  		return 0, err
   876  	}
   877  
   878  	// Function determines the type of Data.
   879  	if fi.ctx.IsLogging(log.Debug) {
   880  		fi.ctx.Debugf("nvproxy: VID_HEAP_CONTROL function %d", ioctlParams.Function)
   881  	}
   882  	// See
   883  	// src/nvidia/interface/deprecated/rmapi_deprecated_vidheapctrl.c:rmVidHeapControlTable
   884  	// for implementation.
   885  	switch ioctlParams.Function {
   886  	case nvgpu.NVOS32_FUNCTION_ALLOC_SIZE:
   887  		return rmVidHeapControlAllocSize(fi, &ioctlParams)
   888  	default:
   889  		fi.ctx.Warningf("nvproxy: unknown VID_HEAP_CONTROL function %d", ioctlParams.Function)
   890  		return 0, linuxerr.EINVAL
   891  	}
   892  }
   893  
   894  func rmMapMemory(fi *frontendIoctlState) (uintptr, error) {
   895  	var ioctlParams nvgpu.IoctlNVOS33ParametersWithFD
   896  	if fi.ioctlParamsSize != nvgpu.SizeofIoctlNVOS33ParametersWithFD {
   897  		return 0, linuxerr.EINVAL
   898  	}
   899  	if _, err := ioctlParams.CopyIn(fi.t, fi.ioctlParamsAddr); err != nil {
   900  		return 0, err
   901  	}
   902  	mapFileGeneric, _ := fi.t.FDTable().Get(ioctlParams.FD)
   903  	if mapFileGeneric == nil {
   904  		return 0, linuxerr.EINVAL
   905  	}
   906  	defer mapFileGeneric.DecRef(fi.ctx)
   907  	mapFile, ok := mapFileGeneric.Impl().(*frontendFD)
   908  	if !ok {
   909  		return 0, linuxerr.EINVAL
   910  	}
   911  	if mapFile.haveMmapContext.Load() || !mapFile.haveMmapContext.CompareAndSwap(false, true) {
   912  		fi.ctx.Warningf("nvproxy: attempted to reuse FD %d for NV_ESC_RM_MAP_MEMORY", ioctlParams.FD)
   913  		return 0, linuxerr.EINVAL
   914  	}
   915  	origFD := ioctlParams.FD
   916  	ioctlParams.FD = mapFile.hostFD
   917  
   918  	n, err := frontendIoctlInvoke(fi, &ioctlParams)
   919  	if err != nil {
   920  		return n, err
   921  	}
   922  
   923  	ioctlParams.FD = origFD
   924  	if _, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr); err != nil {
   925  		return n, err
   926  	}
   927  
   928  	return n, nil
   929  }