Implement the ioctl command VFIO_DEVICE_SET_IRQS.

PiperOrigin-RevId: 618047305
This commit is contained in:
Jing Chen
2024-03-21 20:43:12 -07:00
committed by gVisor bot
parent 24251f576d
commit 628f1bad34
6 changed files with 160 additions and 0 deletions
+1
View File
@@ -79,6 +79,7 @@ go_library(
"uio.go",
"utsname.go",
"vfio.go",
"vfio_unsafe.go",
"wait.go",
"xattr.go",
],
+42
View File
@@ -69,6 +69,33 @@ const (
VFIO_IRQ_INFO_NORESIZE
)
// VFIOIrqSet flags.
const (
VFIO_IRQ_SET_DATA_NONE = 1 << iota
VFIO_IRQ_SET_DATA_BOOL
VFIO_IRQ_SET_DATA_EVENTFD
VFIO_IRQ_SET_ACTION_MASK
VFIO_IRQ_SET_ACTION_UNMASK
VFIO_IRQ_SET_ACTION_TRIGGER
VFIO_IRQ_SET_DATA_TYPE_MASK = VFIO_IRQ_SET_DATA_NONE |
VFIO_IRQ_SET_DATA_BOOL |
VFIO_IRQ_SET_DATA_EVENTFD
VFIO_IRQ_SET_ACTION_TYPE_MASK = VFIO_IRQ_SET_ACTION_MASK |
VFIO_IRQ_SET_ACTION_UNMASK |
VFIO_IRQ_SET_ACTION_TRIGGER
)
// VFIOIrqSet index.
const (
VFIO_PCI_INTX_IRQ_INDEX = iota
VFIO_PCI_MSI_IRQ_INDEX
VFIO_PCI_MSIX_IRQ_INDEX
VFIO_PCI_ERR_IRQ_INDEX
VFIO_PCI_REQ_IRQ_INDEX
VFIO_PCI_NUM_IRQS
)
// IOCTLs for VFIO file descriptor from include/uapi/linux/vfio.h.
var (
VFIO_CHECK_EXTENSION = IO(VFIO_TYPE, VFIO_BASE+1)
@@ -78,6 +105,7 @@ var (
VFIO_DEVICE_GET_INFO = IO(VFIO_TYPE, VFIO_BASE+7)
VFIO_DEVICE_GET_REGION_INFO = IO(VFIO_TYPE, VFIO_BASE+8)
VFIO_DEVICE_GET_IRQ_INFO = IO(VFIO_TYPE, VFIO_BASE+9)
VFIO_DEVICE_SET_IRQS = IO(VFIO_TYPE, VFIO_BASE+10)
)
// VFIODeviceInfo is analogous to vfio_device_info
@@ -122,3 +150,17 @@ type VFIOIrqInfo struct {
Index uint32
Count uint32
}
// VFIOIrqSet is analogous to vfio_irq_set
// from include/uapi/linux/vfio.h.
// The last field `data` from vfio_irq_set is omitted which is an
// flexible array member. It will be handled separately.
//
// +marshal
type VFIOIrqSet struct {
Argsz uint32
Flags uint32
Index uint32
Start uint32
Count uint32
}
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linux
import "unsafe"
// Size returns the number of bytes for a VFIOIrqSet object.
func (vfioIrqSet VFIOIrqSet) Size() uint64 {
return uint64(unsafe.Sizeof(vfioIrqSet))
}
+1
View File
@@ -30,6 +30,7 @@ go_library(
"//pkg/safemem",
"//pkg/seccomp",
"//pkg/sentry/arch",
"//pkg/sentry/fsimpl/eventfd",
"//pkg/sentry/kernel",
"//pkg/sentry/memmap",
"//pkg/sentry/vfs",
@@ -81,6 +81,10 @@ func Filters() seccomp.SyscallRules {
seccomp.NonNegativeFD{},
seccomp.EqualTo(linux.VFIO_DEVICE_GET_IRQ_INFO),
},
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(linux.VFIO_DEVICE_SET_IRQS),
},
},
})
}
+90
View File
@@ -26,12 +26,19 @@ import (
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
const (
// A value of -1 can be used to either de-assign interrupts if already
// assigned or skip un-assigned interrupts.
disableInterrupt = -1
)
var (
// vfioDeviceInfoFlags contains all available flags for
// IOCTL command VFIO_DEVICE_GET_INFO.
@@ -39,6 +46,8 @@ var (
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
// vfioIrqSetFlags includes all available flags for IOCTL comamnd VFIO_DEVICE_SET_IRQS
vfioIrqSetFlags uint32 = linux.VFIO_IRQ_SET_DATA_TYPE_MASK | linux.VFIO_IRQ_SET_ACTION_TYPE_MASK
)
// tpuFD implements vfs.FileDescriptionImpl for /dev/vfio/[0-9]+
@@ -231,6 +240,8 @@ func (fd *pciDeviceFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr,
return fd.vfioRegionInfo(ctx, t, args[2].Pointer())
case linux.VFIO_DEVICE_GET_IRQ_INFO:
return fd.vfioIrqInfo(ctx, t, args[2].Pointer())
case linux.VFIO_DEVICE_SET_IRQS:
return fd.vfioSetIrqs(ctx, t, args[2].Pointer())
}
return 0, linuxerr.ENOSYS
}
@@ -300,3 +311,82 @@ func (fd *pciDeviceFD) vfioIrqInfo(ctx context.Context, t *kernel.Task, arg host
}
return ret, nil
}
func (fd *pciDeviceFD) vfioSetIrqs(ctx context.Context, t *kernel.Task, arg hostarch.Addr) (uintptr, error) {
var irqSet linux.VFIOIrqSet
if _, err := irqSet.CopyIn(t, arg); err != nil {
return 0, err
}
// Callers must set the payload's size.
if irqSet.Argsz == 0 {
return 0, linuxerr.EINVAL
}
// Invalidate unknown flags.
if irqSet.Flags&^vfioIrqSetFlags != 0 {
return 0, linuxerr.EINVAL
}
// See drivers/vfio/vfio_main.c:vfio_set_irqs_validate_and_prepare,
// VFIO uses the data type at the request's flags to determine
// the memory layout of data field.
//
// The struct vfio_irq_set includes a flexible array member, it
// allocates an array for a continuous trunk of memory to back
// a vfio_irq_set object. In order to mirror that behavior, gVisor
// would allocate a slice to store the underlying bytes
// and pass that through to its host.
switch irqSet.Flags & linux.VFIO_IRQ_SET_DATA_TYPE_MASK {
// VFIO_IRQ_SET_DATA_NONE indicates there is no data field for
// the IOCTL command.
// It works with VFIO_IRQ_SET_ACTION_MASK, VFIO_IRQ_SET_ACTION_UNMASK,
// or VFIO_IRQ_SET_ACTION_TRIGGER to mask an interrupt, unmask an
// interrupt, and trigger an interrupt unconditionally.
case linux.VFIO_IRQ_SET_DATA_NONE:
// When there is no data, passing through the given payload
// works just fine.
return IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &irqSet)
// VFIO_IRQ_SET_DATA_BOOL indicates that the data field is an array of uint8.
// The action will be performed if the corresponding boolean is true.
case linux.VFIO_IRQ_SET_DATA_BOOL:
payloadSize := uint32(irqSet.Size()) + irqSet.Count
payload := make([]uint8, payloadSize)
if _, err := primitive.CopyUint8SliceIn(t, arg, payload); err != nil {
return 0, err
}
return IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &payload[0])
// VFIO_IRQ_SET_DATA_EVENTFD indicates that the data field is an array
// of int32 (or event file descriptors). These descriptors will be
// signalled when an action in the flags happens.
case linux.VFIO_IRQ_SET_DATA_EVENTFD:
payloadSize := uint32(irqSet.Size())/4 + irqSet.Count
payload := make([]int32, payloadSize)
if _, err := primitive.CopyInt32SliceIn(t, arg, payload); err != nil {
return 0, err
}
// Transform the input FDs to host FDs.
for i := 0; i < int(irqSet.Count); i++ {
index := len(payload) - 1 - i
fd := payload[index]
// Skip non-event FD.
if fd == disableInterrupt {
continue
}
eventFileGeneric, _ := t.FDTable().Get(fd)
if eventFileGeneric == nil {
return 0, linuxerr.EBADF
}
defer eventFileGeneric.DecRef(ctx)
eventFile, ok := eventFileGeneric.Impl().(*eventfd.EventFileDescription)
if !ok {
return 0, linuxerr.EINVAL
}
eventfd, err := eventFile.HostFD()
if err != nil {
return 0, err
}
payload[index] = int32(eventfd)
}
return IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &payload[0])
}
// No data type is specified or multiple data types are specified.
return 0, linuxerr.EINVAL
}