diff --git a/pkg/abi/linux/vfio.go b/pkg/abi/linux/vfio.go index 91346e10a..4d52ff406 100644 --- a/pkg/abi/linux/vfio.go +++ b/pkg/abi/linux/vfio.go @@ -27,10 +27,49 @@ const ( VFIO_TYPE1v2_IOMMU = 3 ) +// VFIO device info flags. +const ( + // Device supports reset. + VFIO_DEVICE_FLAGS_RESET = 1 << iota + // VFIO-pci device. + VFIO_DEVICE_FLAGS_PCI + // VFIO-platform device. + VFIO_DEVICE_FLAGS_PLATFORM + // VFIO-amba device. + VFIO_DEVICE_FLAGS_AMBA + // VFIO-ccw device. + VFIO_DEVICE_FLAGS_CCW + // VFIO-ap device. + VFIO_DEVICE_FLAGS_AP + // VFIO-fsl-mc device. + VFIO_DEVICE_FLAGS_FSL_MC + // Info supports caps. + VFIO_DEVICE_FLAGS_CAPS + // VFIO-cdx device. + VFIO_DEVICE_FLAGS_CDX +) + // IOCTLs for VFIO file descriptor from include/uapi/linux/vfio.h. var ( VFIO_CHECK_EXTENSION = IO(VFIO_TYPE, VFIO_BASE+1) VFIO_SET_IOMMU = IO(VFIO_TYPE, VFIO_BASE+2) VFIO_GROUP_SET_CONTAINER = IO(VFIO_TYPE, VFIO_BASE+4) VFIO_GROUP_GET_DEVICE_FD = IO(VFIO_TYPE, VFIO_BASE+6) + VFIO_DEVICE_GET_INFO = IO(VFIO_TYPE, VFIO_BASE+7) ) + +// VFIODeviceInfo is analogous to vfio_device_info +// from include/uapi/linux/vfio.h. +// +// +marshal +type VFIODeviceInfo struct { + Argsz uint32 + Flags uint32 + // The total amount of regions. + NumRegions uint32 + // The maximum number of IRQ. + NumIrqs uint32 + // Offset within info struct of first cap. + CapOffset uint32 + pad uint32 +} diff --git a/pkg/sentry/devices/tpuproxy/seccomp_filter.go b/pkg/sentry/devices/tpuproxy/seccomp_filter.go index 307bd72e7..3d94f9dee 100644 --- a/pkg/sentry/devices/tpuproxy/seccomp_filter.go +++ b/pkg/sentry/devices/tpuproxy/seccomp_filter.go @@ -69,6 +69,10 @@ func Filters() seccomp.SyscallRules { seccomp.NonNegativeFD{}, seccomp.EqualTo(linux.VFIO_GROUP_GET_DEVICE_FD), }, + seccomp.PerArg{ + seccomp.NonNegativeFD{}, + seccomp.EqualTo(linux.VFIO_DEVICE_GET_INFO), + }, }, }) } diff --git a/pkg/sentry/devices/tpuproxy/tpu.go b/pkg/sentry/devices/tpuproxy/tpu.go index 0a558a05e..c87bce874 100644 --- a/pkg/sentry/devices/tpuproxy/tpu.go +++ b/pkg/sentry/devices/tpuproxy/tpu.go @@ -32,6 +32,15 @@ import ( "gvisor.dev/gvisor/pkg/waiter" ) +var ( + // vfioDeviceInfoFlags contains all available flags for + // IOCTL command VFIO_DEVICE_GET_INFO. + vfioDeviceInfoFlags uint32 = linux.VFIO_DEVICE_FLAGS_RESET | linux.VFIO_DEVICE_FLAGS_PCI | + linux.VFIO_DEVICE_FLAGS_PLATFORM | linux.VFIO_DEVICE_FLAGS_AMBA | + linux.VFIO_DEVICE_FLAGS_CCW | linux.VFIO_DEVICE_FLAGS_AP | linux.VFIO_DEVICE_FLAGS_FSL_MC | + linux.VFIO_DEVICE_FLAGS_CAPS | linux.VFIO_DEVICE_FLAGS_CDX +) + // tpuFD implements vfs.FileDescriptionImpl for /dev/vfio/[0-9]+ // // tpuFD is not savable until TPU save/restore is needed. @@ -135,8 +144,8 @@ func (fd *tpuFD) getPciDeviceFd(t *kernel.Task, arg hostarch.Addr) (uintptr, fun if err != nil { return 0, func() {}, err } - pciDevFD := &pciDeviceFd{ - hostFd: int32(hostFD), + pciDevFD := &pciDeviceFD{ + hostFD: int32(hostFD), } cleanup := func() { unix.Close(int(hostFD)) @@ -160,28 +169,28 @@ func (fd *tpuFD) getPciDeviceFd(t *kernel.Task, arg hostarch.Addr) (uintptr, fun } // pciDeviceFD implements vfs.FileDescriptionImpl for TPU's PCI device. -type pciDeviceFd struct { +type pciDeviceFD struct { vfsfd vfs.FileDescription vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD - hostFd int32 + hostFD int32 queue waiter.Queue memmapFile tpuFDMemmapFile } // Release implements vfs.FileDescriptionImpl.Release. -func (fd *pciDeviceFd) Release(context.Context) { - fdnotifier.RemoveFD(fd.hostFd) +func (fd *pciDeviceFD) Release(context.Context) { + fdnotifier.RemoveFD(fd.hostFD) fd.queue.Notify(waiter.EventHUp) - unix.Close(int(fd.hostFd)) + unix.Close(int(fd.hostFD)) } // EventRegister implements waiter.Waitable.EventRegister. -func (fd *pciDeviceFd) EventRegister(e *waiter.Entry) error { +func (fd *pciDeviceFD) EventRegister(e *waiter.Entry) error { fd.queue.EventRegister(e) - if err := fdnotifier.UpdateFD(fd.hostFd); err != nil { + if err := fdnotifier.UpdateFD(fd.hostFD); err != nil { fd.queue.EventUnregister(e) return err } @@ -189,24 +198,59 @@ func (fd *pciDeviceFd) EventRegister(e *waiter.Entry) error { } // EventUnregister implements waiter.Waitable.EventUnregister. -func (fd *pciDeviceFd) EventUnregister(e *waiter.Entry) { +func (fd *pciDeviceFD) EventUnregister(e *waiter.Entry) { fd.queue.EventUnregister(e) - if err := fdnotifier.UpdateFD(fd.hostFd); err != nil { + if err := fdnotifier.UpdateFD(fd.hostFD); err != nil { panic(fmt.Sprint("UpdateFD:", err)) } } // Readiness implements waiter.Waitable.Readiness. -func (fd *pciDeviceFd) Readiness(mask waiter.EventMask) waiter.EventMask { - return fdnotifier.NonBlockingPoll(fd.hostFd, mask) +func (fd *pciDeviceFD) Readiness(mask waiter.EventMask) waiter.EventMask { + return fdnotifier.NonBlockingPoll(fd.hostFD, mask) } // Epollable implements vfs.FileDescriptionImpl.Epollable. -func (fd *pciDeviceFd) Epollable() bool { +func (fd *pciDeviceFD) Epollable() bool { return true } // Ioctl implements vfs.FileDescriptionImpl.Ioctl. -func (fd *pciDeviceFd) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { +func (fd *pciDeviceFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { + cmd := args[1].Uint() + + t := kernel.TaskFromContext(ctx) + if t == nil { + panic("Ioctl should be called from a task context") + } + switch cmd { + case linux.VFIO_DEVICE_GET_INFO: + return fd.vfioDeviceInfo(ctx, t, args[2].Pointer()) + } return 0, linuxerr.ENOSYS } + +func (fd *pciDeviceFD) vfioDeviceInfo(ctx context.Context, t *kernel.Task, arg hostarch.Addr) (uintptr, error) { + var deviceInfo linux.VFIODeviceInfo + if _, err := deviceInfo.CopyIn(t, arg); err != nil { + return 0, err + } + // Callers must set VFIODevice.Argsz. + if deviceInfo.Argsz == 0 { + return 0, linuxerr.EINVAL + } + if deviceInfo.Flags&^vfioDeviceInfoFlags != 0 { + return 0, linuxerr.EINVAL + } + ret, err := IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_GET_INFO, &deviceInfo) + if err != nil { + return 0, err + } + // gVisor is not supposed to change any device information that is + // returned from the host since gVisor doesn't own the device. + // Passing the device info back to the caller will be just fine. + if _, err := deviceInfo.CopyOut(t, arg); err != nil { + return 0, err + } + return ret, nil +} diff --git a/pkg/sentry/devices/tpuproxy/tpu_mmap.go b/pkg/sentry/devices/tpuproxy/tpu_mmap.go index 797b59ce4..553b9c09a 100644 --- a/pkg/sentry/devices/tpuproxy/tpu_mmap.go +++ b/pkg/sentry/devices/tpuproxy/tpu_mmap.go @@ -84,26 +84,26 @@ func (mf *tpuFDMemmapFile) FD() int { } // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap. -func (fd *pciDeviceFd) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error { +func (fd *pciDeviceFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error { return vfs.GenericConfigureMMap(&fd.vfsfd, fd, opts) } // AddMapping implements memmap.Mappable.AddMapping. -func (fd *pciDeviceFd) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error { +func (fd *pciDeviceFD) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error { return nil } // RemoveMapping implements memmap.Mappable.RemoveMapping. -func (fd *pciDeviceFd) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) { +func (fd *pciDeviceFD) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) { } // CopyMapping implements memmap.Mappable.CopyMapping. -func (fd *pciDeviceFd) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error { +func (fd *pciDeviceFD) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error { return nil } // Translate implements memmap.Mappable.Translate. -func (fd *pciDeviceFd) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) { +func (fd *pciDeviceFD) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) { return []memmap.Translation{ { Source: optional, @@ -115,12 +115,12 @@ func (fd *pciDeviceFd) Translate(ctx context.Context, required, optional memmap. } // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable. -func (fd *pciDeviceFd) InvalidateUnsavable(ctx context.Context) error { +func (fd *pciDeviceFD) InvalidateUnsavable(ctx context.Context) error { return nil } type pciDeviceFdMemmapFile struct { - fd *pciDeviceFd + fd *pciDeviceFD } // IncRef implements memmap.File.IncRef. @@ -139,5 +139,5 @@ func (mf *pciDeviceFdMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.Ac // FD implements memmap.File.FD. func (mf *pciDeviceFdMemmapFile) FD() int { - return int(mf.fd.hostFd) + return int(mf.fd.hostFD) }