From 73f7d3d3f763ae6f829eda453548ef8e5da9da28 Mon Sep 17 00:00:00 2001 From: Jing Chen Date: Wed, 13 Mar 2024 12:54:12 -0700 Subject: [PATCH] Implement ioctl command VFIO_GROUP_SET_CONTAINER. PiperOrigin-RevId: 615514857 --- pkg/abi/linux/BUILD | 1 + pkg/abi/linux/vfio.go | 28 +++++++++++++++ pkg/sentry/devices/tpuproxy/BUILD | 4 +++ pkg/sentry/devices/tpuproxy/seccomp_filter.go | 6 ++++ pkg/sentry/devices/tpuproxy/tpu.go | 31 +++++++++++++++++ pkg/sentry/devices/tpuproxy/vfio_unsafe.go | 34 +++++++++++++++++++ 6 files changed, 104 insertions(+) create mode 100644 pkg/abi/linux/vfio.go create mode 100644 pkg/sentry/devices/tpuproxy/vfio_unsafe.go diff --git a/pkg/abi/linux/BUILD b/pkg/abi/linux/BUILD index 3e6d5eae1..2b1c75de9 100644 --- a/pkg/abi/linux/BUILD +++ b/pkg/abi/linux/BUILD @@ -78,6 +78,7 @@ go_library( "tty.go", "uio.go", "utsname.go", + "vfio.go", "wait.go", "xattr.go", ], diff --git a/pkg/abi/linux/vfio.go b/pkg/abi/linux/vfio.go new file mode 100644 index 000000000..5943e4735 --- /dev/null +++ b/pkg/abi/linux/vfio.go @@ -0,0 +1,28 @@ +// 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. + +// The package implements VFIOuserspace driver interface. + +package linux + +// For IOCTLs requests from include/uapi/linux/vfio.h. +const ( + VFIO_TYPE = ';' + VFIO_BASE = 100 +) + +// IOCTLs for VFIO file descriptor from include/uapi/linux/vfio.h. +var ( + VFIO_GROUP_SET_CONTAINER = IO(VFIO_TYPE, VFIO_BASE+4) +) diff --git a/pkg/sentry/devices/tpuproxy/BUILD b/pkg/sentry/devices/tpuproxy/BUILD index 10fd936b9..ef2330811 100644 --- a/pkg/sentry/devices/tpuproxy/BUILD +++ b/pkg/sentry/devices/tpuproxy/BUILD @@ -13,6 +13,7 @@ go_library( "tpu_mmap.go", "vfio.go", "vfio_mmap.go", + "vfio_unsafe.go", ], visibility = [ "//pkg/sentry:internal", @@ -25,14 +26,17 @@ go_library( "//pkg/fdnotifier", "//pkg/hostarch", "//pkg/log", + "//pkg/marshal/primitive", "//pkg/safemem", "//pkg/seccomp", "//pkg/sentry/arch", + "//pkg/sentry/kernel", "//pkg/sentry/memmap", "//pkg/sentry/vfs", "//pkg/sync", "//pkg/usermem", "//pkg/waiter", + "@org_golang_x_exp//constraints:go_default_library", "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/pkg/sentry/devices/tpuproxy/seccomp_filter.go b/pkg/sentry/devices/tpuproxy/seccomp_filter.go index f35ddeedf..f989c9ebc 100644 --- a/pkg/sentry/devices/tpuproxy/seccomp_filter.go +++ b/pkg/sentry/devices/tpuproxy/seccomp_filter.go @@ -52,5 +52,11 @@ func Filters() seccomp.SyscallRules { seccomp.AnyValue{}, seccomp.EqualTo(0), }, + unix.SYS_IOCTL: seccomp.Or{ + seccomp.PerArg{ + seccomp.NonNegativeFD{}, + seccomp.EqualTo(linux.VFIO_GROUP_SET_CONTAINER), + }, + }, }) } diff --git a/pkg/sentry/devices/tpuproxy/tpu.go b/pkg/sentry/devices/tpuproxy/tpu.go index 6409e7290..6c1300293 100644 --- a/pkg/sentry/devices/tpuproxy/tpu.go +++ b/pkg/sentry/devices/tpuproxy/tpu.go @@ -19,10 +19,14 @@ import ( "fmt" "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/fdnotifier" + "gvisor.dev/gvisor/pkg/hostarch" + "gvisor.dev/gvisor/pkg/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/arch" + "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/usermem" "gvisor.dev/gvisor/pkg/waiter" @@ -80,5 +84,32 @@ func (fd *tpuFD) Epollable() bool { // Ioctl implements vfs.FileDescriptionImpl.Ioctl. func (fd *tpuFD) 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_GROUP_SET_CONTAINER: + return fd.setContainer(ctx, t, args[2].Pointer()) + } return 0, linuxerr.ENOSYS } + +func (fd *tpuFD) setContainer(ctx context.Context, t *kernel.Task, arg hostarch.Addr) (uintptr, error) { + var vfioContainerFd int32 + if _, err := primitive.CopyInt32In(t, arg, &vfioContainerFd); err != nil { + return 0, err + } + vfioContainerFile, _ := t.FDTable().Get(vfioContainerFd) + if vfioContainerFile == nil { + return 0, linuxerr.EBADF + } + defer vfioContainerFile.DecRef(ctx) + vfioContainer, ok := vfioContainerFile.Impl().(*vfioFd) + if !ok { + return 0, linuxerr.EINVAL + } + return ioctlInvokePtrArg(fd.hostFD, linux.VFIO_GROUP_SET_CONTAINER, &vfioContainer.hostFd) +} diff --git a/pkg/sentry/devices/tpuproxy/vfio_unsafe.go b/pkg/sentry/devices/tpuproxy/vfio_unsafe.go new file mode 100644 index 000000000..3444d99ec --- /dev/null +++ b/pkg/sentry/devices/tpuproxy/vfio_unsafe.go @@ -0,0 +1,34 @@ +// 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 tpuproxy + +import ( + "unsafe" + + "golang.org/x/exp/constraints" + "golang.org/x/sys/unix" +) + +func ioctlInvokePtrArg[Params any](hostFd int32, cmd uint32, params *Params) (uintptr, error) { + return ioctlInvoke[uintptr](hostFd, cmd, uintptr(unsafe.Pointer(params))) +} + +func ioctlInvoke[Arg constraints.Integer](hostFd int32, cmd uint32, arg Arg) (uintptr, error) { + n, _, errno := unix.RawSyscall(unix.SYS_IOCTL, uintptr(hostFd), uintptr(cmd), uintptr(arg)) + if errno != 0 { + return n, errno + } + return n, nil +}