mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement the ioctl command VFIO_DEVICE_SET_IRQS.
PiperOrigin-RevId: 618047305
This commit is contained in:
@@ -79,6 +79,7 @@ go_library(
|
||||
"uio.go",
|
||||
"utsname.go",
|
||||
"vfio.go",
|
||||
"vfio_unsafe.go",
|
||||
"wait.go",
|
||||
"xattr.go",
|
||||
],
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user