summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/solaris
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/solaris')
-rw-r--r--src/VBox/Additions/solaris/.scm-settings54
-rw-r--r--src/VBox/Additions/solaris/DRM/Makefile.kmk71
-rw-r--r--src/VBox/Additions/solaris/DRM/deps.asm47
-rw-r--r--src/VBox/Additions/solaris/DRM/include/drm.h820
-rw-r--r--src/VBox/Additions/solaris/DRM/include/drmP.h908
-rw-r--r--src/VBox/Additions/solaris/DRM/include/drm_atomic.h94
-rw-r--r--src/VBox/Additions/solaris/DRM/include/drm_linux_list.h71
-rw-r--r--src/VBox/Additions/solaris/DRM/include/queue.h585
-rw-r--r--src/VBox/Additions/solaris/DRM/vboxvideo_drm.c409
-rwxr-xr-xsrc/VBox/Additions/solaris/Installer/VBox.sh61
-rwxr-xr-xsrc/VBox/Additions/solaris/Installer/makepackage.sh160
-rwxr-xr-xsrc/VBox/Additions/solaris/Installer/postinstall.sh448
-rwxr-xr-xsrc/VBox/Additions/solaris/Installer/preremove.sh98
-rwxr-xr-xsrc/VBox/Additions/solaris/Installer/vbox_vendor_select88
-rw-r--r--src/VBox/Additions/solaris/Installer/vboxguest.depend1
-rw-r--r--src/VBox/Additions/solaris/Installer/vboxguest.pkginfo15
-rwxr-xr-xsrc/VBox/Additions/solaris/Installer/vboxguest.sh310
-rw-r--r--src/VBox/Additions/solaris/Installer/vboxguest.space5
-rw-r--r--src/VBox/Additions/solaris/Installer/vboxservice.xml88
-rw-r--r--src/VBox/Additions/solaris/Makefile.kmk413
-rw-r--r--src/VBox/Additions/solaris/Mouse/Makefile.kmk81
-rw-r--r--src/VBox/Additions/solaris/Mouse/deps.asm49
-rw-r--r--src/VBox/Additions/solaris/Mouse/testcase/Makefile.kup0
-rw-r--r--src/VBox/Additions/solaris/Mouse/testcase/solaris.h454
-rw-r--r--src/VBox/Additions/solaris/Mouse/testcase/tstVBoxMouse-solaris.c170
-rw-r--r--src/VBox/Additions/solaris/Mouse/vboxms.c1450
-rw-r--r--src/VBox/Additions/solaris/Mouse/vboxms.conf42
-rw-r--r--src/VBox/Additions/solaris/Mouse/vboxmslnk.c217
-rw-r--r--src/VBox/Additions/solaris/Mouse/vboxmslnk.xml92
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/Makefile.kmk113
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/deps.asm49
-rwxr-xr-xsrc/VBox/Additions/solaris/SharedFolders/loadfs.sh105
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/vboxfs.h106
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c179
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c1070
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h200
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c641
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h90
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c2500
-rw-r--r--src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h99
-rw-r--r--src/VBox/Additions/solaris/Virtio/Makefile.kmk68
-rw-r--r--src/VBox/Additions/solaris/Virtio/Virtio-solaris.c224
-rw-r--r--src/VBox/Additions/solaris/Virtio/Virtio-solaris.h205
-rw-r--r--src/VBox/Additions/solaris/Virtio/VirtioNet-solaris.c852
-rw-r--r--src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.c665
-rw-r--r--src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.h48
-rw-r--r--src/VBox/Additions/solaris/Virtio/VirtioRing-solaris.c148
47 files changed, 14663 insertions, 0 deletions
diff --git a/src/VBox/Additions/solaris/.scm-settings b/src/VBox/Additions/solaris/.scm-settings
new file mode 100644
index 00000000..d395b87c
--- /dev/null
+++ b/src/VBox/Additions/solaris/.scm-settings
@@ -0,0 +1,54 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for the Solaris guest additions.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# The whole lot is dual licensed.
+--license-ose-dual
+
+--filter-out-dirs "/DRM/include/."
+
+--filter-out-files /Installer/vboxguest.depend
+--filter-out-files /Installer/vboxguest.pkginfo
+--filter-out-files /Installer/vboxguest.space
+/Installer/postinstall.sh: --no-convert-tabs --license-ose-dual
+/Installer/vbox_vendor_select: --treat-as .sh --license-based-on-mit --no-convert-tabs
+
+/Mouse/vboxms.conf: --treat-as .sh
+
+--filter-out-dirs "/SharedFolders/solaris10/sys/."
+/SharedFolders/*: --no-convert-tabs
+
+/DRM/vboxvideo_drm.c: --no-convert-tabs
+
diff --git a/src/VBox/Additions/solaris/DRM/Makefile.kmk b/src/VBox/Additions/solaris/DRM/Makefile.kmk
new file mode 100644
index 00000000..3d6561e8
--- /dev/null
+++ b/src/VBox/Additions/solaris/DRM/Makefile.kmk
@@ -0,0 +1,71 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the vboxvideo DRM module (Solaris kernel OpenGL module).
+#
+
+#
+# Copyright (C) 2009-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#ifneq ($(KBUILD_HOST),solaris)
+#$(error "The Solaris guest additions can only be built on Solaris!")
+#endif
+
+#
+# vboxvideo - The Video DRM (Direct Rendering Module) kernel module
+#
+SYSMODS.solaris += vboxvideo
+vboxvideo_TEMPLATE = VBoxGuestR0Drv
+vboxvideo_DEFS = VBOX_WITH_HGCM VBOX_SVN_REV=$(VBOX_SVN_REV)
+vboxvideo_DEPS += $(VBOX_SVN_REV_KMK)
+if ($(VBOX_SOLARIS_11_UPDATE_VERSION) > 3)
+ vboxvideo_DEFS += VBOX_WITH_SYSTEM_QUEUE_H
+endif
+vboxvideo_INCS := \
+ include/
+vboxvideo_SOURCES = \
+ vboxvideo_drm.c
+vboxvideo_LIBS = \
+ $(VBOX_LIB_VBGL_R0) \
+ $(VBOX_LIB_IPRT_GUEST_R0)
+ifeq ($(KBUILD_HOST),solaris)
+ vboxvideo_LDFLAGS += -N misc/drm
+else
+ vboxvideo_SOURCES += deps.asm
+ vboxvideo_deps.asm_ASFLAGS = -f bin -g null
+endif
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/solaris/DRM/deps.asm b/src/VBox/Additions/solaris/DRM/deps.asm
new file mode 100644
index 00000000..e191b02c
--- /dev/null
+++ b/src/VBox/Additions/solaris/DRM/deps.asm
@@ -0,0 +1,47 @@
+; $Id: deps.asm $
+;; @file
+; Solaris kernel module dependency
+;
+
+;
+; Copyright (C) 2012-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/solaris/kmoddeps.mac"
+
+kmoddeps_header ; ELF header, section table and shared string table
+
+kmoddeps_dynstr_start ; ELF .dynstr section
+kmoddeps_dynstr_string str_misc_ctf, "misc/ctf"
+kmoddeps_dynstr_end
+
+kmoddeps_dynamic_start ; ELF .dynamic section
+kmoddeps_dynamic_needed str_misc_ctf
+kmoddeps_dynamic_end
diff --git a/src/VBox/Additions/solaris/DRM/include/drm.h b/src/VBox/Additions/solaris/DRM/include/drm.h
new file mode 100644
index 00000000..5bff1e1e
--- /dev/null
+++ b/src/VBox/Additions/solaris/DRM/include/drm.h
@@ -0,0 +1,820 @@
+/* BEGIN CSTYLED */
+
+/**
+ * \file drm.h
+ * Header for the Direct Rendering Manager
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ * \par Acknowledgments:
+ * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic \c cmpxchg.
+ */
+
+/*
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \mainpage
+ *
+ * The Direct Rendering Manager (DRM) is a device-independent kernel-level
+ * device driver that provides support for the XFree86 Direct Rendering
+ * Infrastructure (DRI).
+ *
+ * The DRM supports the Direct Rendering Infrastructure (DRI) in four major
+ * ways:
+ * -# The DRM provides synchronized access to the graphics hardware via
+ * the use of an optimized two-tiered lock.
+ * -# The DRM enforces the DRI security policy for access to the graphics
+ * hardware by only allowing authenticated X11 clients access to
+ * restricted regions of memory.
+ * -# The DRM provides a generic DMA engine, complete with multiple
+ * queues and the ability to detect the need for an OpenGL context
+ * switch.
+ * -# The DRM is extensible via the use of small device-specific modules
+ * that rely extensively on the API exported by the DRM module.
+ *
+ */
+
+/*
+ * Copyright 2009-2010 Oracle Corporation
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DRM_H_
+#define _DRM_H_
+
+#include <sys/types32.h>
+
+#ifndef __user
+#define __user
+#endif
+
+#ifdef __GNUC__
+# define DEPRECATED __attribute__ ((deprecated))
+#else
+# define DEPRECATED
+# define __volatile__ volatile
+#endif
+
+#if defined(__linux__)
+#include <asm/ioctl.h> /* For _IO* macros */
+#define DRM_IOCTL_NR(n) _IOC_NR(n)
+#define DRM_IOC_VOID _IOC_NONE
+#define DRM_IOC_READ _IOC_READ
+#define DRM_IOC_WRITE _IOC_WRITE
+#define DRM_IOC_READWRITE _IOC_READ|_IOC_WRITE
+#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size)
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && defined(IN_MODULE)
+/* Prevent name collision when including sys/ioccom.h */
+#undef ioctl
+#include <sys/ioccom.h>
+#define ioctl(a,b,c) xf86ioctl(a,b,c)
+#else
+#include <sys/ioccom.h>
+#endif /* __FreeBSD__ && xf86ioctl */
+#define DRM_IOCTL_NR(n) ((n) & 0xff)
+#define DRM_IOC_VOID IOC_VOID
+#define DRM_IOC_READ IOC_OUT
+#define DRM_IOC_WRITE IOC_IN
+#define DRM_IOC_READWRITE IOC_INOUT
+#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size)
+#endif
+
+/* Solaris-specific. */
+#if defined(__SOLARIS__) || defined(sun)
+#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
+
+#define _IOC_NRBITS 8
+#define _IOC_TYPEBITS 8
+#define _IOC_SIZEBITS 14
+#define _IOC_DIRBITS 2
+
+#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
+#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
+#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
+#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
+
+#define _IOC_NRSHIFT 0
+#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
+#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
+#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
+
+#define _IOC_NONE 0U
+#define _IOC_WRITE 1U
+#define _IOC_READ 2U
+
+#define _IOC(dir, type, nr, size) \
+ (((dir) << _IOC_DIRSHIFT) | \
+ ((type) << _IOC_TYPESHIFT) | \
+ ((nr) << _IOC_NRSHIFT) | \
+ ((size) << _IOC_SIZESHIFT))
+
+/* used for X server compile */
+#if !defined(_KERNEL)
+#define _IO(type, nr) _IOC(_IOC_NONE, (type), (nr), 0)
+#define _IOR(type, nr, size) _IOC(_IOC_READ, (type), (nr), sizeof (size))
+#define _IOW(type, nr, size) _IOC(_IOC_WRITE, (type), (nr), sizeof (size))
+#define _IOWR(type, nr, size) _IOC(_IOC_READ|_IOC_WRITE, \
+ (type), (nr), sizeof (size))
+
+#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
+#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
+#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
+#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
+
+#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
+#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
+#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
+#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
+#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
+#endif /* _KERNEL */
+
+#define DRM_IOCTL_NR(n) _IOC_NR(n)
+#define DRM_IOC_VOID IOC_VOID
+#define DRM_IOC_READ IOC_OUT
+#define DRM_IOC_WRITE IOC_IN
+#define DRM_IOC_READWRITE IOC_INOUT
+#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size)
+
+#endif /* __Solaris__ or sun */
+#define XFREE86_VERSION(major,minor,patch,snap) \
+ ((major << 16) | (minor << 8) | patch)
+
+#ifndef CONFIG_XFREE86_VERSION
+#define CONFIG_XFREE86_VERSION XFREE86_VERSION(4,1,0,0)
+#endif
+
+#if CONFIG_XFREE86_VERSION < XFREE86_VERSION(4,1,0,0)
+#define DRM_PROC_DEVICES "/proc/devices"
+#define DRM_PROC_MISC "/proc/misc"
+#define DRM_PROC_DRM "/proc/drm"
+#define DRM_DEV_DRM "/dev/drm"
+#define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
+#define DRM_DEV_UID 0
+#define DRM_DEV_GID 0
+#endif
+
+#if CONFIG_XFREE86_VERSION >= XFREE86_VERSION(4,1,0,0)
+#ifdef __OpenBSD__
+#define DRM_MAJOR 81
+#endif
+#if defined(__linux__) || defined(__NetBSD__)
+#define DRM_MAJOR 226
+#endif
+#define DRM_MAX_MINOR 15
+#endif
+#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */
+#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */
+#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */
+#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */
+
+#define _DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */
+#define _DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */
+#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD)
+#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT)
+#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT))
+
+#if defined(__linux__)
+#if defined(__KERNEL__)
+typedef __u64 drm_u64_t;
+#else
+typedef unsigned long long drm_u64_t;
+#endif
+
+typedef unsigned int drm_handle_t;
+#else
+#include <sys/types.h>
+typedef uint64_t drm_u64_t;
+typedef unsigned long long drm_handle_t; /**< To mapped regions */
+#endif
+typedef unsigned int drm_context_t; /**< GLXContext handle */
+typedef unsigned int drm_drawable_t;
+typedef unsigned int drm_magic_t; /**< Magic for authentication */
+
+/**
+ * Cliprect.
+ *
+ * \warning If you change this structure, make sure you change
+ * XF86DRIClipRectRec in the server as well
+ *
+ * \note KW: Actually it's illegal to change either for
+ * backwards-compatibility reasons.
+ */
+typedef struct drm_clip_rect {
+ unsigned short x1;
+ unsigned short y1;
+ unsigned short x2;
+ unsigned short y2;
+} drm_clip_rect_t;
+
+/**
+ * Drawable information.
+ */
+typedef struct drm_drawable_info {
+ unsigned int num_rects;
+ drm_clip_rect_t *rects;
+} drm_drawable_info_t;
+
+/**
+ * Texture region,
+ */
+typedef struct drm_tex_region {
+ unsigned char next;
+ unsigned char prev;
+ unsigned char in_use;
+ unsigned char padding;
+ unsigned int age;
+} drm_tex_region_t;
+
+/**
+ * Hardware lock.
+ *
+ * The lock structure is a simple cache-line aligned integer. To avoid
+ * processor bus contention on a multiprocessor system, there should not be any
+ * other data stored in the same cache line.
+ */
+typedef struct drm_hw_lock {
+ __volatile__ unsigned int lock; /**< lock variable */
+ char padding[60]; /**< Pad to cache line */
+} drm_hw_lock_t;
+
+/* This is beyond ugly, and only works on GCC. However, it allows me to use
+ * drm.h in places (i.e., in the X-server) where I can't use size_t. The real
+ * fix is to use uint32_t instead of size_t, but that fix will break existing
+ * LP64 (i.e., PowerPC64, SPARC64, IA-64, Alpha, etc.) systems. That *will*
+ * eventually happen, though. I chose 'unsigned long' to be the fallback type
+ * because that works on all the platforms I know about. Hopefully, the
+ * real fix will happen before that bites us.
+ */
+
+#ifdef __SIZE_TYPE__
+# define DRM_SIZE_T __SIZE_TYPE__
+#else
+#if !defined(__SOLARIS__) && !defined(sun)
+# warning "__SIZE_TYPE__ not defined. Assuming sizeof(size_t) == sizeof(unsigned long)!"
+#endif
+# define DRM_SIZE_T unsigned long
+#endif
+
+/**
+ * DRM_IOCTL_VERSION ioctl argument type.
+ *
+ * \sa drmGetVersion().
+ */
+typedef struct drm_version {
+ int version_major; /**< Major version */
+ int version_minor; /**< Minor version */
+ int version_patchlevel; /**< Patch level */
+ DRM_SIZE_T name_len; /**< Length of name buffer */
+ char __user *name; /**< Name of driver */
+ DRM_SIZE_T date_len; /**< Length of date buffer */
+ char __user *date; /**< User-space buffer to hold date */
+ DRM_SIZE_T desc_len; /**< Length of desc buffer */
+ char __user *desc; /**< User-space buffer to hold desc */
+} drm_version_t;
+
+/**
+ * DRM_IOCTL_GET_UNIQUE ioctl argument type.
+ *
+ * \sa drmGetBusid() and drmSetBusId().
+ */
+typedef struct drm_unique {
+ DRM_SIZE_T unique_len; /**< Length of unique */
+ char __user *unique; /**< Unique name for driver instantiation */
+} drm_unique_t;
+
+#undef DRM_SIZE_T
+
+typedef struct drm_list {
+ int count; /**< Length of user-space structures */
+ drm_version_t __user *version;
+} drm_list_t;
+
+typedef struct drm_block {
+ int unused;
+} drm_block_t;
+
+/**
+ * DRM_IOCTL_CONTROL ioctl argument type.
+ *
+ * \sa drmCtlInstHandler() and drmCtlUninstHandler().
+ */
+typedef struct drm_control {
+ enum {
+ DRM_ADD_COMMAND,
+ DRM_RM_COMMAND,
+ DRM_INST_HANDLER,
+ DRM_UNINST_HANDLER
+ } func;
+ int irq;
+} drm_control_t;
+
+/**
+ * Type of memory to map.
+ */
+typedef enum drm_map_type {
+ _DRM_FRAME_BUFFER = 0, /**< WC (no caching), no core dump */
+ _DRM_REGISTERS = 1, /**< no caching, no core dump */
+ _DRM_SHM = 2, /**< shared, cached */
+ _DRM_AGP = 3, /**< AGP/GART */
+ _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */
+ _DRM_CONSISTENT = 5, /**< Consistent memory for PCI DMA */
+ _DRM_TTM = 6
+} drm_map_type_t;
+
+/**
+ * Memory mapping flags.
+ */
+typedef enum drm_map_flags {
+ _DRM_RESTRICTED = 0x01, /**< Cannot be mapped to user-virtual */
+ _DRM_READ_ONLY = 0x02,
+ _DRM_LOCKED = 0x04, /**< shared, cached, locked */
+ _DRM_KERNEL = 0x08, /**< kernel requires access */
+ _DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */
+ _DRM_CONTAINS_LOCK = 0x20, /**< SHM page that contains lock */
+ _DRM_REMOVABLE = 0x40, /**< Removable mapping */
+ _DRM_DRIVER = 0x80 /**< Managed by driver */
+} drm_map_flags_t;
+
+typedef struct drm_ctx_priv_map {
+ unsigned int ctx_id; /**< Context requesting private mapping */
+ void *handle; /**< Handle of map */
+} drm_ctx_priv_map_t;
+
+/**
+ * DRM_IOCTL_GET_MAP, DRM_IOCTL_ADD_MAP and DRM_IOCTL_RM_MAP ioctls
+ * argument type.
+ *
+ * \sa drmAddMap().
+ */
+typedef struct drm_map {
+ unsigned long long offset; /**< Requested physical address (0 for SAREA)*/
+ unsigned long long handle;
+ /**< User-space: "Handle" to pass to mmap() */
+ /**< Kernel-space: kernel-virtual address */
+ unsigned long size; /**< Requested physical size (bytes) */
+ drm_map_type_t type; /**< Type of memory to map */
+ drm_map_flags_t flags; /**< Flags */
+ int mtrr; /**< MTRR slot used */
+ /* Private data */
+} drm_map_t;
+
+/**
+ * DRM_IOCTL_GET_CLIENT ioctl argument type.
+ */
+typedef struct drm_client {
+ int idx; /**< Which client desired? */
+ int auth; /**< Is client authenticated? */
+ unsigned long pid; /**< Process ID */
+ unsigned long uid; /**< User ID */
+ unsigned long magic; /**< Magic */
+ unsigned long iocs; /**< Ioctl count */
+} drm_client_t;
+
+typedef enum {
+ _DRM_STAT_LOCK,
+ _DRM_STAT_OPENS,
+ _DRM_STAT_CLOSES,
+ _DRM_STAT_IOCTLS,
+ _DRM_STAT_LOCKS,
+ _DRM_STAT_UNLOCKS,
+ _DRM_STAT_VALUE, /**< Generic value */
+ _DRM_STAT_BYTE, /**< Generic byte counter (1024bytes/K) */
+ _DRM_STAT_COUNT, /**< Generic non-byte counter (1000/k) */
+
+ _DRM_STAT_IRQ, /**< IRQ */
+ _DRM_STAT_PRIMARY, /**< Primary DMA bytes */
+ _DRM_STAT_SECONDARY, /**< Secondary DMA bytes */
+ _DRM_STAT_DMA, /**< DMA */
+ _DRM_STAT_SPECIAL, /**< Special DMA (e.g., priority or polled) */
+ _DRM_STAT_MISSED /**< Missed DMA opportunity */
+ /* Add to the *END* of the list */
+} drm_stat_type_t;
+
+/**
+ * DRM_IOCTL_GET_STATS ioctl argument type.
+ */
+typedef struct drm_stats {
+ unsigned long count;
+ struct {
+ unsigned long value;
+ drm_stat_type_t type;
+ } data[15];
+} drm_stats_t;
+
+/**
+ * Hardware locking flags.
+ */
+typedef enum drm_lock_flags {
+ _DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */
+ _DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */
+ _DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */
+ _DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */
+ /* These *HALT* flags aren't supported yet
+ -- they will be used to support the
+ full-screen DGA-like mode. */
+ _DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */
+ _DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */
+} drm_lock_flags_t;
+
+/**
+ * DRM_IOCTL_LOCK, DRM_IOCTL_UNLOCK and DRM_IOCTL_FINISH ioctl argument type.
+ *
+ * \sa drmGetLock() and drmUnlock().
+ */
+typedef struct drm_lock {
+ int context;
+ drm_lock_flags_t flags;
+} drm_lock_t;
+
+/**
+ * DMA flags
+ *
+ * \warning
+ * These values \e must match xf86drm.h.
+ *
+ * \sa drm_dma.
+ */
+typedef enum drm_dma_flags {
+ /* Flags for DMA buffer dispatch */
+ _DRM_DMA_BLOCK = 0x01, /**<
+ * Block until buffer dispatched.
+ *
+ * \note The buffer may not yet have
+ * been processed by the hardware --
+ * getting a hardware lock with the
+ * hardware quiescent will ensure
+ * that the buffer has been
+ * processed.
+ */
+ _DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */
+ _DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */
+
+ /* Flags for DMA buffer request */
+ _DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */
+ _DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */
+ _DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */
+} drm_dma_flags_t;
+
+/**
+ * DRM_IOCTL_ADD_BUFS and DRM_IOCTL_MARK_BUFS ioctl argument type.
+ *
+ * \sa drmAddBufs().
+ */
+typedef enum {
+ _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */
+ _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */
+ _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */
+ _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */
+ _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */
+} drm_buf_flag;
+typedef struct drm_buf_desc {
+ int count; /**< Number of buffers of this size */
+ int size; /**< Size in bytes */
+ int low_mark; /**< Low water mark */
+ int high_mark; /**< High water mark */
+ drm_buf_flag flags;
+ unsigned long agp_start; /**<
+ * Start address of where the AGP buffers are
+ * in the AGP aperture
+ */
+} drm_buf_desc_t;
+
+/**
+ * DRM_IOCTL_INFO_BUFS ioctl argument type.
+ */
+typedef struct drm_buf_info {
+ int count; /**< Number of buffers described in list */
+ drm_buf_desc_t __user *list; /**< List of buffer descriptions */
+} drm_buf_info_t;
+
+/**
+ * DRM_IOCTL_FREE_BUFS ioctl argument type.
+ */
+typedef struct drm_buf_free {
+ int count;
+ int __user *list;
+} drm_buf_free_t;
+
+/**
+ * Buffer information
+ *
+ * \sa drm_buf_map.
+ */
+typedef struct drm_buf_pub {
+ int idx; /**< Index into the master buffer list */
+ int total; /**< Buffer size */
+ int used; /**< Amount of buffer in use (for DMA) */
+ void __user *address; /**< Address of buffer */
+} drm_buf_pub_t;
+
+/**
+ * DRM_IOCTL_MAP_BUFS ioctl argument type.
+ */
+typedef struct drm_buf_map {
+ int count; /**< Length of the buffer list */
+#if defined(__cplusplus)
+ void __user *c_virtual;
+#else
+ void __user *virtual; /**< Mmap'd area in user-virtual */
+#endif
+ drm_buf_pub_t __user *list; /**< Buffer information */
+ int fd;
+} drm_buf_map_t;
+
+/**
+ * DRM_IOCTL_DMA ioctl argument type.
+ *
+ * Indices here refer to the offset into the buffer list in drm_buf_get.
+ *
+ * \sa drmDMA().
+ */
+typedef struct drm_dma {
+ int context; /**< Context handle */
+ int send_count; /**< Number of buffers to send */
+ int __user *send_indices; /**< List of handles to buffers */
+ int __user *send_sizes; /**< Lengths of data to send */
+ drm_dma_flags_t flags; /**< Flags */
+ int request_count; /**< Number of buffers requested */
+ int request_size; /**< Desired size for buffers */
+ int __user *request_indices; /**< Buffer information */
+ int __user *request_sizes;
+ int granted_count; /**< Number of buffers granted */
+} drm_dma_t;
+
+typedef enum {
+ _DRM_CONTEXT_PRESERVED = 0x01,
+ _DRM_CONTEXT_2DONLY = 0x02
+} drm_ctx_flags_t;
+
+/**
+ * DRM_IOCTL_ADD_CTX ioctl argument type.
+ *
+ * \sa drmCreateContext() and drmDestroyContext().
+ */
+typedef struct drm_ctx {
+ drm_context_t handle;
+ drm_ctx_flags_t flags;
+} drm_ctx_t;
+
+/**
+ * DRM_IOCTL_RES_CTX ioctl argument type.
+ */
+typedef struct drm_ctx_res {
+ int count;
+ drm_ctx_t __user *contexts;
+} drm_ctx_res_t;
+
+
+/**
+ * DRM_IOCTL_ADD_DRAW and DRM_IOCTL_RM_DRAW ioctl argument type.
+ */
+typedef struct drm_draw {
+ drm_drawable_t handle;
+} drm_draw_t;
+
+/**
+ * DRM_IOCTL_UPDATE_DRAW ioctl argument type.
+ */
+typedef enum {
+ DRM_DRAWABLE_CLIPRECTS,
+} drm_drawable_info_type_t;
+
+typedef struct drm_update_draw {
+ drm_drawable_t handle;
+ unsigned int type;
+ unsigned int num;
+ unsigned long long data;
+} drm_update_draw_t;
+
+/**
+ * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type.
+ */
+typedef struct drm_auth {
+ drm_magic_t magic;
+} drm_auth_t;
+
+/**
+ * DRM_IOCTL_IRQ_BUSID ioctl argument type.
+ *
+ * \sa drmGetInterruptFromBusID().
+ */
+typedef struct drm_irq_busid {
+ int irq; /**< IRQ number */
+ int busnum; /**< bus number */
+ int devnum; /**< device number */
+ int funcnum; /**< function number */
+} drm_irq_busid_t;
+
+typedef enum {
+ _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */
+ _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */
+ _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */
+ _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */
+ _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */
+ _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */
+} drm_vblank_seq_type_t;
+
+#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE)
+#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \
+ _DRM_VBLANK_NEXTONMISS)
+
+struct drm_wait_vblank_request {
+ drm_vblank_seq_type_t type;
+ unsigned int sequence;
+ unsigned long signal;
+};
+
+struct drm_wait_vblank_reply {
+ drm_vblank_seq_type_t type;
+ unsigned int sequence;
+ long tval_sec;
+ long tval_usec;
+};
+
+/**
+ * DRM_IOCTL_WAIT_VBLANK ioctl argument type.
+ *
+ * \sa drmWaitVBlank().
+ */
+typedef union drm_wait_vblank {
+ struct drm_wait_vblank_request request;
+ struct drm_wait_vblank_reply reply;
+} drm_wait_vblank_t;
+
+/**
+ * DRM_IOCTL_AGP_ENABLE ioctl argument type.
+ *
+ * \sa drmAgpEnable().
+ */
+typedef struct drm_agp_mode {
+ unsigned long mode; /**< AGP mode */
+} drm_agp_mode_t;
+
+/**
+ * DRM_IOCTL_AGP_ALLOC and DRM_IOCTL_AGP_FREE ioctls argument type.
+ *
+ * \sa drmAgpAlloc() and drmAgpFree().
+ */
+typedef struct drm_agp_buffer {
+ unsigned long size; /**< In bytes -- will round to page boundary */
+ unsigned long handle; /**< Used for binding / unbinding */
+ unsigned long type; /**< Type of memory to allocate */
+ unsigned long physical; /**< Physical used by i810 */
+} drm_agp_buffer_t;
+
+/**
+ * DRM_IOCTL_AGP_BIND and DRM_IOCTL_AGP_UNBIND ioctls argument type.
+ *
+ * \sa drmAgpBind() and drmAgpUnbind().
+ */
+typedef struct drm_agp_binding {
+ unsigned long handle; /**< From drm_agp_buffer */
+ unsigned long offset; /**< In bytes -- will round to page boundary */
+} drm_agp_binding_t;
+
+/**
+ * DRM_IOCTL_AGP_INFO ioctl argument type.
+ *
+ * \sa drmAgpVersionMajor(), drmAgpVersionMinor(), drmAgpGetMode(),
+ * drmAgpBase(), drmAgpSize(), drmAgpMemoryUsed(), drmAgpMemoryAvail(),
+ * drmAgpVendorId() and drmAgpDeviceId().
+ */
+typedef struct drm_agp_info {
+ int agp_version_major;
+ int agp_version_minor;
+ unsigned long mode;
+ unsigned long aperture_base; /**< physical address */
+ unsigned long aperture_size; /**< bytes */
+ unsigned long memory_allowed; /**< bytes */
+ unsigned long memory_used;
+
+ /** \name PCI information */
+ /*@{ */
+ unsigned short id_vendor;
+ unsigned short id_device;
+ /*@} */
+} drm_agp_info_t;
+
+/**
+ * DRM_IOCTL_SG_ALLOC ioctl argument type.
+ */
+typedef struct drm_scatter_gather {
+ unsigned long size; /**< In bytes -- will round to page boundary */
+ unsigned long handle; /**< Used for mapping / unmapping */
+} drm_scatter_gather_t;
+
+/**
+ * DRM_IOCTL_SET_VERSION ioctl argument type.
+ */
+typedef struct drm_set_version {
+ int drm_di_major;
+ int drm_di_minor;
+ int drm_dd_major;
+ int drm_dd_minor;
+} drm_set_version_t;
+
+/**
+ * \name Ioctls Definitions
+ */
+/*@{*/
+
+#define DRM_IOCTL_BASE 'd'
+#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr)
+#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type)
+#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type)
+#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type)
+
+#define DRM_IOCTL_VERSION DRM_IOWR(0x00, drm_version_t)
+#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm_unique_t)
+#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, drm_auth_t)
+#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, drm_irq_busid_t)
+#define DRM_IOCTL_GET_MAP DRM_IOWR(0x04, drm_map_t)
+#define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, drm_client_t)
+#define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, drm_stats_t)
+#define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, drm_set_version_t)
+
+#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm_unique_t)
+#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, drm_auth_t)
+#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, drm_block_t)
+#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, drm_block_t)
+#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, drm_control_t)
+#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, drm_map_t)
+#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, drm_buf_desc_t)
+#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, drm_buf_desc_t)
+#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm_buf_info_t)
+#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm_buf_map_t)
+#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm_buf_free_t)
+
+#define DRM_IOCTL_RM_MAP DRM_IOW( 0x1b, drm_map_t)
+
+#define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, drm_ctx_priv_map_t)
+#define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, drm_ctx_priv_map_t)
+
+#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, drm_ctx_t)
+#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, drm_ctx_t)
+#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, drm_ctx_t)
+#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, drm_ctx_t)
+#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, drm_ctx_t)
+#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, drm_ctx_t)
+#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, drm_ctx_res_t)
+#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, drm_draw_t)
+#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, drm_draw_t)
+#define DRM_IOCTL_DMA DRM_IOWR(0x29, drm_dma_t)
+#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, drm_lock_t)
+#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, drm_lock_t)
+#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, drm_lock_t)
+
+#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30)
+#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31)
+#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, drm_agp_mode_t)
+#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, drm_agp_info_t)
+#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, drm_agp_binding_t)
+#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, drm_agp_binding_t)
+
+#define DRM_IOCTL_SG_ALLOC DRM_IOW( 0x38, drm_scatter_gather_t)
+#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, drm_scatter_gather_t)
+
+#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t)
+
+#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, drm_update_draw_t)
+/*@}*/
+
+/**
+ * Device specific ioctls should only be in their respective headers
+ * The device specific ioctl range is from 0x40 to 0x99.
+ * Generic IOCTLS restart at 0xA0.
+ *
+ * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and
+ * drmCommandReadWrite().
+ */
+#define DRM_COMMAND_BASE 0x40
+#define DRM_COMMAND_END 0xA0
+
+#endif /* _DRM_H_ */
diff --git a/src/VBox/Additions/solaris/DRM/include/drmP.h b/src/VBox/Additions/solaris/DRM/include/drmP.h
new file mode 100644
index 00000000..0763e708
--- /dev/null
+++ b/src/VBox/Additions/solaris/DRM/include/drmP.h
@@ -0,0 +1,908 @@
+/*
+ * drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
+ * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com
+ */
+/*
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Rickard E. (Rik) Faith <faith@valinux.com>
+ * Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+/*
+ * Copyright 2009-2010 Oracle Corporation
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DRMP_H
+#define _DRMP_H
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/cmn_err.h>
+#include <sys/varargs.h>
+#include <sys/pci.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/pmem.h>
+#include <sys/agpgart.h>
+#include <sys/time.h>
+#include "drm_atomic.h"
+#include "drm.h"
+#if !defined(VBOX_WITH_SYSTEM_QUEUE_H)
+# include "queue.h"
+#endif
+#include "drm_linux_list.h"
+
+#ifndef __inline__
+#define __inline__ inline
+#endif
+
+#if !defined(__FUNCTION__)
+#if defined(C99)
+#define __FUNCTION__ __func__
+#else
+#define __FUNCTION__ " "
+#endif
+#endif
+
+/* DRM space units */
+#define DRM_PAGE_SHIFT PAGESHIFT
+#define DRM_PAGE_SIZE (1 << DRM_PAGE_SHIFT)
+#define DRM_PAGE_OFFSET (DRM_PAGE_SIZE - 1)
+#define DRM_PAGE_MASK ~(DRM_PAGE_SIZE - 1)
+#define DRM_MB2PAGES(x) ((x) << 8)
+#define DRM_PAGES2BYTES(x) ((x) << DRM_PAGE_SHIFT)
+#define DRM_BYTES2PAGES(x) ((x) >> DRM_PAGE_SHIFT)
+#define DRM_PAGES2KB(x) ((x) << 2)
+#define DRM_ALIGNED(offset) (((offset) & DRM_PAGE_OFFSET) == 0)
+
+#define PAGE_SHIFT DRM_PAGE_SHIFT
+#define PAGE_SIZE DRM_PAGE_SIZE
+
+#define DRM_MAX_INSTANCES 8
+#define DRM_DEVNODE "drm"
+#define DRM_UNOPENED 0
+#define DRM_OPENED 1
+
+#define DRM_HASH_SIZE 16 /* Size of key hash table */
+#define DRM_KERNEL_CONTEXT 0 /* Change drm_resctx if changed */
+#define DRM_RESERVED_CONTEXTS 1 /* Change drm_resctx if changed */
+
+#define DRM_MEM_DMA 0
+#define DRM_MEM_SAREA 1
+#define DRM_MEM_DRIVER 2
+#define DRM_MEM_MAGIC 3
+#define DRM_MEM_IOCTLS 4
+#define DRM_MEM_MAPS 5
+#define DRM_MEM_BUFS 6
+#define DRM_MEM_SEGS 7
+#define DRM_MEM_PAGES 8
+#define DRM_MEM_FILES 9
+#define DRM_MEM_QUEUES 10
+#define DRM_MEM_CMDS 11
+#define DRM_MEM_MAPPINGS 12
+#define DRM_MEM_BUFLISTS 13
+#define DRM_MEM_DRMLISTS 14
+#define DRM_MEM_TOTALDRM 15
+#define DRM_MEM_BOUNDDRM 16
+#define DRM_MEM_CTXBITMAP 17
+#define DRM_MEM_STUB 18
+#define DRM_MEM_SGLISTS 19
+#define DRM_MEM_AGPLISTS 20
+#define DRM_MEM_CTXLIST 21
+#define DRM_MEM_MM 22
+#define DRM_MEM_HASHTAB 23
+#define DRM_MEM_OBJECTS 24
+
+#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
+#define DRM_MAP_HASH_OFFSET 0x10000000
+#define DRM_MAP_HASH_ORDER 12
+#define DRM_OBJECT_HASH_ORDER 12
+#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
+#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)
+#define DRM_MM_INIT_MAX_PAGES 256
+
+
+/* Internal types and structures */
+#define DRM_ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
+#define DRM_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define DRM_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define DRM_IF_VERSION(maj, min) (maj << 16 | min)
+
+#define __OS_HAS_AGP 1
+
+#define DRM_DEV_MOD (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
+#define DRM_DEV_UID 0
+#define DRM_DEV_GID 0
+
+#define DRM_CURRENTPID ddi_get_pid()
+#define DRM_SPINLOCK(l) mutex_enter(l)
+#define DRM_SPINUNLOCK(u) mutex_exit(u)
+#define DRM_SPINLOCK_ASSERT(l)
+#define DRM_LOCK() mutex_enter(&dev->dev_lock)
+#define DRM_UNLOCK() mutex_exit(&dev->dev_lock)
+#define DRM_LOCK_OWNED() ASSERT(mutex_owned(&dev->dev_lock))
+#define spin_lock_irqsave(l, flag) mutex_enter(l)
+#define spin_unlock_irqrestore(u, flag) mutex_exit(u)
+#define spin_lock(l) mutex_enter(l)
+#define spin_unlock(u) mutex_exit(u)
+
+#define DRM_UDELAY(sec) delay(drv_usectohz(sec *1000))
+#define DRM_MEMORYBARRIER()
+
+typedef struct drm_file drm_file_t;
+typedef struct drm_device drm_device_t;
+typedef struct drm_driver_info drm_driver_t;
+
+#define DRM_DEVICE drm_device_t *dev = dev1
+#define DRM_IOCTL_ARGS \
+ drm_device_t *dev1, intptr_t data, drm_file_t *fpriv, int mode
+
+#define DRM_COPYFROM_WITH_RETURN(dest, src, size) \
+ if (ddi_copyin(src, dest, size, 0)) \
+ return (EFAULT)
+
+#define DRM_COPYTO_WITH_RETURN(dest, src, size) \
+ if (ddi_copyout((src), (dest), (size), 0)) \
+ return (EFAULT)
+
+#define DRM_COPY_FROM_USER(dest, src, size) \
+ ddi_copyin((src), (dest), (size), 0) /* flag for src */
+
+#define DRM_COPY_TO_USER(dest, src, size) \
+ ddi_copyout((src), (dest), (size), 0) /* flags for dest */
+
+#define DRM_COPY_FROM_USER_UNCHECKED(arg1, arg2, arg3) \
+ ddi_copyin((arg2), (arg1), (arg3), 0)
+
+#define DRM_COPY_TO_USER_UNCHECKED(arg1, arg2, arg3) \
+ ddi_copyout((arg2), arg1, arg3, 0)
+
+#define DRM_READ8(map, offset) \
+ *(uint8_t *)((uintptr_t)((map)->dev_addr) + (offset))
+#define DRM_READ16(map, offset) \
+ *(uint16_t *)((uintptr_t)((map)->dev_addr) + (offset))
+#define DRM_READ32(map, offset) \
+ *(uint32_t *)((uintptr_t)((map)->dev_addr) + (offset))
+#define DRM_WRITE8(map, offset, val) \
+ *(uint8_t *)((uintptr_t)((map)->dev_addr) + (offset)) = (val)
+#define DRM_WRITE16(map, offset, val) \
+ *(uint16_t *)((uintptr_t)((map)->dev_addr) + (offset)) = (val)
+#define DRM_WRITE32(map, offset, val) \
+ *(uint32_t *)((uintptr_t)((map)->dev_addr) + (offset)) = (val)
+
+typedef struct drm_wait_queue {
+ kcondvar_t cv;
+ kmutex_t lock;
+}wait_queue_head_t;
+
+#define DRM_INIT_WAITQUEUE(q, pri) \
+{ \
+ mutex_init(&(q)->lock, NULL, MUTEX_DRIVER, pri); \
+ cv_init(&(q)->cv, NULL, CV_DRIVER, NULL); \
+}
+
+#define DRM_FINI_WAITQUEUE(q) \
+{ \
+ mutex_destroy(&(q)->lock); \
+ cv_destroy(&(q)->cv); \
+}
+
+#define DRM_WAKEUP(q) \
+{ \
+ mutex_enter(&(q)->lock); \
+ cv_broadcast(&(q)->cv); \
+ mutex_exit(&(q)->lock); \
+}
+
+#define jiffies ddi_get_lbolt()
+#define DRM_WAIT_ON(ret, q, timeout, condition) \
+mutex_enter(&(q)->lock); \
+while (!(condition)) { \
+ ret = cv_timedwait_sig(&(q)->cv, &(q)->lock, jiffies + timeout); \
+ if (ret == -1) { \
+ ret = EBUSY; \
+ break; \
+ } else if (ret == 0) { \
+ ret = EINTR; \
+ break; \
+ } else { \
+ ret = 0; \
+ } \
+} \
+mutex_exit(&(q)->lock);
+
+#define DRM_GETSAREA() \
+{ \
+ drm_local_map_t *map; \
+ DRM_SPINLOCK_ASSERT(&dev->dev_lock); \
+ TAILQ_FOREACH(map, &dev->maplist, link) { \
+ if (map->type == _DRM_SHM && \
+ map->flags & _DRM_CONTAINS_LOCK) { \
+ dev_priv->sarea = map; \
+ break; \
+ } \
+ } \
+}
+
+#define LOCK_TEST_WITH_RETURN(dev, fpriv) \
+ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || \
+ dev->lock.filp != fpriv) { \
+ DRM_ERROR("called without lock held"); \
+ return (EINVAL); \
+ }
+
+#define DRM_IRQ_ARGS caddr_t arg
+#define IRQ_HANDLED DDI_INTR_CLAIMED
+#define IRQ_NONE DDI_INTR_UNCLAIMED
+
+enum {
+ DRM_IS_NOT_AGP,
+ DRM_IS_AGP,
+ DRM_MIGHT_BE_AGP
+};
+
+/* Capabilities taken from src/sys/dev/pci/pcireg.h. */
+#ifndef PCIY_AGP
+#define PCIY_AGP 0x02
+#endif
+
+#ifndef PCIY_EXPRESS
+#define PCIY_EXPRESS 0x10
+#endif
+
+#define PAGE_ALIGN(addr) (((addr) + DRM_PAGE_SIZE - 1) & DRM_PAGE_MASK)
+#define DRM_SUSER(p) (crgetgid(p) == 0 || crgetuid(p) == 0)
+
+/*
+ * wait for 400 milliseconds
+ */
+#define DRM_HZ drv_usectohz(400000)
+
+typedef unsigned long dma_addr_t;
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef uint_t irqreturn_t;
+
+#define DRM_SUPPORT 1
+#define DRM_UNSUPPORT 0
+
+#define __OS_HAS_AGP 1
+
+#define __offsetof(type, field) ((size_t)(&((type *)0)->field))
+#define offsetof(type, field) __offsetof(type, field)
+
+typedef struct drm_pci_id_list
+{
+ int vendor;
+ int device;
+ long driver_private;
+ char *name;
+} drm_pci_id_list_t;
+
+#define DRM_AUTH 0x1
+#define DRM_MASTER 0x2
+#define DRM_ROOT_ONLY 0x4
+typedef int drm_ioctl_t(DRM_IOCTL_ARGS);
+typedef struct drm_ioctl_desc {
+ int (*func)(DRM_IOCTL_ARGS);
+ int flags;
+} drm_ioctl_desc_t;
+
+typedef struct drm_magic_entry {
+ drm_magic_t magic;
+ struct drm_file *priv;
+ struct drm_magic_entry *next;
+} drm_magic_entry_t;
+
+typedef struct drm_magic_head {
+ struct drm_magic_entry *head;
+ struct drm_magic_entry *tail;
+} drm_magic_head_t;
+
+typedef struct drm_buf {
+ int idx; /* Index into master buflist */
+ int total; /* Buffer size */
+ int order; /* log-base-2(total) */
+ int used; /* Amount of buffer in use (for DMA) */
+ unsigned long offset; /* Byte offset (used internally) */
+ void *address; /* Address of buffer */
+ unsigned long bus_address; /* Bus address of buffer */
+ struct drm_buf *next; /* Kernel-only: used for free list */
+ volatile int pending; /* On hardware DMA queue */
+ drm_file_t *filp;
+ /* Uniq. identifier of holding process */
+ int context; /* Kernel queue for this buffer */
+ enum {
+ DRM_LIST_NONE = 0,
+ DRM_LIST_FREE = 1,
+ DRM_LIST_WAIT = 2,
+ DRM_LIST_PEND = 3,
+ DRM_LIST_PRIO = 4,
+ DRM_LIST_RECLAIM = 5
+ } list; /* Which list we're on */
+
+ int dev_priv_size; /* Size of buffer private storage */
+ void *dev_private; /* Per-buffer private storage */
+} drm_buf_t;
+
+typedef struct drm_freelist {
+ int initialized; /* Freelist in use */
+ uint32_t count; /* Number of free buffers */
+ drm_buf_t *next; /* End pointer */
+
+ int low_mark; /* Low water mark */
+ int high_mark; /* High water mark */
+} drm_freelist_t;
+
+typedef struct drm_buf_entry {
+ int buf_size;
+ int buf_count;
+ drm_buf_t *buflist;
+ int seg_count;
+ int page_order;
+
+ uint32_t *seglist;
+ unsigned long *seglist_bus;
+
+ drm_freelist_t freelist;
+} drm_buf_entry_t;
+
+typedef TAILQ_HEAD(drm_file_list, drm_file) drm_file_list_t;
+struct drm_file {
+ TAILQ_ENTRY(drm_file) link;
+ int authenticated;
+ int master;
+ int minor;
+ pid_t pid;
+ uid_t uid;
+ int refs;
+ drm_magic_t magic;
+ unsigned long ioctl_count;
+ void *driver_priv;
+};
+
+typedef struct drm_lock_data {
+ drm_hw_lock_t *hw_lock; /* Hardware lock */
+ drm_file_t *filp;
+ /* Uniq. identifier of holding process */
+ kcondvar_t lock_cv; /* lock queue - SOLARIS Specific */
+ kmutex_t lock_mutex; /* lock - SOLARIS Specific */
+ unsigned long lock_time; /* Time of last lock in jiffies */
+} drm_lock_data_t;
+
+/*
+ * This structure, in drm_device_t, is always initialized while the device
+ * is open. dev->dma_lock protects the incrementing of dev->buf_use, which
+ * when set marks that no further bufs may be allocated until device teardown
+ * occurs (when the last open of the device has closed). The high/low
+ * watermarks of bufs are only touched by the X Server, and thus not
+ * concurrently accessed, so no locking is needed.
+ */
+typedef struct drm_device_dma {
+ drm_buf_entry_t bufs[DRM_MAX_ORDER+1];
+ int buf_count;
+ drm_buf_t **buflist; /* Vector of pointers info bufs */
+ int seg_count;
+ int page_count;
+ unsigned long *pagelist;
+ unsigned long byte_count;
+ enum {
+ _DRM_DMA_USE_AGP = 0x01,
+ _DRM_DMA_USE_SG = 0x02
+ } flags;
+} drm_device_dma_t;
+
+typedef struct drm_agp_mem {
+ void *handle;
+ unsigned long bound; /* address */
+ int pages;
+ caddr_t phys_addr;
+ struct drm_agp_mem *prev;
+ struct drm_agp_mem *next;
+} drm_agp_mem_t;
+
+typedef struct drm_agp_head {
+ agp_info_t agp_info;
+ const char *chipset;
+ drm_agp_mem_t *memory;
+ unsigned long mode;
+ int enabled;
+ int acquired;
+ unsigned long base;
+ int mtrr;
+ int cant_use_aperture;
+ unsigned long page_mask;
+ ldi_ident_t agpgart_li;
+ ldi_handle_t agpgart_lh;
+} drm_agp_head_t;
+
+
+typedef struct drm_dma_handle {
+ ddi_dma_handle_t dma_hdl;
+ ddi_acc_handle_t acc_hdl;
+ ddi_dma_cookie_t cookie;
+ uint_t cookie_num;
+ uintptr_t vaddr; /* virtual addr */
+ uintptr_t paddr; /* physical addr */
+ size_t real_sz; /* real size of memory */
+} drm_dma_handle_t;
+
+typedef struct drm_sg_mem {
+ unsigned long handle;
+ void *virtual;
+ int pages;
+ dma_addr_t *busaddr;
+ ddi_umem_cookie_t *umem_cookie;
+ drm_dma_handle_t *dmah_sg;
+ drm_dma_handle_t *dmah_gart; /* Handle to PCI memory */
+} drm_sg_mem_t;
+
+typedef TAILQ_HEAD(drm_map_list, drm_local_map) drm_map_list_t;
+
+/* BEGIN CSTYLED */
+typedef struct drm_local_map {
+ unsigned long offset; /* Physical address (0 for SAREA) */
+ unsigned long size; /* Physical size (bytes) */
+ drm_map_type_t type; /* Type of memory mapped */
+ drm_map_flags_t flags; /* Flags */
+ void *handle; /* User-space: "Handle" to pass to mmap */
+ /* Kernel-space: kernel-virtual address */
+ int mtrr; /* Boolean: MTRR used */
+ /* Private data */
+ int rid; /* PCI resource ID for bus_space */
+ int kernel_owned; /* Boolean: 1= initmapped, 0= addmapped */
+ caddr_t dev_addr; /* base device address */
+ ddi_acc_handle_t dev_handle; /* The data access handle */
+ ddi_umem_cookie_t drm_umem_cookie; /* For SAREA alloc and free */
+ TAILQ_ENTRY(drm_local_map) link;
+} drm_local_map_t;
+/* END CSTYLED */
+
+typedef TAILQ_HEAD(drm_vbl_sig_list, drm_vbl_sig) drm_vbl_sig_list_t;
+typedef struct drm_vbl_sig {
+ TAILQ_ENTRY(drm_vbl_sig) link;
+ unsigned int sequence;
+ int signo;
+ int pid;
+} drm_vbl_sig_t;
+
+
+/* used for clone device */
+typedef TAILQ_HEAD(drm_cminor_list, drm_cminor) drm_cminor_list_t;
+typedef struct drm_cminor {
+ TAILQ_ENTRY(drm_cminor) link;
+ drm_file_t *fpriv;
+ int minor;
+} drm_cminor_t;
+
+/* location of GART table */
+#define DRM_ATI_GART_MAIN 1
+#define DRM_ATI_GART_FB 2
+
+typedef struct ati_pcigart_info {
+ int gart_table_location;
+ int is_pcie;
+ void *addr;
+ dma_addr_t bus_addr;
+ drm_local_map_t mapping;
+} drm_ati_pcigart_info;
+
+/* DRM device structure */
+struct drm_device;
+struct drm_driver_info {
+ int (*load)(struct drm_device *, unsigned long);
+ int (*firstopen)(struct drm_device *);
+ int (*open)(struct drm_device *, drm_file_t *);
+ void (*preclose)(struct drm_device *, drm_file_t *);
+ void (*postclose)(struct drm_device *, drm_file_t *);
+ void (*lastclose)(struct drm_device *);
+ int (*unload)(struct drm_device *);
+ void (*reclaim_buffers_locked)(struct drm_device *, drm_file_t *);
+ int (*presetup)(struct drm_device *);
+ int (*postsetup)(struct drm_device *);
+ int (*open_helper)(struct drm_device *, drm_file_t *);
+ void (*free_filp_priv)(struct drm_device *, drm_file_t *);
+ void (*release)(struct drm_device *, void *);
+ int (*dma_ioctl)(DRM_IOCTL_ARGS);
+ void (*dma_ready)(struct drm_device *);
+ int (*dma_quiescent)(struct drm_device *);
+ int (*dma_flush_block_and_flush)(struct drm_device *,
+ int, drm_lock_flags_t);
+ int (*dma_flush_unblock)(struct drm_device *, int,
+ drm_lock_flags_t);
+ int (*context_ctor)(struct drm_device *, int);
+ int (*context_dtor)(struct drm_device *, int);
+ int (*kernel_context_switch)(struct drm_device *, int, int);
+ int (*kernel_context_switch_unlock)(struct drm_device *);
+ int (*device_is_agp) (struct drm_device *);
+ int (*irq_preinstall)(struct drm_device *);
+ void (*irq_postinstall)(struct drm_device *);
+ void (*irq_uninstall)(struct drm_device *dev);
+ uint_t (*irq_handler)(DRM_IRQ_ARGS);
+ int (*vblank_wait)(struct drm_device *, unsigned int *);
+ int (*vblank_wait2)(struct drm_device *, unsigned int *);
+ /* added for intel minimized vblank */
+ u32 (*get_vblank_counter)(struct drm_device *dev, int crtc);
+ int (*enable_vblank)(struct drm_device *dev, int crtc);
+ void (*disable_vblank)(struct drm_device *dev, int crtc);
+
+ drm_ioctl_desc_t *driver_ioctls;
+ int max_driver_ioctl;
+
+ int buf_priv_size;
+ int driver_major;
+ int driver_minor;
+ int driver_patchlevel;
+ const char *driver_name; /* Simple driver name */
+ const char *driver_desc; /* Longer driver name */
+ const char *driver_date; /* Date of last major changes. */
+
+ unsigned use_agp :1;
+ unsigned require_agp :1;
+ unsigned use_sg :1;
+ unsigned use_dma :1;
+ unsigned use_pci_dma :1;
+ unsigned use_dma_queue :1;
+ unsigned use_irq :1;
+ unsigned use_vbl_irq :1;
+ unsigned use_vbl_irq2 :1;
+ unsigned use_mtrr :1;
+};
+
+/*
+ * hardware-specific code needs to initialize mutexes which
+ * can be used in interrupt context, so they need to know
+ * the interrupt priority. Interrupt cookie in drm_device
+ * structure is the intr_block field.
+ */
+#define DRM_INTR_PRI(dev) \
+ DDI_INTR_PRI((dev)->intr_block)
+
+struct drm_device {
+ drm_driver_t *driver;
+ drm_cminor_list_t minordevs;
+ dev_info_t *dip;
+ void *drm_handle;
+ int drm_supported;
+ const char *desc; /* current driver description */
+ kmutex_t *irq_mutex;
+ kcondvar_t *irq_cv;
+
+ ddi_iblock_cookie_t intr_block;
+ uint32_t pci_device; /* PCI device id */
+ uint32_t pci_vendor;
+ char *unique; /* Unique identifier: e.g., busid */
+ int unique_len; /* Length of unique field */
+ int if_version; /* Highest interface version set */
+ int flags; /* Flags to open(2) */
+
+ /* Locks */
+ kmutex_t vbl_lock; /* protects vblank operations */
+ kmutex_t dma_lock; /* protects dev->dma */
+ kmutex_t irq_lock; /* protects irq condition checks */
+ kmutex_t dev_lock; /* protects everything else */
+ drm_lock_data_t lock; /* Information on hardware lock */
+
+ /* Usage Counters */
+ int open_count; /* Outstanding files open */
+ int buf_use; /* Buffers in use -- cannot alloc */
+
+ /* Performance counters */
+ unsigned long counters;
+ drm_stat_type_t types[15];
+ uint32_t counts[15];
+
+ /* Authentication */
+ drm_file_list_t files;
+ drm_magic_head_t magiclist[DRM_HASH_SIZE];
+
+ /* Linked list of mappable regions. Protected by dev_lock */
+ drm_map_list_t maplist;
+
+ drm_local_map_t **context_sareas;
+ int max_context;
+
+ /* DMA queues (contexts) */
+ drm_device_dma_t *dma; /* Optional pointer for DMA support */
+
+ /* Context support */
+ int irq; /* Interrupt used by board */
+ int irq_enabled; /* True if the irq handler is enabled */
+ int pci_domain;
+ int pci_bus;
+ int pci_slot;
+ int pci_func;
+ atomic_t context_flag; /* Context swapping flag */
+ int last_context; /* Last current context */
+
+ /* Only used for Radeon */
+ atomic_t vbl_received;
+ atomic_t vbl_received2;
+
+ drm_vbl_sig_list_t vbl_sig_list;
+ drm_vbl_sig_list_t vbl_sig_list2;
+
+ wait_queue_head_t vbl_queue; /* vbl wait channel */
+ /* vbl wait channel array */
+ wait_queue_head_t *vbl_queues;
+
+ /* number of VBLANK interrupts */
+ /* (driver must alloc the right number of counters) */
+ atomic_t *_vblank_count;
+ /* signal list to send on VBLANK */
+ struct drm_vbl_sig_list *vbl_sigs;
+
+ /* number of signals pending on all crtcs */
+ atomic_t vbl_signal_pending;
+ /* number of users of vblank interrupts per crtc */
+ atomic_t *vblank_refcount;
+ /* protected by dev->vbl_lock, used for wraparound handling */
+ u32 *last_vblank;
+ /* so we don't call enable more than */
+ atomic_t *vblank_enabled;
+ /* for compensation of spurious wraparounds */
+ u32 *vblank_premodeset;
+ /* Don't wait while crtc is likely disabled */
+ int *vblank_suspend;
+ /* size of vblank counter register */
+ u32 max_vblank_count;
+ int num_crtcs;
+ kmutex_t tasklet_lock;
+ void (*locked_tasklet_func)(struct drm_device *dev);
+
+ pid_t buf_pgid;
+ drm_agp_head_t *agp;
+ drm_sg_mem_t *sg; /* Scatter gather memory */
+ uint32_t *ctx_bitmap;
+ void *dev_private;
+ unsigned int agp_buffer_token;
+ drm_local_map_t *agp_buffer_map;
+
+ kstat_t *asoft_ksp; /* kstat support */
+
+ /* name Drawable information */
+ kmutex_t drw_lock;
+ unsigned int drw_bitfield_length;
+ u32 *drw_bitfield;
+ unsigned int drw_info_length;
+ drm_drawable_info_t **drw_info;
+ /*
+ * Saving S3 context
+ */
+ void *s3_private;
+};
+
+/* Memory management support (drm_memory.c) */
+void drm_mem_init(void);
+void drm_mem_uninit(void);
+void *drm_alloc(size_t, int);
+void *drm_calloc(size_t, size_t, int);
+void *drm_realloc(void *, size_t, size_t, int);
+void drm_free(void *, size_t, int);
+int drm_ioremap(drm_device_t *, drm_local_map_t *);
+void drm_ioremapfree(drm_local_map_t *);
+
+void drm_core_ioremap(struct drm_local_map *, struct drm_device *);
+void drm_core_ioremapfree(struct drm_local_map *, struct drm_device *);
+
+void drm_pci_free(drm_device_t *, drm_dma_handle_t *);
+void *drm_pci_alloc(drm_device_t *, size_t, size_t, dma_addr_t, int);
+
+struct drm_local_map *drm_core_findmap(struct drm_device *, unsigned long);
+
+int drm_context_switch(drm_device_t *, int, int);
+int drm_context_switch_complete(drm_device_t *, int);
+int drm_ctxbitmap_init(drm_device_t *);
+void drm_ctxbitmap_cleanup(drm_device_t *);
+void drm_ctxbitmap_free(drm_device_t *, int);
+int drm_ctxbitmap_next(drm_device_t *);
+
+/* Locking IOCTL support (drm_lock.c) */
+int drm_lock_take(drm_lock_data_t *, unsigned int);
+int drm_lock_transfer(drm_device_t *,
+ drm_lock_data_t *, unsigned int);
+int drm_lock_free(drm_device_t *,
+ volatile unsigned int *, unsigned int);
+
+/* Buffer management support (drm_bufs.c) */
+unsigned long drm_get_resource_start(drm_device_t *, unsigned int);
+unsigned long drm_get_resource_len(drm_device_t *, unsigned int);
+int drm_initmap(drm_device_t *, unsigned long, unsigned long,
+ unsigned int, int, int);
+void drm_rmmap(drm_device_t *, drm_local_map_t *);
+int drm_addmap(drm_device_t *, unsigned long, unsigned long,
+ drm_map_type_t, drm_map_flags_t, drm_local_map_t **);
+int drm_order(unsigned long);
+
+/* DMA support (drm_dma.c) */
+int drm_dma_setup(drm_device_t *);
+void drm_dma_takedown(drm_device_t *);
+void drm_free_buffer(drm_device_t *, drm_buf_t *);
+void drm_reclaim_buffers(drm_device_t *, drm_file_t *);
+#define drm_core_reclaim_buffers drm_reclaim_buffers
+
+/* IRQ support (drm_irq.c) */
+int drm_irq_install(drm_device_t *);
+int drm_irq_uninstall(drm_device_t *);
+uint_t drm_irq_handler(DRM_IRQ_ARGS);
+void drm_driver_irq_preinstall(drm_device_t *);
+void drm_driver_irq_postinstall(drm_device_t *);
+void drm_driver_irq_uninstall(drm_device_t *);
+int drm_vblank_wait(drm_device_t *, unsigned int *);
+void drm_vbl_send_signals(drm_device_t *);
+void drm_handle_vblank(struct drm_device *dev, int crtc);
+u32 drm_vblank_count(struct drm_device *dev, int crtc);
+int drm_vblank_get(struct drm_device *dev, int crtc);
+void drm_vblank_put(struct drm_device *dev, int crtc);
+int drm_vblank_init(struct drm_device *dev, int num_crtcs);
+void drm_locked_tasklet(drm_device_t *, void(*func)(drm_device_t *));
+
+/* AGP/GART support (drm_agpsupport.c) */
+int drm_device_is_agp(drm_device_t *);
+int drm_device_is_pcie(drm_device_t *);
+drm_agp_head_t *drm_agp_init(drm_device_t *);
+void drm_agp_fini(drm_device_t *);
+int drm_agp_do_release(drm_device_t *);
+void *drm_agp_allocate_memory(size_t, uint32_t);
+int drm_agp_free_memory(void *);
+int drm_agp_bind_memory(unsigned int, uint32_t, drm_device_t *);
+int drm_agp_unbind_memory(unsigned long, drm_device_t *);
+
+/* kstat support (drm_kstats.c) */
+int drm_init_kstats(drm_device_t *);
+void drm_fini_kstats(drm_device_t *);
+
+/* Scatter Gather Support (drm_scatter.c) */
+void drm_sg_cleanup(drm_device_t *, drm_sg_mem_t *);
+
+/* ATI PCIGART support (ati_pcigart.c) */
+int drm_ati_pcigart_init(drm_device_t *, drm_ati_pcigart_info *);
+int drm_ati_pcigart_cleanup(drm_device_t *, drm_ati_pcigart_info *);
+
+/* Locking IOCTL support (drm_drv.c) */
+int drm_lock(DRM_IOCTL_ARGS);
+int drm_unlock(DRM_IOCTL_ARGS);
+int drm_version(DRM_IOCTL_ARGS);
+int drm_setversion(DRM_IOCTL_ARGS);
+
+/* Misc. IOCTL support (drm_ioctl.c) */
+int drm_irq_by_busid(DRM_IOCTL_ARGS);
+int drm_getunique(DRM_IOCTL_ARGS);
+int drm_setunique(DRM_IOCTL_ARGS);
+int drm_getmap(DRM_IOCTL_ARGS);
+int drm_getclient(DRM_IOCTL_ARGS);
+int drm_getstats(DRM_IOCTL_ARGS);
+int drm_noop(DRM_IOCTL_ARGS);
+
+/* Context IOCTL support (drm_context.c) */
+int drm_resctx(DRM_IOCTL_ARGS);
+int drm_addctx(DRM_IOCTL_ARGS);
+int drm_modctx(DRM_IOCTL_ARGS);
+int drm_getctx(DRM_IOCTL_ARGS);
+int drm_switchctx(DRM_IOCTL_ARGS);
+int drm_newctx(DRM_IOCTL_ARGS);
+int drm_rmctx(DRM_IOCTL_ARGS);
+int drm_setsareactx(DRM_IOCTL_ARGS);
+int drm_getsareactx(DRM_IOCTL_ARGS);
+
+/* Drawable IOCTL support (drm_drawable.c) */
+int drm_adddraw(DRM_IOCTL_ARGS);
+int drm_rmdraw(DRM_IOCTL_ARGS);
+int drm_update_draw(DRM_IOCTL_ARGS);
+
+/* Authentication IOCTL support (drm_auth.c) */
+int drm_getmagic(DRM_IOCTL_ARGS);
+int drm_authmagic(DRM_IOCTL_ARGS);
+int drm_remove_magic(drm_device_t *, drm_magic_t);
+drm_file_t *drm_find_file(drm_device_t *, drm_magic_t);
+/* Buffer management support (drm_bufs.c) */
+int drm_addmap_ioctl(DRM_IOCTL_ARGS);
+int drm_rmmap_ioctl(DRM_IOCTL_ARGS);
+int drm_addbufs_ioctl(DRM_IOCTL_ARGS);
+int drm_infobufs(DRM_IOCTL_ARGS);
+int drm_markbufs(DRM_IOCTL_ARGS);
+int drm_freebufs(DRM_IOCTL_ARGS);
+int drm_mapbufs(DRM_IOCTL_ARGS);
+
+/* DMA support (drm_dma.c) */
+int drm_dma(DRM_IOCTL_ARGS);
+
+/* IRQ support (drm_irq.c) */
+int drm_control(DRM_IOCTL_ARGS);
+int drm_wait_vblank(DRM_IOCTL_ARGS);
+
+/* AGP/GART support (drm_agpsupport.c) */
+int drm_agp_acquire(DRM_IOCTL_ARGS);
+int drm_agp_release(DRM_IOCTL_ARGS);
+int drm_agp_enable(DRM_IOCTL_ARGS);
+int drm_agp_info(DRM_IOCTL_ARGS);
+int drm_agp_alloc(DRM_IOCTL_ARGS);
+int drm_agp_free(DRM_IOCTL_ARGS);
+int drm_agp_unbind(DRM_IOCTL_ARGS);
+int drm_agp_bind(DRM_IOCTL_ARGS);
+
+/* Scatter Gather Support (drm_scatter.c) */
+int drm_sg_alloc(DRM_IOCTL_ARGS);
+int drm_sg_free(DRM_IOCTL_ARGS);
+
+extern int drm_debug_flag;
+
+
+/* We add function to support DRM_DEBUG,DRM_ERROR,DRM_INFO */
+extern void drm_debug(const char *fmt, ...);
+extern void drm_error(const char *fmt, ...);
+extern void drm_info(const char *fmt, ...);
+
+#ifdef DEBUG
+#define DRM_DEBUG if (drm_debug_flag >= 2) drm_debug
+#define DRM_INFO if (drm_debug_flag >= 1) drm_info
+#else
+#define DRM_DEBUG(...)
+#define DRM_INFO(...)
+#endif
+
+#define DRM_ERROR drm_error
+
+
+#define MAX_INSTNUMS 16
+
+extern int drm_dev_to_instance(dev_t);
+extern int drm_dev_to_minor(dev_t);
+extern void *drm_supp_register(dev_info_t *, drm_device_t *);
+extern int drm_supp_unregister(void *);
+
+extern int drm_open(drm_device_t *, drm_cminor_t *, int, int, cred_t *);
+extern int drm_close(drm_device_t *, int, int, int, cred_t *);
+extern int drm_attach(drm_device_t *);
+extern int drm_detach(drm_device_t *);
+extern int drm_probe(drm_device_t *, drm_pci_id_list_t *);
+
+extern int drm_pci_init(drm_device_t *);
+extern void drm_pci_end(drm_device_t *);
+extern int pci_get_info(drm_device_t *, int *, int *, int *);
+extern int pci_get_irq(drm_device_t *);
+extern int pci_get_vendor(drm_device_t *);
+extern int pci_get_device(drm_device_t *);
+
+extern struct drm_drawable_info *drm_get_drawable_info(drm_device_t *,
+ drm_drawable_t);
+/* File Operations helpers (drm_fops.c) */
+extern drm_file_t *drm_find_file_by_proc(drm_device_t *, cred_t *);
+extern drm_cminor_t *drm_find_file_by_minor(drm_device_t *, int);
+extern int drm_open_helper(drm_device_t *, drm_cminor_t *, int, int,
+ cred_t *);
+
+#endif /* _DRMP_H */
diff --git a/src/VBox/Additions/solaris/DRM/include/drm_atomic.h b/src/VBox/Additions/solaris/DRM/include/drm_atomic.h
new file mode 100644
index 00000000..05401cc4
--- /dev/null
+++ b/src/VBox/Additions/solaris/DRM/include/drm_atomic.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2009-2010 Oracle Corporation
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * \file drm_atomic.h
+ * Atomic operations used in the DRM which may or may not be provided by the OS.
+ *
+ * \author Eric Anholt <anholt@FreeBSD.org>
+ */
+
+/*
+ * Copyright 2004 Eric Anholt
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Many of these implementations are rather fake, but good enough. */
+
+
+
+#ifndef _SYS_DRM_ATOMIC_H_
+#define _SYS_DRM_ATOMIC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/atomic.h>
+
+#ifdef __LINT__
+#undef inline
+#define inline
+#endif
+typedef uint32_t atomic_t;
+
+#define atomic_set(p, v) (*(p) = (v))
+#define atomic_read(p) (*(p))
+#define atomic_inc(p) atomic_add_int(p, 1)
+#define atomic_dec(p) atomic_dec_uint(p)
+#define atomic_add(n, p) atomic_add_int(p, n)
+#define atomic_sub(n, p) atomic_dec_uint(p, n)
+#define atomic_set_int(p, bits) atomic_or_uint(p, bits)
+#define atomic_clear_int(p, bits) atomic_and_uint(p, ~(bits))
+#define atomic_cmpset_int(p, c, n) \
+ ((c == atomic_cas_uint(p, c, n)) ? 1 : 0)
+
+#define set_bit(b, p) \
+ atomic_set_int(((volatile uint_t *)(void *)p) + (b >> 5), \
+ 1 << (b & 0x1f))
+
+#define clear_bit(b, p) \
+ atomic_clear_int(((volatile uint_t *)(void *)p) + (b >> 5), \
+ 1 << (b & 0x1f))
+
+#define test_bit(b, p) \
+ (((volatile uint_t *)(void *)p)[b >> 5] & (1 << (b & 0x1f)))
+
+/*
+ * Note: this routine doesn't return old value. It return
+ * 0 when succeeds, or -1 when fails.
+ */
+#ifdef _LP64
+#define test_and_set_bit(b, p) \
+ atomic_set_long_excl(((ulong_t *)(void *)p) + (b >> 6), (b & 0x3f))
+#else
+#define test_and_set_bit(b, p) \
+ atomic_set_long_excl(((ulong_t *)(void *)p) + (b >> 5), (b & 0x1f))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DRM_ATOMIC_H_ */
diff --git a/src/VBox/Additions/solaris/DRM/include/drm_linux_list.h b/src/VBox/Additions/solaris/DRM/include/drm_linux_list.h
new file mode 100644
index 00000000..e811c9fe
--- /dev/null
+++ b/src/VBox/Additions/solaris/DRM/include/drm_linux_list.h
@@ -0,0 +1,71 @@
+/*
+ * drm_linux_list.h -- linux list functions for the BSDs.
+ * Created: Mon Apr 7 14:30:16 1999 by anholt@FreeBSD.org
+ */
+/*
+ * -
+ * Copyright 2003 Eric Anholt
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <anholt@FreeBSD.org>
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef _DRM_LINUX_LIST_H_
+#define _DRM_LINUX_LIST_H_
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+/* Cheat, assume the list_head is at the start of the struct */
+#define list_entry(entry, type, member) (type *)(entry)
+
+#define INIT_LIST_HEAD(head) { \
+ (head)->next = head; \
+ (head)->prev = head; \
+}
+
+#define list_add_tail(entry, head) { \
+ (entry)->prev = (head)->prev; \
+ (entry)->next = head; \
+ (head)->prev->next = entry; \
+ (head)->prev = entry; \
+}
+
+#define list_del(entry) { \
+ (entry)->next->prev = (entry)->prev; \
+ (entry)->prev->next = (entry)->next; \
+}
+
+#define list_for_each(entry, head) \
+ for (entry = (head)->next; entry != head; entry = (entry)->next)
+
+#define list_for_each_safe(entry, temp, head) \
+ for (entry = (head)->next, temp = (entry)->next; \
+ temp != head; \
+ entry = temp, temp = temp->next)
+
+#endif /* _DRM_LINUX_LIST_H_ */
diff --git a/src/VBox/Additions/solaris/DRM/include/queue.h b/src/VBox/Additions/solaris/DRM/include/queue.h
new file mode 100644
index 00000000..4994209e
--- /dev/null
+++ b/src/VBox/Additions/solaris/DRM/include/queue.h
@@ -0,0 +1,585 @@
+/* BEGIN CSTYLED */
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: /repoman/r/ncvs/src/sys/sys/queue.h,v 1.66 2006/05/26 18:17:53 emaste Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - - - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+ char * lastfile;
+ int lastline;
+ char * prevfile;
+ int prevline;
+};
+
+#define TRACEBUF struct qm_trace trace;
+#define TRASHIT(x) do {(x) = (void *)-1;} while (*"\0")
+
+#define QMD_TRACE_HEAD(head) do { \
+ (head)->trace.prevline = (head)->trace.lastline; \
+ (head)->trace.prevfile = (head)->trace.lastfile; \
+ (head)->trace.lastline = __LINE__; \
+ (head)->trace.lastfile = __FILE__; \
+} while (*"\0")
+
+#define QMD_TRACE_ELEM(elem) do { \
+ (elem)->trace.prevline = (elem)->trace.lastline; \
+ (elem)->trace.prevfile = (elem)->trace.lastfile; \
+ (elem)->trace.lastline = __LINE__; \
+ (elem)->trace.lastfile = __FILE__; \
+} while (*"\0")
+
+#else
+#define QMD_TRACE_ELEM(elem)
+#define QMD_TRACE_HEAD(head)
+#define TRACEBUF
+#define TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != NULL; \
+ (varp) = &SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+} while (*"\0")
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+} while (*"\0")
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+} while (*"\0")
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_NEXT(curelm, field) = \
+ SLIST_NEXT(SLIST_NEXT(curelm, field), field); \
+ } \
+ TRASHIT((elm)->field.sle_next); \
+} while (*"\0")
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+} while (*"\0")
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (*"\0")
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (*"\0")
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+} while (*"\0")
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+} while (*"\0")
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (*"\0")
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->stqh_last) - __offsetof(struct type, field))))
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ if ((STAILQ_NEXT(curelm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+ } \
+ TRASHIT((elm)->field.stqe_next); \
+} while (*"\0")
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (*"\0")
+
+#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
+ if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (*"\0")
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_LIST_CHECK_HEAD(head, field) do { \
+ if (LIST_FIRST((head)) != NULL && \
+ LIST_FIRST((head))->field.le_prev != \
+ &LIST_FIRST((head))) \
+ panic("Bad list head %p first->prev != head", (head)); \
+} while (*"\0")
+
+#define QMD_LIST_CHECK_NEXT(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL && \
+ LIST_NEXT((elm), field)->field.le_prev != \
+ &((elm)->field.le_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+} while (*"\0")
+
+#define QMD_LIST_CHECK_PREV(elm, field) do { \
+ if (*(elm)->field.le_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+} while (*"\0")
+#else
+#define QMD_LIST_CHECK_HEAD(head, field)
+#define QMD_LIST_CHECK_NEXT(elm, field)
+#define QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+} while (*"\0")
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ QMD_LIST_CHECK_NEXT(listelm, field); \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+} while (*"\0")
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ QMD_LIST_CHECK_PREV(listelm, field); \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+} while (*"\0")
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ QMD_LIST_CHECK_HEAD((head), field); \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (*"\0")
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) do { \
+ QMD_LIST_CHECK_NEXT(elm, field); \
+ QMD_LIST_CHECK_PREV(elm, field); \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+ TRASHIT((elm)->field.le_next); \
+ TRASHIT((elm)->field.le_prev); \
+} while (*"\0")
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
+ if (!TAILQ_EMPTY(head) && \
+ TAILQ_FIRST((head))->field.tqe_prev != \
+ &TAILQ_FIRST((head))) \
+ panic("Bad tailq head %p first->prev != head", (head)); \
+} while (*"\0")
+
+#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
+ if (*(head)->tqh_last != NULL) \
+ panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
+} while (*"\0")
+
+#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
+ if (TAILQ_NEXT((elm), field) != NULL && \
+ TAILQ_NEXT((elm), field)->field.tqe_prev != \
+ &((elm)->field.tqe_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+} while (*"\0")
+
+#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+} while (*"\0")
+#else
+#define QMD_TAILQ_CHECK_HEAD(head, field)
+#define QMD_TAILQ_CHECK_TAIL(head, headname)
+#define QMD_TAILQ_CHECK_NEXT(elm, field)
+#define QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ QMD_TRACE_HEAD(head1); \
+ QMD_TRACE_HEAD(head2); \
+ } \
+} while (*"\0")
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+} while (*"\0")
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QMD_TAILQ_CHECK_NEXT(listelm, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (*"\0")
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QMD_TAILQ_CHECK_PREV(listelm, field); \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (*"\0")
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QMD_TAILQ_CHECK_HEAD(head, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (*"\0")
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QMD_TAILQ_CHECK_TAIL(head, field); \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (*"\0")
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ QMD_TAILQ_CHECK_NEXT(elm, field); \
+ QMD_TAILQ_CHECK_PREV(elm, field); \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ QMD_TRACE_HEAD(head); \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ TRASHIT((elm)->field.tqe_next); \
+ TRASHIT((elm)->field.tqe_prev); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (*"\0")
+
+
+#ifdef _KERNEL
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
+
+/* END CSTYLED */
diff --git a/src/VBox/Additions/solaris/DRM/vboxvideo_drm.c b/src/VBox/Additions/solaris/DRM/vboxvideo_drm.c
new file mode 100644
index 00000000..c63deb33
--- /dev/null
+++ b/src/VBox/Additions/solaris/DRM/vboxvideo_drm.c
@@ -0,0 +1,409 @@
+/* $Id: vboxvideo_drm.c $ */
+/** @file
+ * vboxvideo_drm - Direct Rendering Module, Solaris Specific Code.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#undef offsetof /* This gets redefined in drmP.h */
+#include "include/drmP.h"
+#include "include/drm.h"
+
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+
+#include <VBox/log.h>
+#include <VBox/version.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXSOLQUOTE2(x) #x
+#define VBOXSOLQUOTE(x) VBOXSOLQUOTE2(x)
+/** The module name. */
+#define DEVICE_NAME "vboxvideo"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox DRM"
+
+/** DRM Specific defines */
+#define DRIVER_AUTHOR "Oracle Corporation"
+#define DRIVER_NAME DEVICE_NAME
+#define DRIVER_DESC DEVICE_DESC_DRV
+#define DRIVER_DATE "20090317"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+#define vboxvideo_PCI_IDS { 0x80ee, 0xbeef, 0, "VirtualBox Video" }, \
+ { 0, 0, 0, NULL }
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int VBoxVideoSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int VBoxVideoSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+static int VBoxVideoSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult);
+
+static void vboxVideoSolarisConfigure(drm_driver_t *pDriver);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+extern struct cb_ops drm_cb_ops;
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_VBoxVideoSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxVideoSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxVideoSolarisAttach,
+ VBoxVideoSolarisDetach,
+ nodev, /* reset */
+ &drm_cb_ops,
+ NULL, /* dev bus ops*/
+ NULL /* power */
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_VBoxVideoSolarisModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" VBOXSOLQUOTE(VBOX_SVN_REV),
+ &g_VBoxVideoSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxVideoSolarisModLinkage =
+{
+ MODREV_1, /* loadable module system revision */
+ &g_VBoxVideoSolarisModule,
+ NULL /* terminate array of linkage structures */
+};
+
+/* VBoxVideo device PCI ID */
+static drm_pci_id_list_t vboxvideo_pciidlist[] = {
+ vboxvideo_PCI_IDS
+};
+
+
+/** DRM Driver */
+static drm_driver_t g_VBoxVideoSolarisDRMDriver = { 0 };
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Soft state. */
+static void *g_pVBoxVideoSolarisState;
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ LogFlow((DEVICE_NAME ":_init flow\n"));
+ cmn_err(CE_NOTE, DEVICE_NAME ":_init\n");
+
+ vboxVideoSolarisConfigure(&g_VBoxVideoSolarisDRMDriver);
+ int rc = ddi_soft_state_init(&g_pVBoxVideoSolarisState, sizeof(drm_device_t), DRM_MAX_INSTANCES);
+ if (!rc)
+ return mod_install(&g_VBoxVideoSolarisModLinkage);
+ else
+ LogRel((DEVICE_NAME ":_init: ddi_soft_state_init failed. rc=%d\n", rc));
+}
+
+
+int _fini(void)
+{
+ LogFlow((DEVICE_NAME ":_fini flow\n"));
+ cmn_err(CE_NOTE, DEVICE_NAME ":_fini\n");
+ int rc = mod_remove(&g_VBoxVideoSolarisModLinkage);
+ if (!rc)
+ ddi_soft_state_fini(&g_pVBoxVideoSolarisState);
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFlow((DEVICE_NAME ":_info flow\n"));
+ cmn_err(CE_NOTE, DEVICE_NAME ":_info\n");
+ return mod_info(&g_VBoxVideoSolarisModLinkage, pModInfo);
+}
+
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (attach/resume).
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxVideoSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFlow((DEVICE_NAME ":VBoxVideoSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+ cmn_err(CE_NOTE, DEVICE_NAME ":attach\n");
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ drm_device_t *pState;
+ int Instance = ddi_get_instance(pDip);
+ int rc = ddi_soft_state_zalloc(g_pVBoxVideoSolarisState, Instance);
+ if (rc == DDI_SUCCESS)
+ {
+ pState = ddi_get_soft_state(g_pVBoxVideoSolarisState, Instance);
+ pState->dip = pDip;
+ pState->driver = &g_VBoxVideoSolarisDRMDriver;
+
+ /*
+ * Register using the DRM module which will create the minor nodes
+ */
+ void *pDRMHandle = drm_supp_register(pDip, pState);
+ if (pDRMHandle)
+ {
+ pState->drm_handle = pDRMHandle;
+
+ /*
+ * Probe with our pci-id.
+ * -XXX- is probing really required???
+ */
+ pState->drm_supported = DRM_UNSUPPORT;
+ rc = drm_probe(pState, vboxvideo_pciidlist);
+ if (rc == DDI_SUCCESS)
+ {
+ pState->drm_supported = DRM_SUPPORT;
+
+ /*
+ * Call the common attach DRM routine.
+ */
+ rc = drm_attach(pState);
+ if (rc == DDI_SUCCESS)
+ {
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxVideoSolarisAttach drm_attach failed.rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxVideoSolarisAttach drm_probe failed.rc=%d\n", rc));
+
+ drm_supp_unregister(pDRMHandle);
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxVideoSolarisAttach drm_supp_register failed.\n"));
+
+ ddi_soft_state_free(g_pVBoxVideoSolarisState, Instance);
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxVideoSolarisAttach failed to alloc memory for soft state.rc=%d\n", rc));
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+ }
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Detach entry point, to detach a device to the system or suspend it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (detach/suspend).
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxVideoSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFlow((DEVICE_NAME ":VBoxVideoSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ int Instance = ddi_get_instance(pDip);
+ drm_device_t *pState = ddi_get_soft_state(g_pVBoxVideoSolarisState, Instance);
+ if (pState)
+ {
+ drm_detach(pState);
+ drm_supp_unregister(pState->drm_handle);
+ ddi_soft_state_free(g_pVBoxVideoSolarisState, Instance);
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxVideoSolarisDetach failed to get soft state.\n"));
+
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+ }
+ return DDI_FAILURE;
+}
+
+
+/*
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VBoxVideoSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ LogFlow((DEVICE_NAME ":VBoxGuestSolarisGetInfo\n"));
+
+ int rc = DDI_FAILURE;
+ int Instance = drm_dev_to_instance((dev_t)pvArg);
+ drm_device_t *pState = NULL;
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ pState = ddi_get_soft_state(g_pVBoxVideoSolarisState, Instance);
+ if ( pState
+ && pState->dip)
+ {
+ *ppvResult = (void *)pState->dip;
+ rc = DDI_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":VBoxGuestSolarisGetInfo state or state's devinfo invalid.\n"));
+ rc = DDI_FAILURE;
+ }
+ break;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ *ppvResult = (void *)(uintptr_t)Instance;
+ rc = DDI_SUCCESS;
+ break;
+ }
+
+ default:
+ {
+ rc = DDI_FAILURE;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+static int vboxVideoSolarisLoad(drm_device_t *pDevice, unsigned long fFlag)
+{
+ return 0;
+}
+
+static int vboxVideoSolarisUnload(drm_device_t *pDevice)
+{
+ return 0;
+}
+
+static void vboxVideoSolarisLastClose(drm_device_t *pDevice)
+{
+}
+
+static void vboxVideoSolarisPreClose(drm_device_t *pDevice, drm_file_t *pFile)
+{
+}
+
+
+static void vboxVideoSolarisConfigure(drm_driver_t *pDriver)
+{
+ /*
+ * DRM entry points, use the common DRM extension wherever possible.
+ */
+ pDriver->buf_priv_size = 1;
+ pDriver->load = vboxVideoSolarisLoad;
+ pDriver->unload = vboxVideoSolarisUnload;
+ pDriver->preclose = vboxVideoSolarisPreClose;
+ pDriver->lastclose = vboxVideoSolarisLastClose;
+ pDriver->device_is_agp = drm_device_is_agp;
+#if 0
+ pDriver->get_vblank_counter = drm_vblank_count;
+ pDriver->enable_vblank = NULL;
+ pDriver->disable_vblank = NULL;
+ pDriver->irq_install = drm_driver_irq_install;
+ pDriver->irq_preinstall = drm_driver_irq_preinstall;
+ pDriver->irq_postinstall = drm_driver_irq_postinstall;
+ pDirver->irq_uninstall = drm_driver_irq_uninstall;
+ pDriver->irq_handler = drm_driver_irq_handler;
+
+ pDriver->driver_ioctls =
+ pDriver->max_driver_ioctls =
+#endif
+
+ pDriver->driver_name = DRIVER_NAME;
+ pDriver->driver_desc = DRIVER_DESC;
+ pDriver->driver_date = DRIVER_DATE;
+ pDriver->driver_major = DRIVER_MAJOR;
+ pDriver->driver_minor = DRIVER_MINOR;
+ pDriver->driver_patchlevel = DRIVER_PATCHLEVEL;
+
+ pDriver->use_agp = 1;
+ pDriver->require_agp = 1;
+ pDriver->use_irq = 1;
+}
+
diff --git a/src/VBox/Additions/solaris/Installer/VBox.sh b/src/VBox/Additions/solaris/Installer/VBox.sh
new file mode 100755
index 00000000..c2935a56
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/VBox.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+# $Id: VBox.sh $
+## @file
+# VirtualBox startup script for Solaris Guests Additions
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+CURRENT_ISA=`isainfo -k`
+if test "$CURRENT_ISA" = "amd64"; then
+ INSTALL_DIR="/opt/VirtualBoxAdditions/amd64"
+else
+ INSTALL_DIR="/opt/VirtualBoxAdditions"
+fi
+
+APP=`basename $0`
+case "$APP" in
+ VBoxClient)
+ exec "$INSTALL_DIR/VBoxClient" "$@"
+ ;;
+ VBoxService)
+ exec "$INSTALL_DIR/VBoxService" "$@"
+ ;;
+ VBoxControl)
+ exec "$INSTALL_DIR/VBoxControl" "$@"
+ ;;
+ *)
+ echo "Unknown application - $APP"
+ exit 1
+ ;;
+esac
+exit 0
diff --git a/src/VBox/Additions/solaris/Installer/makepackage.sh b/src/VBox/Additions/solaris/Installer/makepackage.sh
new file mode 100755
index 00000000..abe33065
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/makepackage.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+# $Id: makepackage.sh $
+## @file
+# VirtualBox Solaris Guest Additions package creation script.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+#
+# Usage:
+# makespackage.sh $(PATH_TARGET)/install packagename svnrev
+
+if test -z "$3"; then
+ echo "Usage: $0 installdir packagename svnrev"
+ exit 1
+fi
+ostype=`uname -s`
+if test "$ostype" != "Linux" && test "$ostype" != "SunOS" ; then
+ echo "Linux/Solaris not detected."
+ exit 1
+fi
+
+VBOX_BASEPKG_DIR=$1
+VBOX_INSTALLED_DIR="$VBOX_BASEPKG_DIR"/opt/VirtualBoxAdditions
+VBOX_PKGFILENAME=$2
+VBOX_SVN_REV=$3
+
+VBOX_PKGNAME=SUNWvboxguest
+VBOX_AWK=/usr/bin/awk
+case "$ostype" in
+"SunOS")
+ VBOX_GGREP=/usr/sfw/bin/ggrep
+ VBOX_SOL_PKG_DEV=/var/spool/pkg
+ ;;
+*)
+ VBOX_GGREP=`which grep`
+ VBOX_SOL_PKG_DEV=$4
+ ;;
+esac
+VBOX_AWK=/usr/bin/awk
+
+# check for GNU grep we use which might not ship with all Solaris
+if test ! -f "$VBOX_GGREP" && test ! -h "$VBOX_GGREP"; then
+ echo "## GNU grep not found in $VBOX_GGREP."
+ exit 1
+fi
+
+# bail out on non-zero exit status
+set -e
+
+# Fixup filelist using awk, the parameters must be in awk syntax
+# params: filename condition action
+filelist_fixup()
+{
+ "$VBOX_AWK" 'NF == 6 && '"$2"' { '"$3"' } { print }' "$1" > "tmp-$1"
+ mv -f "tmp-$1" "$1"
+}
+
+dirlist_fixup()
+{
+ "$VBOX_AWK" 'NF == 6 && $1 == "d" && '"$2"' { '"$3"' } { print }' "$1" > "tmp-$1"
+ mv -f "tmp-$1" "$1"
+}
+
+# Create relative hardlinks
+cd "$VBOX_INSTALLED_DIR"
+ln -f ./VBoxISAExec $VBOX_INSTALLED_DIR/VBoxService
+ln -f ./VBoxISAExec $VBOX_INSTALLED_DIR/VBoxClient
+ln -f ./VBoxISAExec $VBOX_INSTALLED_DIR/VBoxControl
+ln -f ./VBoxISAExec $VBOX_INSTALLED_DIR/vboxmslnk
+
+# prepare file list
+cd "$VBOX_BASEPKG_DIR"
+echo 'i pkginfo=./vboxguest.pkginfo' > prototype
+echo 'i postinstall=./postinstall.sh' >> prototype
+echo 'i preremove=./preremove.sh' >> prototype
+echo 'i space=./vboxguest.space' >> prototype
+echo 'i depend=./vboxguest.depend' >> prototype
+if test -f "./vboxguest.copyright"; then
+ echo 'i copyright=./vboxguest.copyright' >> prototype
+fi
+
+# Exclude directory entries to not cause conflicts (owner,group) with existing directories in the system
+find . ! -type d | $VBOX_GGREP -v -E 'prototype|makepackage.sh|vboxguest.pkginfo|postinstall.sh|preremove.sh|vboxguest.space|vboxguest.depend|vboxguest.copyright' | pkgproto >> prototype
+
+# Include opt/VirtualBoxAdditions and subdirectories as we want uninstall to clean up directory structure as well
+find . -type d | $VBOX_GGREP -E 'opt/VirtualBoxAdditions|var/svc/manifest/application/virtualbox' | pkgproto >> prototype
+
+# Include /etc/fs/vboxfs (as we need to create the subdirectory)
+find . -type d | $VBOX_GGREP -E 'etc/fs/vboxfs' | pkgproto >> prototype
+
+
+# don't grok for the class files
+filelist_fixup prototype '$2 == "none"' '$5 = "root"; $6 = "bin"'
+
+# VBoxService requires suid
+filelist_fixup prototype '$3 == "opt/VirtualBoxAdditions/VBoxService"' '$4 = "4755"'
+filelist_fixup prototype '$3 == "opt/VirtualBoxAdditions/amd64/VBoxService"' '$4 = "4755"'
+
+# Manifest class action scripts
+filelist_fixup prototype '$3 == "var/svc/manifest/application/virtualbox/vboxservice.xml"' '$2 = "manifest";$6 = "sys"'
+filelist_fixup prototype '$3 == "var/svc/manifest/application/virtualbox/vboxmslnk.xml"' '$2 = "manifest";$6 = "sys"'
+
+# vboxguest
+filelist_fixup prototype '$3 == "usr/kernel/drv/vboxguest"' '$6="sys"'
+filelist_fixup prototype '$3 == "usr/kernel/drv/amd64/vboxguest"' '$6="sys"'
+
+# vboxms
+filelist_fixup prototype '$3 == "usr/kernel/drv/vboxms"' '$6="sys"'
+filelist_fixup prototype '$3 == "usr/kernel/drv/amd64/vboxms"' '$6="sys"'
+
+# Use 'root' as group so as to match attributes with the previous installation and prevent a conflict. Otherwise pkgadd bails out thinking
+# we're violating directory attributes of another (non existing) package
+dirlist_fixup prototype '$3 == "var/svc/manifest/application/virtualbox"' '$6 = "root"'
+
+echo " --- start of prototype ---"
+cat prototype
+echo " --- end of prototype --- "
+
+# explicitly set timestamp to shutup warning
+VBOXPKG_TIMESTAMP=vboxguest`date '+%Y%m%d%H%M%S'`_r$VBOX_SVN_REV
+
+# create the package instance
+pkgmk -d $VBOX_SOL_PKG_DEV -p $VBOXPKG_TIMESTAMP -o -r .
+
+# translate into package datastream
+pkgtrans -s -o "$VBOX_SOL_PKG_DEV" `pwd`/$VBOX_PKGFILENAME "$VBOX_PKGNAME"
+
+rm -rf "$VBOX_SOL_PKG_DEV/$VBOX_PKGNAME"
+exit $?
+
diff --git a/src/VBox/Additions/solaris/Installer/postinstall.sh b/src/VBox/Additions/solaris/Installer/postinstall.sh
new file mode 100755
index 00000000..4bfdfc78
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/postinstall.sh
@@ -0,0 +1,448 @@
+#!/bin/sh
+# $Id: postinstall.sh $
+## @file
+# VirtualBox postinstall script for Solaris Guest Additions.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# LC_ALL should take precedence over LC_* and LANG but whatever...
+LC_ALL=C
+export LC_ALL
+
+LANG=C
+export LANG
+
+# "Remote" installs ('pkgadd -R') can skip many of the steps below.
+REMOTE_INST=0
+BASEDIR_OPT=""
+if test "x${PKG_INSTALL_ROOT:-/}" != "x/"; then
+ BASEDIR_OPT="-R $PKG_INSTALL_ROOT"
+ REMOTE_INST=1
+fi
+export REMOTE_INST
+export BASEDIR_OPT
+
+# uncompress(directory, file)
+# Updates package metadata and uncompresses the file.
+uncompress_file()
+{
+ if test -z "$1" || test -z "$2"; then
+ echo "missing argument to uncompress_file()"
+ return 1
+ fi
+
+ # Remove compressed path from the pkg
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST "$1/$2.Z" 1>/dev/null
+
+ # Add uncompressed path to the pkg
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST "$1/$2" f
+
+ # Uncompress the file (removes compressed file when done)
+ uncompress -f "$1/$2.Z" > /dev/null 2>&1
+}
+
+# uncompress_files(directory_with_*.Z_files)
+uncompress_files()
+{
+ for i in "${1}/"*.Z; do
+ uncompress_file "${1}" "`basename \"${i}\" .Z`"
+ done
+}
+
+solaris64dir="amd64"
+solaris32dir="i386"
+vboxadditions_path="${PKG_INSTALL_ROOT}/opt/VirtualBoxAdditions"
+vboxadditions32_path=$vboxadditions_path/$solaris32dir
+vboxadditions64_path=$vboxadditions_path/$solaris64dir
+
+# get the current zone
+currentzone=`zonename`
+# get what ISA the guest is running
+cputype=`isainfo -k`
+if test "$cputype" = "amd64"; then
+ isadir=$solaris64dir
+else
+ isadir=""
+fi
+
+vboxadditionsisa_path=$vboxadditions_path/$isadir
+
+
+# uncompress if necessary
+if test -f "$vboxadditions32_path/VBoxClient.Z" || test -f "$vboxadditions64_path/VBoxClient.Z"; then
+ echo "Uncompressing files..."
+ if test -f "$vboxadditions32_path/VBoxClient.Z"; then
+ uncompress_files "$vboxadditions32_path"
+ fi
+ if test -f "$vboxadditions64_path/VBoxClient.Z"; then
+ uncompress_files "$vboxadditions64_path"
+ fi
+fi
+
+
+if test "$currentzone" = "global"; then
+ # vboxguest.sh would've been installed, we just need to call it.
+ echo "Configuring VirtualBox guest kernel module..."
+ # stop all previous modules (vboxguest, vboxfs) and load vboxguest
+ # ('vboxguest.sh start' only starts vboxguest).
+ if test "$REMOTE_INST" -eq 0; then
+ $vboxadditions_path/vboxguest.sh stopall silentunload
+ fi
+ $vboxadditions_path/vboxguest.sh start
+
+ # Figure out group to use for /etc/devlink.tab (before Solaris 11 SRU6
+ # it was always using group sys)
+ group=sys
+ if [ -f /etc/dev/reserved_devnames ]; then
+ # Solaris 11 SRU6 and later use group root (check a file which isn't
+ # tainted by VirtualBox install scripts and allow no other group)
+ refgroup=`LC_ALL=C /usr/bin/ls -lL /etc/dev/reserved_devnames | awk '{ print $4 }' 2>/dev/null`
+ if [ $? -eq 0 -a "x$refgroup" = "xroot" ]; then
+ group=root
+ fi
+ unset refgroup
+ fi
+
+ sed -e '/name=vboxguest/d' ${PKG_INSTALL_ROOT}/etc/devlink.tab > ${PKG_INSTALL_ROOT}/etc/devlink.vbox
+ echo "type=ddi_pseudo;name=vboxguest \D" >> ${PKG_INSTALL_ROOT}/etc/devlink.vbox
+ chmod 0644 ${PKG_INSTALL_ROOT}/etc/devlink.vbox
+ chown root:$group ${PKG_INSTALL_ROOT}/etc/devlink.vbox
+ mv -f ${PKG_INSTALL_ROOT}/etc/devlink.vbox ${PKG_INSTALL_ROOT}/etc/devlink.tab
+
+ # create the device link
+ /usr/sbin/devfsadm $BASEDIR_OPT -i vboxguest
+fi
+
+# create links
+echo "Creating links..."
+if test "$currentzone" = "global"; then
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST /dev/vboxguest=../devices/pci@0,0/pci80ee,cafe@4:vboxguest s
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST /dev/vboxms=../devices/pseudo/vboxms@0:vboxms s
+fi
+
+# check if X.Org exists (snv_130 and higher have /usr/X11/* as /usr/*)
+if test -f "${PKG_INSTALL_ROOT}/usr/bin/Xorg"; then
+ xorgbin="${PKG_INSTALL_ROOT}/usr/bin/Xorg"
+elif test -f "${PKG_INSTALL_ROOT}/usr/X11/bin/Xorg"; then
+ xorgbin="${PKG_INSTALL_ROOT}/usr/X11/bin/Xorg"
+else
+ xorgbin=""
+ retval=0
+fi
+
+# Install Xorg components to the required places
+if test ! -z "$xorgbin"; then
+ xorgversion_long=`$xorgbin -version 2>&1 | grep "X Window System Version"`
+ xorgversion=`/usr/bin/expr "${xorgversion_long}" : 'X Window System Version \([^ ]*\)'`
+ if test -z "$xorgversion_long"; then
+ xorgversion_long=`$xorgbin -version 2>&1 | grep "X.Org X Server"`
+ xorgversion=`/usr/bin/expr "${xorgversion_long}" : 'X.Org X Server \([^ ]*\)'`
+ fi
+
+ # "X.Y.Z" - strip off all numerics after the 2nd '.' character, e.g. "1.11.3" -> "1.11"
+ # The second sed invocation removes all '.' characters, e.g. "1.11" -> "111".
+ fileversion=`echo $xorgversion | sed "s/\.[0-9]*//2" | sed "s/\.//"`
+ vboxvideo_src="vboxvideo_drv_$fileversion.so"
+
+ # Handle exceptions now where the X.org version does not exactly match the file-version.
+ case "$xorgversion" in
+ 1.5.99 )
+ vboxvideo_src="vboxvideo_drv_16.so"
+ ;;
+ 7.2.* )
+ vboxvideo_src="vboxvideo_drv_71.so"
+ ;;
+ 6.9.* )
+ vboxvideo_src="vboxvideo_drv_70.so"
+ ;;
+ esac
+
+ retval=0
+ if test -z "$vboxvideo_src"; then
+ echo "*** Unknown version of the X Window System installed."
+ echo "*** Failed to install the VirtualBox X Window System drivers."
+
+ # Exit as partially failed installation
+ retval=2
+ elif test ! -f "$vboxadditions32_path/$vboxvideo_src" && test ! -f "$vboxadditions64_path/$vboxvideo_src"; then
+ if test ! -f "${PKG_INSTALL_ROOT}/usr/lib/xorg/modules/drivers/vboxvideo_drv.so"; then
+ # Xorg 1.19 and later (delivered first in st_006) already contain a driver
+ # for vboxvideo so advise users to install the required package if it isn't
+ # already present.
+ echo "As of X.Org Server 1.19, the VirtualBox graphics driver (vboxvideo) is part"
+ echo "of Solaris. Please install the package pkg:/x11/server/xorg/driver/xorg-video-vboxvideo"
+ echo "from the package repository for the vboxvideo_drv.so graphics driver."
+ fi
+ else
+ echo "Installing video driver for X.Org $xorgversion..."
+
+ # Determine destination paths (snv_130 and above use "/usr/lib/xorg", older use "/usr/X11/lib"
+ vboxvideo32_dest_base="${PKG_INSTALL_ROOT}/usr/lib/xorg/modules/drivers"
+ if test ! -d $vboxvideo32_dest_base; then
+ vboxvideo32_dest_base="${PKG_INSTALL_ROOT}/usr/X11/lib/modules/drivers"
+ fi
+
+ vboxvideo64_dest_base=$vboxvideo32_dest_base/$solaris64dir
+
+ # snv_163 drops 32-bit support completely, and uses 32-bit locations for the 64-bit stuff. Ugly.
+ # We try to detect this by looking at bitness of "vesa_drv.so", and adjust our destination paths accordingly.
+ # We do not rely on using Xorg -version's ABI output because some builds (snv_162 iirc) have 64-bit ABI with
+ # 32-bit file locations.
+ if test -f "$vboxvideo32_dest_base/vesa_drv.so"; then
+ bitsize=`file "$vboxvideo32_dest_base/vesa_drv.so" | grep -i "32-bit"`
+ skip32="no"
+ else
+ echo "* Warning vesa_drv.so missing. Assuming Xorg ABI is 64-bit..."
+ fi
+
+ if test -z "$bitsize"; then
+ skip32="yes"
+ vboxvideo64_dest_base=$vboxvideo32_dest_base
+ fi
+
+ # Make sure destination path exists
+ if test ! -d $vboxvideo64_dest_base; then
+ echo "*** Missing destination paths for video module. Aborting."
+ echo "*** Failed to install the VirtualBox X Window System driver."
+
+ # Exit as partially failed installation
+ retval=2
+ else
+ # 32-bit x11 drivers
+ if test "$skip32" = "no" && test -f "$vboxadditions32_path/$vboxvideo_src"; then
+ vboxvideo_dest="$vboxvideo32_dest_base/vboxvideo_drv.so"
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST "$vboxvideo_dest" f
+ cp "$vboxadditions32_path/$vboxvideo_src" "$vboxvideo_dest"
+
+ # Removing redundant names from pkg and files from disk
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST $vboxadditions32_path/vboxvideo_drv_* 1>/dev/null
+ rm -f $vboxadditions32_path/vboxvideo_drv_*
+ fi
+
+ # 64-bit x11 drivers
+ if test -f "$vboxadditions64_path/$vboxvideo_src"; then
+ vboxvideo_dest="$vboxvideo64_dest_base/vboxvideo_drv.so"
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST "$vboxvideo_dest" f
+ cp "$vboxadditions64_path/$vboxvideo_src" "$vboxvideo_dest"
+
+ # Removing redundant names from pkg and files from disk
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST $vboxadditions64_path/vboxvideo_drv_* 1>/dev/null
+ rm -f $vboxadditions64_path/vboxvideo_drv_*
+ fi
+
+ # Some distros like Indiana have no xorg.conf, deal with this
+ if test ! -f '${PKG_INSTALL_ROOT}/etc/X11/xorg.conf' && \
+ test ! -f '${PKG_INSTALL_ROOT}/etc/X11/.xorg.conf'; then
+
+ # Xorg 1.3.x+ should use the modeline-less Xorg confs while older should
+ # use ones with all the video modelines in place. Argh.
+ xorgconf_file="solaris_xorg_modeless.conf"
+ xorgconf_unfit="solaris_xorg.conf"
+ case "$xorgversion" in
+ 7.1.* | 7.2.* | 6.9.* | 7.0.* )
+ xorgconf_file="solaris_xorg.conf"
+ xorgconf_unfit="solaris_xorg_modeless.conf"
+ ;;
+ esac
+
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST $vboxadditions_path/$xorgconf_file 1>/dev/null
+ mv -f $vboxadditions_path/$xorgconf_file ${PKG_INSTALL_ROOT}/etc/X11/.xorg.conf
+
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST $vboxadditions_path/$xorgconf_unfit 1>/dev/null
+ rm -f $vboxadditions_path/$xorgconf_unfit
+ fi
+
+ # Check for VirtualBox graphics card
+ # S10u10's prtconf doesn't support the '-d' option, so let's use -v even though it's slower.
+ is_vboxgraphics=`${PKG_INSTALL_ROOT}/usr/sbin/prtconf -v | grep -i pci80ee,beef`
+ if test "$?" -eq 0; then
+ drivername="vboxvideo"
+ else
+ # Check for VMware graphics card
+ is_vmwaregraphics=`${PKG_INSTALL_ROOT}/usr/sbin/prtconf -v | grep -i pci15ad,405`
+ if test "$?" -eq 0; then
+ echo "Configuring X.Org to use VMware SVGA graphics driver..."
+ drivername="vmware"
+ fi
+ fi
+
+ # Adjust xorg.conf with video driver sections if a supported graphics card is found
+ if test ! -z "$drivername"; then
+ $vboxadditions_path/x11config15sol.pl "$drivername"
+ else
+ # No supported graphics card found, do nothing.
+ echo "## No supported graphics card found. Skipped configuring of X.org drivers."
+ fi
+ fi
+ fi
+
+
+ # Setup our VBoxClient
+ echo "Configuring client..."
+ vboxclient_src=$vboxadditions_path
+ vboxclient_dest="${PKG_INSTALL_ROOT}/usr/share/gnome/autostart"
+ clientinstalled=0
+ if test -d "$vboxclient_dest"; then
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST $vboxclient_dest/vboxclient.desktop=$vboxadditions_path/vboxclient.desktop s
+ clientinstalled=1
+ fi
+ vboxclient_dest="${PKG_INSTALL_ROOT}/usr/dt/config/Xsession.d"
+ if test -d "$vboxclient_dest"; then
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST $vboxclient_dest/1099.vboxclient=$vboxadditions_path/1099.vboxclient s
+ clientinstalled=1
+ fi
+
+ # Try other autostart locations if none of the above ones work
+ if test $clientinstalled -eq 0; then
+ vboxclient_dest="${PKG_INSTALL_ROOT}/etc/xdg/autostart"
+ if test -d "$vboxclient_dest"; then
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST $vboxclient_dest/1099.vboxclient=$vboxadditions_path/1099.vboxclient s
+ clientinstalled=1
+ else
+ echo "*** Failed to configure client, couldn't find any autostart directory!"
+ # Exit as partially failed installation
+ retval=2
+ fi
+ fi
+else
+ echo "(*) X.Org not found, skipped configuring X.Org guest additions."
+fi
+
+
+# Shared Folder kernel module (different for S10 & Nevada)
+osverstr=`uname -r`
+vboxfsmod="vboxfs"
+vboxfsunused="vboxfs_s10"
+if test "$osverstr" = "5.10"; then
+ vboxfsmod="vboxfs_s10"
+ vboxfsunused="vboxfs"
+fi
+
+# Move the appropriate module to kernel/fs & remove the unused module name from pkg and file from disk
+# 64-bit shared folder module
+if test -f "$vboxadditions64_path/$vboxfsmod"; then
+ echo "Installing 64-bit shared folders module..."
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST "/usr/kernel/fs/$solaris64dir/vboxfs" f
+ mv -f $vboxadditions64_path/$vboxfsmod ${PKG_INSTALL_ROOT}/usr/kernel/fs/$solaris64dir/vboxfs
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST $vboxadditions64_path/$vboxfsmod 1>/dev/null
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST $vboxadditions64_path/$vboxfsunused 1>/dev/null
+ rm -f $vboxadditions64_path/$vboxfsunused
+fi
+
+# 32-bit shared folder module
+if test -f "$vboxadditions32_path/$vboxfsmod"; then
+ echo "Installing 32-bit shared folders module..."
+ /usr/sbin/installf $BASEDIR_OPT -c none $PKGINST "/usr/kernel/fs/vboxfs" f
+ mv -f $vboxadditions32_path/$vboxfsmod ${PKG_INSTALL_ROOT}/usr/kernel/fs/vboxfs
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST $vboxadditions32_path/$vboxfsmod 1>/dev/null
+ /usr/sbin/removef $BASEDIR_OPT $PKGINST $vboxadditions32_path/$vboxfsunused 1>/dev/null
+ rm -f $vboxadditions32_path/$vboxfsunused
+fi
+
+# Add a group "vboxsf" for Shared Folders access
+# All users which want to access the auto-mounted Shared Folders have to
+# be added to this group.
+if test "$REMOTE_INST" -eq 0; then
+ groupadd vboxsf >/dev/null 2>&1
+fi
+
+# Move the pointer integration module to kernel/drv & remove the unused module name from pkg and file from disk
+
+# Finalize
+/usr/sbin/removef $BASEDIR_OPT -f $PKGINST
+/usr/sbin/installf $BASEDIR_OPT -f $PKGINST
+
+
+if test "$currentzone" = "global"; then
+ /usr/sbin/devfsadm $BASEDIR_OPT -i vboxguest
+
+ # Setup VBoxService and vboxmslnk and start the services automatically
+ echo "Configuring VBoxService and vboxmslnk services (this might take a while)..."
+ cmax=32
+ cslept=0
+ success=0
+ sync
+
+ if test "$REMOTE_INST" -eq 0; then
+ # Since S11 the way to import a manifest is via restarting manifest-import which is asynchronous and can
+ # take a while to complete, using disable/enable -s doesn't work either. So we restart it, and poll in
+ # 1 second intervals to see if our service has been successfully imported and timeout after 'cmax' seconds.
+ /usr/sbin/svcadm restart svc:system/manifest-import:default
+ /usr/bin/svcs virtualbox/vboxservice >/dev/null 2>&1 && /usr/bin/svcs virtualbox/vboxmslnk >/dev/null 2>&1
+ while test "$?" -ne 0;
+ do
+ sleep 1
+ cslept=`expr $cslept + 1`
+ if test "$cslept" -eq "$cmax"; then
+ success=1
+ break
+ fi
+ /usr/bin/svcs virtualbox/vboxservice >/dev/null 2>&1 && /usr/bin/svcs virtualbox/vboxmslnk >/dev/null 2>&1
+ done
+ if test "$success" -eq 0; then
+ echo "Enabling services..."
+ /usr/sbin/svcadm enable -s virtualbox/vboxservice
+ /usr/sbin/svcadm enable -s virtualbox/vboxmslnk
+ else
+ echo "## Service import failed."
+ echo "## See /var/svc/log/system-manifest-import:default.log for details."
+ # Exit as partially failed installation
+ retval=2
+ fi
+ fi
+
+ # Update boot archive
+ BOOTADMBIN=/sbin/bootadm
+ if test -x "$BOOTADMBIN"; then
+ if test -h "${PKG_INSTALL_ROOT}/dev/vboxguest"; then
+ echo "Updating boot archive..."
+ $BOOTADMBIN update-archive $BASEDIR_OPT > /dev/null
+ else
+ echo "## Guest kernel module doesn't seem to be up. Skipped explicit boot-archive update."
+ fi
+ else
+ echo "## $BOOTADMBIN not found/executable. Skipped explicit boot-archive update."
+ fi
+fi
+
+echo "Done."
+if test "$REMOTE_INST" -eq 0; then
+ if test $retval -eq 0; then
+ if test ! -z "$xorgbin"; then
+ echo "Please re-login to activate the X11 guest additions."
+ fi
+ echo "If you have just un-installed the previous guest additions a REBOOT is required."
+ fi
+fi
+exit $retval
+
diff --git a/src/VBox/Additions/solaris/Installer/preremove.sh b/src/VBox/Additions/solaris/Installer/preremove.sh
new file mode 100755
index 00000000..16fb8070
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/preremove.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+# $Id: preremove.sh $
+## @file
+# VirtualBox preremove script for Solaris Guest Additions.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+LC_ALL=C
+export LC_ALL
+
+LANG=C
+export LANG
+
+echo "Removing VirtualBox service..."
+
+# stop and unregister VBoxService
+if [ "${PKG_INSTALL_ROOT:-/}" = "/" ] ; then
+ /usr/sbin/svcadm disable -s svc:/application/virtualbox/vboxservice:default
+ /usr/sbin/svcadm disable -s svc:/application/virtualbox/vboxmslnk:default
+ # Don't need to delete, taken care of by the manifest action
+ #/usr/sbin/svccfg delete svc:/application/virtualbox/vboxservice:default
+ #/usr/sbin/svccfg delete svc:/application/virtualbox/vboxmslnk:default
+ /usr/sbin/svcadm restart -s svc:/system/manifest-import:default
+
+ # stop VBoxClient
+ pkill -INT VBoxClient
+fi
+
+echo "Removing VirtualBox kernel modules..."
+
+# vboxguest.sh would've been installed, we just need to call it.
+
+${PKG_INSTALL_ROOT}/opt/VirtualBoxAdditions/vboxguest.sh stopall silentunload
+
+# Figure out group to use for /etc/devlink.tab (before Solaris 11 SRU6
+# it was always using group sys)
+group=sys
+if [ -f /etc/dev/reserved_devnames ]; then
+ # Solaris 11 SRU6 and later use group root (check a file which isn't
+ # tainted by VirtualBox install scripts and allow no other group)
+ refgroup=`LC_ALL=C /usr/bin/ls -lL /etc/dev/reserved_devnames | awk '{ print $4 }' 2>/dev/null`
+ if [ $? -eq 0 -a "x$refgroup" = "xroot" ]; then
+ group=root
+ fi
+ unset refgroup
+fi
+
+# remove devlink.tab entry for vboxguest
+sed -e '/name=vboxguest/d' ${PKG_INSTALL_ROOT}/etc/devlink.tab > ${PKG_INSTALL_ROOT}/etc/devlink.vbox
+chmod 0644 ${PKG_INSTALL_ROOT}/etc/devlink.vbox
+chown root:$group ${PKG_INSTALL_ROOT}/etc/devlink.vbox
+mv -f ${PKG_INSTALL_ROOT}/etc/devlink.vbox ${PKG_INSTALL_ROOT}/etc/devlink.tab
+
+# remove the link
+if test -h "${PKG_INSTALL_ROOT}/dev/vboxguest" || test -f "${PKG_INSTALL_ROOT}/dev/vboxguest"; then
+ rm -f ${PKG_INSTALL_ROOT}/dev/vboxdrv
+fi
+if test -h "${PKG_INSTALL_ROOT}/dev/vboxms" || test -f "${PKG_INSTALL_ROOT}/dev/vboxms"; then
+ rm -f ${PKG_INSTALL_ROOT}/dev/vboxms
+fi
+
+# Try and restore xorg.conf!
+echo "Restoring X.Org..."
+${PKG_INSTALL_ROOT}/opt/VirtualBoxAdditions/x11restore.pl
+
+
+echo "Done."
+
diff --git a/src/VBox/Additions/solaris/Installer/vbox_vendor_select b/src/VBox/Additions/solaris/Installer/vbox_vendor_select
new file mode 100755
index 00000000..3e823acd
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/vbox_vendor_select
@@ -0,0 +1,88 @@
+#!/bin/ksh93
+# $Id: vbox_vendor_select $
+## @file
+# ???
+#
+
+#
+# Copyright (C) 2013-2023 Oracle and/or its affiliates.
+# This file is based on mesa_vendor_select from Solaris 11.3 with the following copyright:
+#
+# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+LINKDIR=/system/volatile/opengl
+MESA_SELECT=/lib/opengl/ogl_select/mesa_vendor_select
+
+PATH=/usr/bin:/usr/sbin
+
+ARCH="$(uname -p)"
+
+case "${ARCH}" in
+ i386) ;;
+ *) exit 1 ;; # Unsupported architecture
+esac
+
+# We need Mesa for the parts we do not supply.
+if [[ ! -x "${MESA_SELECT}" ]]; then
+ exit 0
+fi
+
+if [[ $# -eq 1 ]] && [[ $1 == "identify" ]] ; then
+ # Probe time. Check whether this system supports pass-through.
+ # If so, emit an identity string attaching us to the current
+ # console identifier.
+ if /usr/bin/VBoxClient --check3d ; then
+ print "$(constype) vbox"
+ fi
+ return 0
+fi
+
+# Make a file link. $1 is the source path, $2 is the target path
+function make_link {
+ if [[ $# != 2 ]]; then
+ return
+ fi
+ if [[ -h $2 ]]; then
+ rm -f $2
+ fi
+ ln -sf $1 $2
+}
+
+# Start by setting up Mesa, as we use that for everything except the user
+# libraries.
+${MESA_SELECT}
+
+# User libraries
+if [[ -f ${LINKDIR}/lib/libGL.so.1 ]] && [[ -f /usr/lib/VBoxOGL.so ]] ; then
+ make_link /usr/lib/VBoxOGL.so ${LINKDIR}/lib/libGL.so.1
+fi
+if [[ -f ${LINKDIR}/lib/i386/libGL.so.1 ]] && \
+ [[ -f /usr/lib/i386/VBoxOGL.so ]] ; then
+ make_link /usr/lib/i386/VBoxOGL.so ${LINKDIR}/lib/i386/libGL.so.1
+fi
+if [[ -f ${LINKDIR}/lib/amd64/libGL.so.1 ]] && \
+ [[ -f /usr/lib/amd64/VBoxOGL.so ]] ; then
+ make_link /usr/lib/amd64/VBoxOGL.so ${LINKDIR}/lib/amd64/libGL.so.1
+fi
+
+return 0
diff --git a/src/VBox/Additions/solaris/Installer/vboxguest.depend b/src/VBox/Additions/solaris/Installer/vboxguest.depend
new file mode 100644
index 00000000..68196355
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/vboxguest.depend
@@ -0,0 +1 @@
+P SUNWuiu8 Iconv modules for UTF-8 Locale
diff --git a/src/VBox/Additions/solaris/Installer/vboxguest.pkginfo b/src/VBox/Additions/solaris/Installer/vboxguest.pkginfo
new file mode 100644
index 00000000..9456d0d4
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/vboxguest.pkginfo
@@ -0,0 +1,15 @@
+PKG="SUNWvboxguest"
+NAME="@VBOX_PRODUCT@ Guest Additions"
+SUNW_PRODNAME="@VBOX_PRODUCT@ Guest Additions"
+ARCH="@UNAME_P@"
+VERSION="@VBOX_VERSION_STRING@,REV=r@VBOX_SVN_REV@.@VBOX_VERSION_REVSTAMP@"
+SUNW_PRODVERS="@VBOX_VERSION_STRING@"
+SUNW_PKGVERS=1.0
+CATEGORY="application"
+VENDOR="@VBOX_VENDOR@"
+EMAIL="info@virtualbox.org"
+HOTLINE="Please contact your local service provider"
+BASEDIR="/"
+CLASSES=none manifest
+DESC="@VBOX_PRODUCT@ Guest Additions for Solaris guests"
+
diff --git a/src/VBox/Additions/solaris/Installer/vboxguest.sh b/src/VBox/Additions/solaris/Installer/vboxguest.sh
new file mode 100755
index 00000000..abf04ec2
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/vboxguest.sh
@@ -0,0 +1,310 @@
+#!/bin/sh
+# $Id: vboxguest.sh $
+## @file
+# VirtualBox Guest Additions kernel module control script for Solaris.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+LC_ALL=C
+export LC_ALL
+
+LANG=C
+export LANG
+
+SILENTUNLOAD=""
+MODNAME="vboxguest"
+VFSMODNAME="vboxfs"
+VMSMODNAME="vboxms"
+MODDIR32="${PKG_INSTALL_ROOT}/usr/kernel/drv"
+MODDIR64="${PKG_INSTALL_ROOT}/usr/kernel/drv/amd64"
+VFSDIR32="${PKG_INSTALL_ROOT}/usr/kernel/fs"
+VFSDIR64="${PKG_INSTALL_ROOT}/usr/kernel/fs/amd64"
+
+abort()
+{
+ echo 1>&2 "## $1"
+ exit 1
+}
+
+info()
+{
+ echo 1>&2 "$1"
+}
+
+check_if_installed()
+{
+ cputype=`isainfo -k`
+ modulepath="$MODDIR32/$MODNAME"
+ if test "$cputype" = "amd64"; then
+ modulepath="$MODDIR64/$MODNAME"
+ fi
+ if test -f "$modulepath"; then
+ return 0
+ fi
+ abort "VirtualBox kernel module ($MODNAME) NOT installed."
+}
+
+module_loaded()
+{
+ if test -z "$1"; then
+ abort "missing argument to module_loaded()"
+ fi
+
+ if test "$REMOTE_INST" -eq 1; then
+ return 1
+ fi
+
+ modname=$1
+ # modinfo should now work properly since we prevent module autounloading.
+ loadentry=`/usr/sbin/modinfo | grep "$modname "`
+ if test -z "$loadentry"; then
+ return 1
+ fi
+ return 0
+}
+
+vboxguest_loaded()
+{
+ module_loaded $MODNAME
+ return $?
+}
+
+vboxfs_loaded()
+{
+ module_loaded $VFSMODNAME
+ return $?
+}
+
+vboxms_loaded()
+{
+ module_loaded $VMSMODNAME
+ return $?
+}
+
+check_root()
+{
+ # the reason we don't use "-u" is that some versions of id are old and do not
+ # support this option (eg. Solaris 10) and do not have a "--version" to check it either
+ # so go with the uglier but more generic approach
+ idbin=`which id`
+ isroot=`$idbin | grep "uid=0"`
+ if test -z "$isroot"; then
+ abort "This program must be run with administrator privileges. Aborting"
+ fi
+}
+
+start_module()
+{
+ if test "$REMOTE_INST" -eq 1; then
+ /usr/sbin/add_drv $BASEDIR_OPT -i'pci80ee,cafe' -m'* 0666 root sys' $MODNAME 2>/dev/null || \
+ abort "Failed to install VirtualBox guest kernel module into ${PKG_INSTALL_ROOT}."
+ info "VirtualBox guest kernel module installed."
+ return
+ fi
+
+ /usr/sbin/add_drv -i'pci80ee,cafe' -m'* 0666 root sys' $MODNAME
+ if test ! vboxguest_loaded; then
+ abort "Failed to load VirtualBox guest kernel module."
+ elif test -c "/devices/pci@0,0/pci80ee,cafe@4:$MODNAME"; then
+ info "VirtualBox guest kernel module loaded."
+ else
+ info "VirtualBox guest kernel module failed to attach."
+ fi
+}
+
+stop_module()
+{
+ if test "$REMOTE_INST" -eq 1; then
+ /usr/sbin/rem_drv $BASEDIR_OPT $MODNAME || abort "Failed to uninstall VirtualBox guest kernel module."
+ info "VirtualBox guest kernel module uninstalled."
+ return
+ fi
+
+ if vboxguest_loaded; then
+ /usr/sbin/rem_drv $MODNAME || abort "Failed to unload VirtualBox guest kernel module."
+ info "VirtualBox guest kernel module unloaded."
+ elif test -z "$SILENTUNLOAD"; then
+ info "VirtualBox guest kernel module not loaded."
+ fi
+}
+
+start_vboxfs()
+{
+ if test "$REMOTE_INST" -eq 1; then
+ return
+ fi
+
+ if vboxfs_loaded; then
+ info "VirtualBox FileSystem kernel module already loaded."
+ else
+ /usr/sbin/modload -p fs/$VFSMODNAME || abort "Failed to load VirtualBox FileSystem kernel module."
+ if test ! vboxfs_loaded; then
+ info "Failed to load VirtualBox FileSystem kernel module."
+ else
+ info "VirtualBox FileSystem kernel module loaded."
+ fi
+ fi
+}
+
+stop_vboxfs()
+{
+ if test "$REMOTE_INST" -eq 1; then
+ return
+ fi
+
+ if vboxfs_loaded; then
+ vboxfs_mod_id=`/usr/sbin/modinfo | grep $VFSMODNAME | cut -f 1 -d ' ' `
+ if test -n "$vboxfs_mod_id"; then
+ /usr/sbin/modunload -i $vboxfs_mod_id || abort "Failed to unload VirtualBox FileSystem module."
+ info "VirtualBox FileSystem kernel module unloaded."
+ fi
+ elif test -z "$SILENTUNLOAD"; then
+ info "VirtualBox FileSystem kernel module not loaded."
+ fi
+}
+
+start_vboxms()
+{
+ if test "$REMOTE_INST" -eq 1; then
+ /usr/sbin/add_drv $BASEDIR_OPT -m'* 0666 root sys' $VMSMODNAME 2>/dev/null ||
+ abort "Failed to install VirtualBox pointer integration module."
+ info "VirtualBox pointer integration module installed."
+ return
+ fi
+
+ /usr/sbin/add_drv -m'* 0666 root sys' $VMSMODNAME
+ if test ! vboxms_loaded; then
+ abort "Failed to load VirtualBox pointer integration module."
+ elif test -c "/devices/pseudo/$VMSMODNAME@0:$VMSMODNAME"; then
+ info "VirtualBox pointer integration module loaded."
+ else
+ info "VirtualBox pointer integration module failed to attach."
+ fi
+}
+
+stop_vboxms()
+{
+ if test "$REMOTE_INST" -eq 1; then
+ /usr/sbin/rem_drv $BASEDIR_OPT $VMSMODNAME || abort "Failed to uninstall VirtualBox pointer integration module."
+ info "VirtualBox pointer integration module uninstalled."
+ return
+ fi
+
+ if vboxms_loaded; then
+ /usr/sbin/rem_drv $VMSMODNAME || abort "Failed to unload VirtualBox pointer integration module."
+ info "VirtualBox pointer integration module unloaded."
+ elif test -z "$SILENTUNLOAD"; then
+ info "VirtualBox pointer integration module not loaded."
+ fi
+}
+
+status_module()
+{
+ if vboxguest_loaded; then
+ info "Running."
+ else
+ info "Stopped."
+ fi
+}
+
+stop_all()
+{
+ stop_vboxms
+ stop_vboxfs
+ stop_module
+ return 0
+}
+
+restart_all()
+{
+ stop_all
+ start_module
+ start_vboxfs
+ start_vboxms
+ return 0
+}
+
+# "Remote" installs ('pkgadd -R') can skip many of the steps below.
+REMOTE_INST=0
+BASEDIR_OPT=""
+if test "x${PKG_INSTALL_ROOT:-/}" != "x/"; then
+ BASEDIR_OPT="-b $PKG_INSTALL_ROOT"
+ REMOTE_INST=1
+fi
+export REMOTE_INST
+export BASEDIR_OPT
+
+check_root
+check_if_installed
+
+if test "$2" = "silentunload"; then
+ SILENTUNLOAD="$2"
+fi
+
+case "$1" in
+stopall)
+ stop_all
+ ;;
+restartall)
+ restart_all
+ ;;
+start)
+ start_module
+ start_vboxms
+ ;;
+stop)
+ stop_vboxms
+ stop_module
+ ;;
+status)
+ status_module
+ ;;
+vfsstart)
+ start_vboxfs
+ ;;
+vfsstop)
+ stop_vboxfs
+ ;;
+vmsstart)
+ start_vboxms
+ ;;
+vmsstop)
+ stop_vboxms
+ ;;
+*)
+ echo "Usage: $0 {start|stop|restart|status}"
+ exit 1
+esac
+
+exit 0
+
diff --git a/src/VBox/Additions/solaris/Installer/vboxguest.space b/src/VBox/Additions/solaris/Installer/vboxguest.space
new file mode 100644
index 00000000..993a6f6d
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/vboxguest.space
@@ -0,0 +1,5 @@
+# Space file for VirtualBox Guest Additions installer
+# We only create a few links and copy a few files, reserve some space
+# pathname blocks inodes
+/ 5000 8
+
diff --git a/src/VBox/Additions/solaris/Installer/vboxservice.xml b/src/VBox/Additions/solaris/Installer/vboxservice.xml
new file mode 100644
index 00000000..25ac7162
--- /dev/null
+++ b/src/VBox/Additions/solaris/Installer/vboxservice.xml
@@ -0,0 +1,88 @@
+<?xml version='1.0'?>
+<!--
+#
+# Solaris SMF service manifest for VBoxService (timesync).
+#
+-->
+<!--
+ Copyright (C) 2008-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ The contents of this file may alternatively be used under the terms
+ of the Common Development and Distribution License Version 1.0
+ (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ in the VirtualBox distribution, in which case the provisions of the
+ CDDL are applicable instead of those of the GPL.
+
+ You may elect to license modified versions of this file under the
+ terms and conditions of either the GPL or the CDDL or both.
+
+ SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+-->
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+
+<service_bundle type='manifest' name='SUNWvboxguest:vboxservice'>
+
+<service
+ name='application/virtualbox/vboxservice'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance/>
+
+ <!-- Wait for devices to be initialized as we depend on vboxguest (PCI) -->
+ <dependency
+ name='milestone'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/milestone/devices:default' />
+ </dependency>
+
+ <!-- Wait for local filesystems to be mounted (just to be safe, don't start too early) -->
+ <dependency
+ name='filesystem-local'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/local:default' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/bin/VBoxService'
+ timeout_seconds='30' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>VirtualBox Service.</loctext>
+ </common_name>
+ </template>
+</service>
+
+</service_bundle>
+
diff --git a/src/VBox/Additions/solaris/Makefile.kmk b/src/VBox/Additions/solaris/Makefile.kmk
new file mode 100644
index 00000000..6151ff6b
--- /dev/null
+++ b/src/VBox/Additions/solaris/Makefile.kmk
@@ -0,0 +1,413 @@
+# $Id: Makefile.kmk $
+## @file
+# Makefile for the Solaris guest additions base directory.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#ifneq ($(KBUILD_HOST),solaris)
+#$(error "The Solaris guest additions installer can only be built on Solaris!")
+#endif
+
+ifeq ($(KBUILD_TARGET),solaris)
+ include $(PATH_SUB_CURRENT)/SharedFolders/Makefile.kmk
+ include $(PATH_SUB_CURRENT)/Mouse/Makefile.kmk
+ include $(PATH_SUB_CURRENT)/DRM/Makefile.kmk
+endif
+
+ifdef VBOX_WITH_COMBINED_SOLARIS_PACKAGE ## @todo remove this when tinderclient.pl is updated.
+ VBOX_WITH_COMBINED_GUEST_PACKAGE := 1
+endif
+
+PKGFILENAME := VBoxSolarisAdditions.pkg
+PKGINFO_ARCH = $(shell uname -p)
+PKGINFO_REVSTAMP = $(date %Y.%m.%d.%H.%M)
+VBOX_PATH_SOLARIS_ADDITION_INSTALLER := $(PATH_SUB_CURRENT)/Installer
+VBOX_PATH_X11_ADDITION_INSTALLER := $(PATH_ROOT)/src/VBox/Additions/x11/Installer
+
+SOLARIS_ADDDIR_NAME_64 := amd64
+SOLARIS_ADDDIR_NAME_32 := i386
+
+SOLARIS_ADDINST_OUT_DIR := $(PATH_TARGET)/AdditionsInstaller
+SOLARIS_VBOXADDINST_SUBDIR := /opt/VirtualBoxAdditions
+SOLARIS_VBOXADDINST_DIR := $(SOLARIS_ADDINST_OUT_DIR)$(SOLARIS_VBOXADDINST_SUBDIR)
+SOLARIS_VBOXADDINST_DIR_32 := $(SOLARIS_VBOXADDINST_DIR)/$(SOLARIS_ADDDIR_NAME_32)
+SOLARIS_VBOXADDINST_DIR_64 := $(SOLARIS_VBOXADDINST_DIR)/$(SOLARIS_ADDDIR_NAME_64)
+
+SOLARIS_ADD_OUT_BIN_64 := $(PATH_OUT_BASE)/solaris.amd64/$(KBUILD_TYPE)/bin/additions
+SOLARIS_ADD_OUT_BIN_32 := $(PATH_OUT_BASE)/solaris.x86/$(KBUILD_TYPE)/bin/additions
+SOLARIS_ADD_OUT_BIN_ISA := $(PATH_OUT_BASE)/solaris.$(KBUILD_TARGET_ARCH)/$(KBUILD_TYPE)/bin/additions
+
+SOLARIS_ADD_DRIVERINST_DIR := $(SOLARIS_ADDINST_OUT_DIR)/usr/kernel/drv
+SOLARIS_ADD_DRIVERINST_DIR_32 := $(SOLARIS_ADD_DRIVERINST_DIR)
+SOLARIS_ADD_DRIVERINST_DIR_64 := $(SOLARIS_ADD_DRIVERINST_DIR)/amd64
+
+SOLARIS_ADD_SYSLIBINST_DIR_32 := $(SOLARIS_ADDINST_OUT_DIR)/usr/lib
+SOLARIS_ADD_SYSLIBINST_DIR_64 := $(SOLARIS_ADDINST_OUT_DIR)/usr/lib/amd64
+
+SOLARIS_ADD_USRBIN_DIR := $(SOLARIS_ADDINST_OUT_DIR)/usr/bin
+SOLARIS_ADD_USRSBIN_DIR := $(SOLARIS_ADDINST_OUT_DIR)/usr/sbin
+SOLARIS_ADD_ETCFS_DIR := $(SOLARIS_ADDINST_OUT_DIR)/etc/fs/vboxfs
+SOLARIS_ADD_SERVICESINST_DIR := $(SOLARIS_ADDINST_OUT_DIR)/var/svc/manifest/application/virtualbox
+
+ifeq ($(KBUILD_TARGET_ARCH),x86)
+ SOLARIS_ADDDIR_NAME_ISA := $(SOLARIS_ADDDIR_NAME_32)
+ SOLARIS_VBOXADDINST_DIR_ISA := $(SOLARIS_VBOXADDINST_DIR_32)
+ SOLARIS_ADD_DRIVERINST_DIR_ISA := $(SOLARIS_ADD_DRIVERINST_DIR_32)
+ SOLARIS_ADD_SYSLIBINST_DIR_ISA := $(SOLARIS_ADD_SYSLIBINST_DIR_32)
+else
+ SOLARIS_ADDDIR_NAME_ISA := $(SOLARIS_ADDDIR_NAME_64)
+ SOLARIS_VBOXADDINST_DIR_ISA := $(SOLARIS_VBOXADDINST_DIR_64)
+ SOLARIS_ADD_DRIVERINST_DIR_ISA := $(SOLARIS_ADD_DRIVERINST_DIR_64)
+ SOLARIS_ADD_SYSLIBINST_DIR_ISA := $(SOLARIS_ADD_SYSLIBINST_DIR_64)
+endif
+
+ifeq ($(KBUILD_TYPE),debug)
+ BIN_COPY := $(CP) -f
+ BIN_COPY_SYMBOLS := $(CP) -f
+else
+ BIN_COPY := /usr/sfw/bin/gobjcopy -S -R .comment
+ BIN_COPY_SYMBOLS := /usr/sfw/bin/gobjcopy -g -R .comment
+ VBOX_COMPRESS := compress -f
+endif
+
+INSTALLS += solaris-addcommon solaris-addcommonbins
+PROGRAMS += VBoxAddISAExec
+PACKING += $(PATH_STAGE_BIN)/additions/$(PKGFILENAME)
+OTHER_CLEAN += $(PACKING) $(SOLARIS_ADDINST_OUT_DIR)/$(PKGFILENAME)
+
+#
+# VBoxAddISAExec
+#
+VBoxAddISAExec_TEMPLATE = VBoxGuestR3Exe
+VBoxAddISAExec_NAME = VBoxISAExec
+VBoxAddISAExec_INST = $(INST_ADDITIONS)
+VBoxAddISAExec_DEPS = $(VBOX_SVN_REV_KMK)
+VBoxAddISAExec_SOURCES = $(PATH_ROOT)/src/VBox/Installer/solaris/VBoxISAExec.c
+
+#
+# Install to $(PATH_STAGE_BIN)/additions/ files from various source paths (to pack them using rules)
+#
+solaris-addcommonbins_INST = bin/additions/
+solaris-addcommonbins_MODE = a+rx,u+w
+solaris-addcommonbins_SOURCES = \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxguest.sh \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/x11config.pl \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/x11config15sol.pl \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/x11restore.pl \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/98vboxadd-xclient=>1099.vboxclient \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/solaris_xorg.conf \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/solaris_xorg_modeless.conf \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vbox_vendor_select \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/VBox.sh
+
+solaris-addcommon_INST = bin/additions/
+solaris-addcommon_MODE = a+r,u+w
+solaris-addcommon_SOURCES = \
+ $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf=>vboxguest.conf \
+ $(PATH_ROOT)/src/VBox/Additions/solaris/Mouse/vboxms.conf=>vboxms.conf \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/vboxclient.desktop \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxservice.xml \
+ $(PATH_ROOT)/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml \
+ $(VBOX_BRAND_LICENSE_TXT)=>LICENSE
+
+#
+# Create full directory tree
+#
+BLDDIRS += \
+ $(SOLARIS_ADDINST_OUT_DIR) \
+ $(addprefix $(SOLARIS_ADDINST_OUT_DIR)/, \
+ opt \
+ opt/VirtualBoxAdditions \
+ opt/VirtualBoxAdditions/$(SOLARIS_ADDDIR_NAME_32) \
+ opt/VirtualBoxAdditions/$(SOLARIS_ADDDIR_NAME_64) \
+ usr \
+ usr/bin \
+ usr/sbin \
+ usr/lib \
+ usr/lib/amd64 \
+ usr/kernel \
+ usr/kernel/drv \
+ usr/kernel/drv/amd64 \
+ var \
+ var/svc \
+ var/svc/manifest \
+ var/svc/manifest/application \
+ var/svc/manifest/application/virtualbox \
+ )
+
+SOLARIS_ADD_STRIP_BINS = \
+ VBoxClient \
+ VBoxService \
+ VBoxControl \
+ vboxfsmount \
+ vboxfs \
+ vboxmslnk \
+ $(if ($VBOX_WITH_PAM),pam_vbox.so,) \
+ $(if $(VBOX_OSE),,vboxfs_s10) \
+ $(if $(VBOX_WITH_ADDITIONS_SHIPPING_AUDIO_TEST),VBoxAudioTest,)
+
+SOLARIS_ADD_DRIVERS = \
+ vboxguest \
+ vboxms
+
+SOLARIS_ADD_DRIVERS_CONF = \
+ vboxguest.conf \
+ vboxms.conf
+
+SOLARIS_ADD_XORG_DRIVERS = \
+ vboxvideo_drv_13.so \
+ vboxvideo_drv_14.so \
+ vboxvideo_drv_15.so \
+ vboxvideo_drv_16.so \
+ vboxvideo_drv_17.so \
+ vboxvideo_drv_18.so \
+ vboxvideo_drv_19.so \
+ vboxvideo_drv_110.so \
+ vboxvideo_drv_111.so \
+ vboxvideo_drv_112.so \
+ vboxvideo_drv_113.so \
+ vboxvideo_drv_114.so \
+ vboxvideo_drv_117.so \
+ vboxvideo_drv_118.so \
+ vboxvideo_drv_70.so \
+ vboxvideo_drv_71.so
+
+SOLARIS_ADD_COMMON_BINS = \
+ vboxguest.sh \
+ x11config15sol.pl \
+ x11restore.pl \
+ VBox.sh \
+ 1099.vboxclient \
+ vbox_vendor_select \
+ VBoxISAExec
+
+SOLARIS_ADD_USRBIN_LINKS = \
+ VBoxService \
+ VBoxClient \
+ VBoxControl
+
+SOLARIS_ADD_USRSBIN_LINKS = \
+ vboxmslnk
+
+SOLARIS_ADD_COMMON = \
+ vboxclient.desktop \
+ solaris_xorg.conf \
+ solaris_xorg_modeless.conf \
+ LICENSE
+
+SOLARIS_ADD_SERVICES = \
+ vboxservice.xml \
+ vboxmslnk.xml
+
+ifdef VBOX_COMPRESS
+ SOLARIS_ADD_COMPRESS_FILES = \
+ $(SOLARIS_ADD_XORG_DRIVERS) \
+ VBoxService \
+ VBoxClient \
+ VBoxControl
+endif
+
+ifdef VBOX_WITH_COMBINED_SOLARIS_GUEST_PACKAGE
+ SOLARIS_ARCH_ADD_DEPFILES = \
+ $(addprefix $(SOLARIS_VBOXADDINST_DIR_64)/,$(SOLARIS_ADD_STRIP_BINS)) \
+ $(addprefix $(SOLARIS_VBOXADDINST_DIR_64)/,$(SOLARIS_ADD_XORG_DRIVERS)) \
+ $(addprefix $(SOLARIS_VBOXADDINST_DIR_32)/,$(SOLARIS_ADD_STRIP_BINS)) \
+ $(addprefix $(SOLARIS_ADD_DRIVERINST_DIR_64)/,$(SOLARIS_ADD_DRIVERS)) \
+ $(addprefix $(SOLARIS_ADD_DRIVERINST_DIR_32)/,$(SOLARIS_ADD_DRIVERS)) \
+ $(addprefix $(SOLARIS_ADD_DRIVERINST_DIR)/,$(SOLARIS_ADD_DRIVERS_CONF)) \
+ $(addprefix $(SOLARIS_VBOXADDINST_DIR_32)/,$(SOLARIS_ADD_XORG_DRIVERS))
+else
+ SOLARIS_ARCH_ADD_DEPFILES = \
+ $(addprefix $(SOLARIS_VBOXADDINST_DIR_ISA)/,$(SOLARIS_ADD_STRIP_BINS)) \
+ $(addprefix $(SOLARIS_ADD_DRIVERINST_DIR_ISA)/,$(SOLARIS_ADD_DRIVERS)) \
+ $(addprefix $(SOLARIS_ADD_DRIVERINST_DIR)/,$(SOLARIS_ADD_DRIVERS_CONF)) \
+ $(addprefix $(SOLARIS_VBOXADDINST_DIR_ISA)/,$(SOLARIS_ADD_XORG_DRIVERS))
+endif
+
+SOLARIS_ARCH_ADD_DEPFILES += \
+ $(addprefix $(SOLARIS_VBOXADDINST_DIR)/,$(SOLARIS_ADD_COMMON_BINS)) \
+ $(addprefix $(SOLARIS_VBOXADDINST_DIR)/,$(SOLARIS_ADD_COMMON)) \
+ $(addprefix $(SOLARIS_ADD_SERVICESINST_DIR)/,$(SOLARIS_ADD_SERVICES)) \
+ $(addprefix $(SOLARIS_ADD_USRBIN_DIR)/,$(SOLARIS_ADD_USRBIN_LINKS)) \
+ $(addprefix $(SOLARIS_ADD_USRSBIN_DIR)/,$(SOLARIS_ADD_USRSBIN_LINKS))
+
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
+#
+# -=-=-=-=-=-=-=- Additions package -=-=-=-=-=-=-=-
+#
+
+$(PATH_STAGE_BIN)/additions/$(PKGFILENAME): \
+ $(VBOX_VERSION_STAMP) \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/makepackage.sh \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/postinstall.sh \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/preremove.sh \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxguest.pkginfo \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxguest.depend \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxguest.sh \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxservice.xml \
+ $(PATH_ROOT)/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/VBox.sh \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/98vboxadd-xclient \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/x11config15sol.pl \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/x11restore.pl \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/solaris_xorg.conf \
+ $(VBOX_PATH_X11_ADDITION_INSTALLER)/solaris_xorg_modeless.conf \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vbox_vendor_select \
+ $(VBOX_BRAND_LICENSE_TXT) \
+ $(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf \
+ $(PATH_SUB_CURRENT)/solaris/Makefile.kmk \
+ $(SOLARIS_ARCH_ADD_DEPFILES)
+ $(call MSG_L1,Installing guest additions)
+ $(QUIET)$(SED) \
+ -e "s+@VBOX_PRODUCT@+$(VBOX_PRODUCT)+g" \
+ -e "s+@VBOX_VENDOR@+$(VBOX_VENDOR)+g" \
+ -e "s+@VBOX_VERSION_STRING@+$(VBOX_VERSION_STRING)+g" \
+ -e "s+@VBOX_SVN_REV@+$(VBOX_SVN_REV)+g" \
+ -e "s+@VBOX_VERSION_REVSTAMP@+$(PKGINFO_REVSTAMP)+g" \
+ -e "s+@UNAME_P@+$(PKGINFO_ARCH)+g" \
+ --output $(SOLARIS_ADDINST_OUT_DIR)/vboxguest.pkginfo \
+ $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxguest.pkginfo
+ $(QUIET)$(INSTALL) -m 0755 $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/makepackage.sh $(SOLARIS_ADDINST_OUT_DIR)/makepackage.sh
+ $(QUIET)$(INSTALL) -m 0755 $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/postinstall.sh $(SOLARIS_ADDINST_OUT_DIR)/postinstall.sh
+ $(QUIET)$(INSTALL) -m 0755 $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/preremove.sh $(SOLARIS_ADDINST_OUT_DIR)/preremove.sh
+ $(QUIET)$(INSTALL) -m 0644 $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxguest.space $(SOLARIS_ADDINST_OUT_DIR)/vboxguest.space
+ $(QUIET)$(INSTALL) -m 0644 $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxguest.depend $(SOLARIS_ADDINST_OUT_DIR)/vboxguest.depend
+ # don't display the license on package install, since 4.0
+ #$(QUIET)$(INSTALL) -m 0644 $(VBOX_BRAND_LICENSE_TXT) $(SOLARIS_ADDINST_OUT_DIR)/vboxguest.copyright
+ $(call MSG_L1,Creating install package: $@)
+ $(QUIET)$(MKDIR) -p $(SOLARIS_ADD_ETCFS_DIR)
+ $(QUIET)$(MKDIR) -p $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)
+ $(QUIET)$(LN_SYMLINK) -f ../..$(SOLARIS_VBOXADDINST_SUBDIR)/1099.vboxclient $(SOLARIS_ADD_USRBIN_DIR)/VBoxClient-all
+ifdef VBOX_WITH_COMBINED_SOLARIS_GUEST_PACKAGE
+ ifdef VBOX_COMPRESS
+ $(QUIET)$(VBOX_COMPRESS) $(addprefix $(SOLARIS_VBOXADDINST_DIR_32)/,$(SOLARIS_ADD_COMPRESS_FILES))
+ $(QUIET)$(VBOX_COMPRESS) $(addprefix $(SOLARIS_VBOXADDINST_DIR_64)/,$(SOLARIS_ADD_COMPRESS_FILES))
+ endif
+ $(QUIET)$(LN_SYMLINK) -f ../../..$(SOLARIS_VBOXADDINST_SUBDIR)/$(SOLARIS_ADDDIR_NAME_32)/vboxfsmount $(SOLARIS_ADD_ETCFS_DIR)/mount
+else # !VBOX_WITH_COMBINED_SOLARIS_GUEST_PACKAGE
+ ifdef VBOX_COMPRESS
+ $(QUIET)$(VBOX_COMPRESS) $(addprefix $(SOLARIS_VBOXADDINST_DIR_ISA)/,$(SOLARIS_ADD_COMPRESS_FILES))
+ endif
+ $(LN_SYMLINK) -f ../../..$(SOLARIS_VBOXADDINST_SUBDIR)/$(SOLARIS_ADDDIR_NAME_ISA)/vboxfsmount $(SOLARIS_ADD_ETCFS_DIR)/mount
+endif
+ $(QUIET)$(INSTALL) -m 0644 $(VBOX_PATH_SOLARIS_ADDITION_INSTALLER)/vboxservice.xml $(SOLARIS_ADD_SERVICESINST_DIR)/vboxservice.xml
+ $(QUIET)$(INSTALL) -m 0644 $(PATH_ROOT)/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml $(SOLARIS_ADD_SERVICESINST_DIR)/vboxmslnk.xml
+ $(QUIET)$(SOLARIS_ADDINST_OUT_DIR)/makepackage.sh $(SOLARIS_ADDINST_OUT_DIR) $(PKGFILENAME) $(VBOX_SVN_REV) $(VBOX_SOL_PKG_DEV)
+ $(QUIET)$(INSTALL) -m 0644 $(SOLARIS_ADDINST_OUT_DIR)/$(PKGFILENAME) $(PATH_STAGE_BIN)/additions/$(PKGFILENAME)
+ $(QUIET)$(RM) -f $(SOLARIS_ADDINST_OUT_DIR)/$(PKGFILENAME)
+
+
+
+#
+# -=-=-=-=-=-=-=- Package rules -=-=-=-=-=-=-=-
+#
+
+#
+# 32-bit files
+#
+$(addprefix $(SOLARIS_VBOXADDINST_DIR_32)/,$(SOLARIS_ADD_STRIP_BINS)): \
+ $(SOLARIS_VBOXADDINST_DIR_32)/% : $(SOLARIS_ADD_OUT_BIN_32)/% | $$(dir $$@)
+ $(INSTALL) -m 0755 $(if $(VBOX_DO_STRIP),-s,) $< $@
+
+$(addprefix $(SOLARIS_ADD_DRIVERINST_DIR_32)/,$(SOLARIS_ADD_DRIVERS)): \
+ $(SOLARIS_ADD_DRIVERINST_DIR_32)/% : $(SOLARIS_ADD_OUT_BIN_32)/% | $$(dir $$@)
+ $(INSTALL) -m 0644 $< $@
+
+$(addprefix $(SOLARIS_VBOXADDINST_DIR_32)/,$(SOLARIS_ADD_XORG_DRIVERS)): \
+ $(SOLARIS_VBOXADDINST_DIR_32)/% : $(SOLARIS_ADD_OUT_BIN_32)/% | $$(dir $$@)
+ $(BIN_COPY) $< $@
+
+
+#
+# 64-bit files
+#
+$(addprefix $(SOLARIS_VBOXADDINST_DIR_64)/,$(SOLARIS_ADD_STRIP_BINS)): \
+ $(SOLARIS_VBOXADDINST_DIR_64)/% : $(SOLARIS_ADD_OUT_BIN_64)/% | $$(dir $$@)
+ $(INSTALL) -m 0755 $(if $(VBOX_DO_STRIP),-s,) $< $@
+
+$(addprefix $(SOLARIS_ADD_DRIVERINST_DIR_64)/,$(SOLARIS_ADD_DRIVERS)): \
+ $(SOLARIS_ADD_DRIVERINST_DIR_64)/% : $(SOLARIS_ADD_OUT_BIN_64)/% | $$(dir $$@)
+ $(INSTALL) -m 0644 $< $@
+
+$(addprefix $(SOLARIS_VBOXADDINST_DIR_64)/,$(SOLARIS_ADD_XORG_DRIVERS)): \
+ $(SOLARIS_VBOXADDINST_DIR_64)/% : $(SOLARIS_ADD_OUT_BIN_64)/% | $$(dir $$@)
+ $(BIN_COPY) $< $@
+
+
+#
+# Common files
+#
+$(addprefix $(SOLARIS_VBOXADDINST_DIR)/,$(SOLARIS_ADD_COMMON)): \
+ $(SOLARIS_VBOXADDINST_DIR)/% : $(SOLARIS_ADD_OUT_BIN_ISA)/% | $$(dir $$@)
+ $(INSTALL) -m 0644 $< $@
+
+#
+# Common binaries/shell scripts
+#
+$(addprefix $(SOLARIS_VBOXADDINST_DIR)/,$(SOLARIS_ADD_COMMON_BINS)): \
+ $(SOLARIS_VBOXADDINST_DIR)/% : $(SOLARIS_ADD_OUT_BIN_ISA)/% | $$(dir $$@)
+ $(INSTALL) -m 0755 $< $@
+
+#
+# Driver .conf files
+#
+$(addprefix $(SOLARIS_ADD_DRIVERINST_DIR)/,$(SOLARIS_ADD_DRIVERS_CONF)): \
+ $(SOLARIS_ADD_DRIVERINST_DIR)/% : $(SOLARIS_ADD_OUT_BIN_ISA)/% | $$(dir $$@)
+ $(INSTALL) -m 0644 $< $@
+
+#
+# SMF Service files
+#
+$(addprefix $(SOLARIS_ADD_SERVICESINST_DIR)/,$(SOLARIS_ADD_SERVICES)): \
+ $(SOLARIS_ADD_SERVICESINST_DIR)/% : $(SOLARIS_ADD_OUT_BIN_ISA)/% | $$(dir $$@)
+ $(INSTALL) -m 0644 $< $@
+
+#
+# Symlinks from /usr/bin/ to /opt/VirtualBoxAdditions
+#
+$(addprefix $(SOLARIS_ADD_USRBIN_DIR)/,$(SOLARIS_ADD_USRBIN_LINKS)): \
+ $(SOLARIS_ADD_USRBIN_DIR)/% : % | $$(dir $$@)
+ $(LN_SYMLINK) -f ../..$(SOLARIS_VBOXADDINST_SUBDIR)/$< $@
+
+#
+# Symlinks from /usr/sbin/ to /opt/VirtualBoxAdditions
+#
+$(addprefix $(SOLARIS_ADD_USRSBIN_DIR)/,$(SOLARIS_ADD_USRSBIN_LINKS)): \
+ $(SOLARIS_ADD_USRSBIN_DIR)/% : % | $$(dir $$@)
+ $(LN_SYMLINK) -f ../..$(SOLARIS_VBOXADDINST_SUBDIR)/$< $@
diff --git a/src/VBox/Additions/solaris/Mouse/Makefile.kmk b/src/VBox/Additions/solaris/Mouse/Makefile.kmk
new file mode 100644
index 00000000..b3fec956
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/Makefile.kmk
@@ -0,0 +1,81 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Solaris Mouse Integration kernel module.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#ifneq ($(KBUILD_HOST),solaris)
+#$(error "The Solaris guest additions can only be built on Solaris!")
+#endif
+
+#
+# vboxms - The Mouse Integration Driver
+#
+SYSMODS.solaris += vboxms
+vboxms_TEMPLATE = VBoxGuestR0Drv
+vboxms_DEFS = VBOX_WITH_HGCM VBOX_SVN_REV=$(VBOX_SVN_REV)
+vboxms_DEPS += $(VBOX_SVN_REV_KMK)
+vboxms_SOURCES = \
+ vboxms.c
+vboxms_LIBS = \
+ $(VBOX_LIB_VBGL_R0)
+ifeq ($(KBUILD_HOST),solaris)
+ vboxms_LDFLAGS += -N drv/vboxguest -N misc/ctf
+else
+ vboxms_SOURCES += deps.asm
+ vboxms_deps.asm_ASFLAGS = -f bin -g null
+endif
+
+
+PROGRAMS += vboxmslnk
+vboxmslnk_TEMPLATE = VBoxGuestR3Exe
+vboxmslnk_SOURCES = vboxmslnk.c
+
+
+if 0 # Broken - unresolved externals: vbglDriver*, RTR0AssertPanicSystem.
+ if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+ PROGRAMS += tstVBoxMouse-solaris
+ tstVBoxMouse-solaris_TEMPLATE = VBoxR3TstExe
+ tstVBoxMouse-solaris_SOURCES = \
+ vboxms.c \
+ testcase/tstVBoxMouse-solaris.c
+ tstVBoxMouse-solaris_DEFS = TESTCASE
+ tstVBoxMouse-solaris_LIBS = $(LIB_RUNTIME)
+ endif
+endif
+
+include $(KBUILD_PATH)/subfooter.kmk
+
diff --git a/src/VBox/Additions/solaris/Mouse/deps.asm b/src/VBox/Additions/solaris/Mouse/deps.asm
new file mode 100644
index 00000000..d01557ed
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/deps.asm
@@ -0,0 +1,49 @@
+; $Id: deps.asm $
+;; @file
+; Solaris kernel module dependency
+;
+
+;
+; Copyright (C) 2012-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/solaris/kmoddeps.mac"
+
+kmoddeps_header ; ELF header, section table and shared string table
+
+kmoddeps_dynstr_start ; ELF .dynstr section
+kmoddeps_dynstr_string str_misc_ctf, "misc/ctf"
+kmoddeps_dynstr_string str_drv_vboxguest, "drv/vboxguest"
+kmoddeps_dynstr_end
+
+kmoddeps_dynamic_start ; ELF .dynamic section
+kmoddeps_dynamic_needed str_misc_ctf
+kmoddeps_dynamic_needed str_drv_vboxguest
+kmoddeps_dynamic_end
diff --git a/src/VBox/Additions/solaris/Mouse/testcase/Makefile.kup b/src/VBox/Additions/solaris/Mouse/testcase/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/testcase/Makefile.kup
diff --git a/src/VBox/Additions/solaris/Mouse/testcase/solaris.h b/src/VBox/Additions/solaris/Mouse/testcase/solaris.h
new file mode 100644
index 00000000..2040bba6
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/testcase/solaris.h
@@ -0,0 +1,454 @@
+/* $Id: solaris.h $ */
+/** @file
+ * VBoxGuest - Guest Additions Driver for Solaris - testcase stubs.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_solaris_Mouse_testcase_solaris_h
+#define GA_INCLUDED_SRC_solaris_Mouse_testcase_solaris_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/string.h> /* RT_ZERO */
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/winsock2.h> /* struct timeval */
+#endif
+#include <errno.h>
+#include <time.h> /* struct timeval */
+
+/* Overrides */
+#define dev_t unsigned
+
+/* Constants */
+#define DDI_FAILURE (-1)
+#define DDI_SUCCESS (0)
+
+#define MODMAXNAMELEN 32
+#define MODMAXLINKINFOLEN 32
+#define MODMAXLINK 10
+
+#define MOD_NOAUTOUNLOAD 0x1
+
+#define M_DATA 0x00
+#define M_BREAK 0x08
+#define M_PASSFP 0x09
+#define M_EVENT 0x0a
+#define M_SIG 0x0b
+#define M_DELAY 0x0c
+#define M_CTL 0x0d
+#define M_IOCTL 0x0e
+#define M_SETOPTS 0x10
+#define M_RSE 0x11
+
+#define M_IOCACK 0x81
+#define M_IOCNAK 0x82
+#define M_PCPROTO 0x83
+#define M_PCSIG 0x84
+#define M_READ 0x85
+#define M_FLUSH 0x86
+#define M_STOP 0x87
+#define M_START 0x88
+#define M_HANGUP 0x89
+#define M_ERROR 0x8a
+#define M_COPYIN 0x8b
+#define M_COPYOUT 0x8c
+#define M_IOCDATA 0x8d
+#define M_PCRSE 0x8e
+#define M_STOPI 0x8f
+#define M_STARTI 0x90
+#define M_PCEVENT 0x91
+#define M_UNHANGUP 0x92
+#define M_CMD 0x93
+
+#define BPRI_LO 1
+#define BPRI_MED 2
+#define BPRI_HI 3
+
+#define FLUSHALL 1
+#define FLUSHDATA 0
+
+#define TRANSPARENT (unsigned int)(-1)
+
+#define FLUSHR 0x01
+#define FLUSHW 0x02
+
+#define MSIOC ('m'<<8)
+#define MSIOGETPARMS (MSIOC|1)
+#define MSIOSETPARMS (MSIOC|2)
+#define MSIOBUTTONS (MSIOC|3)
+#define MSIOSRESOLUTION (MSIOC|4)
+
+#define VUIOC ('v'<<8)
+#define VUIDSFORMAT (VUIOC|1)
+#define VUIDGFORMAT (VUIOC|2)
+#define VUID_NATIVE 0
+#define VUID_FIRM_EVENT 1
+
+#define VUIDSADDR (VUIOC|3)
+#define VUIDGADDR (VUIOC|4)
+
+#define VUID_WHEEL_MAX_COUNT 256
+#define VUIDGWHEELCOUNT (VUIOC|15)
+#define VUIDGWHEELINFO (VUIOC|16)
+#define VUIDGWHEELSTATE (VUIOC|17)
+#define VUIDSWHEELSTATE (VUIOC|18)
+
+#define DDI_DEVICE_ATTR_V0 0x0001
+#define DDI_DEVICE_ATTR_V1 0x0002
+
+#define DDI_NEVERSWAP_ACC 0x00
+#define DDI_STRUCTURE_LE_ACC 0x01
+#define DDI_STRUCTURE_BE_ACC 0x02
+
+#define DDI_STRICTORDER_ACC 0x00
+#define DDI_UNORDERED_OK_ACC 0x01
+#define DDI_MERGING_OK_ACC 0x02
+#define DDI_LOADCACHING_OK_ACC 0x03
+#define DDI_STORECACHING_OK_ACC 0x04
+
+/** @todo fix this */
+#define DDI_DEFAULT_ACC DDI_STRICTORDER_ACC
+
+#define DDI_INTR_CLAIMED 1
+#define DDI_INTR_UNCLAIMED 0
+
+#define DDI_INTR_TYPE_FIXED 0x1
+#define DDI_INTR_TYPE_MSI 0x2
+#define DDI_INTR_TYPE_MSIX 0x4
+
+#define LOC_FIRST_DELTA 32640
+#define LOC_X_DELTA 32640
+#define LOC_Y_DELTA 32641
+#define LOC_LAST_DELTA 32641
+#define LOC_FIRST_ABSOLUTE 32642
+#define LOC_X_ABSOLUTE 32642
+#define LOC_Y_ABSOLUTE 32643
+#define LOC_LAST_ABSOLUTE 32643
+
+#define FE_PAIR_NONE 0
+#define FE_PAIR_SET 1
+#define FE_PAIR_DELTA 2
+#define FE_PAIR_ABSOLUTE 3
+
+typedef struct __ldi_handle *ldi_handle_t;
+
+typedef enum
+{
+ DDI_INFO_DEVT2DEVINFO = 0,
+ DDI_INFO_DEVT2INSTANCE = 1
+} ddi_info_cmd_t;
+
+typedef enum
+{
+ DDI_ATTACH = 0,
+ DDI_RESUME = 1,
+ DDI_PM_RESUME = 2
+} ddi_attach_cmd_t;
+
+typedef enum
+{
+ DDI_DETACH = 0,
+ DDI_SUSPEND = 1,
+ DDI_PM_SUSPEND = 2,
+ DDI_HOTPLUG_DETACH = 3
+} ddi_detach_cmd_t;
+
+/* Simple types */
+
+typedef struct cred *cred_t;
+typedef struct dev_info *dev_info_t;
+typedef struct __ddi_acc_handle * ddi_acc_handle_t;
+typedef struct __ddi_intr_handle *ddi_intr_handle_t;
+typedef struct mutex *kmutex_t;
+typedef unsigned int uint_t;
+typedef unsigned short ushort_t;
+typedef unsigned char uchar_t;
+
+/* Structures */
+
+struct modspecific_info {
+ char msi_linkinfo[MODMAXLINKINFOLEN];
+ int msi_p0;
+};
+
+struct modinfo {
+ int mi_info;
+ int mi_state;
+ int mi_id;
+ int mi_nextid;
+ char *mi_base; /* Was caddr_t. */
+ size_t mi_size;
+ int mi_rev;
+ int mi_loadcnt;
+ char mi_name[MODMAXNAMELEN];
+ struct modspecific_info mi_msinfo[MODMAXLINK];
+};
+
+typedef struct queue
+{
+ struct qinit *q_qinfo;
+ struct msgb *q_first;
+ struct msgb *q_last;
+ struct queue *q_next;
+ void *q_ptr;
+ size_t q_count;
+ uint_t q_flag;
+ ssize_t q_minpsz;
+ ssize_t q_maxpsz;
+ size_t q_hiwat;
+ size_t q_lowat;
+} queue_t;
+
+typedef struct msgb
+{
+ struct msgb *b_next;
+ struct msgb *b_prev;
+ struct msgb *b_cont;
+ unsigned char *b_rptr;
+ unsigned char *b_wptr;
+ struct datab *b_datap;
+ unsigned char b_band;
+ unsigned short b_flag;
+} mblk_t;
+
+typedef struct datab
+{
+ unsigned char *db_base;
+ unsigned char *db_lim;
+ unsigned char db_ref;
+ unsigned char db_type;
+} dblk_t;
+
+struct iocblk
+{
+ int ioc_cmd;
+ cred_t *ioc_cr;
+ uint_t ioc_id;
+ uint_t ioc_flag;
+ size_t ioc_count;
+ int ioc_rval;
+ int ioc_error;
+#if defined(RT_ARCH_AMD64) /* Actually this should be LP64. */
+ int dummy; /* For simplicity, to ensure the structure size matches
+ struct copyreq. */
+#endif
+};
+
+struct copyreq
+{
+ int cq_cmd;
+ cred_t *cq_cr;
+ uint_t cq_id;
+ uint_t cq_flag;
+ mblk_t *cq_private;
+ char *cq_addr; /* Was caddr_t. */
+ size_t cq_size;
+};
+
+struct copyresp
+{
+ int cp_cmd;
+ cred_t *cp_cr;
+ uint_t cp_id;
+ uint_t cp_flag;
+ mblk_t *cp_private;
+ char *cp_rval; /* Was caddr_t. */
+};
+
+typedef struct modctl
+{
+ /* ... */
+ char mod_loadflags;
+ /* ... */
+} modctl_t;
+
+typedef struct {
+ int jitter_thresh;
+ int speed_law;
+ int speed_limit;
+} Ms_parms;
+
+typedef struct {
+ int height;
+ int width;
+} Ms_screen_resolution;
+
+typedef struct vuid_addr_probe {
+ short base;
+ union
+ {
+ short next;
+ short current;
+ } data;
+} Vuid_addr_probe;
+
+typedef struct ddi_device_acc_attr
+{
+ ushort_t devacc_attr_version;
+ uchar_t devacc_attr_endian_flags;
+ uchar_t devacc_attr_dataorder;
+ uchar_t devacc_attr_access;
+} ddi_device_acc_attr_t;
+
+typedef struct firm_event
+{
+ ushort_t id;
+ uchar_t pair_type;
+ uchar_t pair;
+ int value;
+ struct timeval time;
+} Firm_event;
+
+/* Prototypes */
+
+#define _init vboxguestSolarisInit
+extern int vboxguestSolarisInit(void);
+#define _fini vboxguestSolarisFini
+extern int vboxguestSolarisFini(void);
+#define _info vboxguestSolarisInfo
+extern int vboxguestSolarisInfo(struct modinfo *pModInfo);
+
+/* Simple API stubs */
+
+#define cmn_err(...) do {} while(0)
+#define mod_remove(...) 0
+#define mod_info(...) 0
+#define RTR0Init(...) VINF_SUCCESS
+#define RTR0Term(...) do {} while(0)
+#define RTR0AssertPanicSystem(...) do {} while(0)
+#define RTLogCreate(...) VINF_SUCCESS
+#define RTLogRelSetDefaultInstance(...) do {} while(0)
+#define RTLogDestroy(...) do {} while(0)
+#if 0
+#define VBoxGuestCreateKernelSession(...) VINF_SUCCESS
+#define VBoxGuestCreateUserSession(...) VINF_SUCCESS
+#define VBoxGuestCloseSession(...) do {} while(0)
+#define VBoxGuestInitDevExt(...) VINF_SUCCESS
+#define VBoxGuestDeleteDevExt(...) do {} while(0)
+#define VBoxGuestCommonIOCtl(...) VINF_SUCCESS
+#define VBoxGuestCommonISR(...) true
+#define VbglR0GRAlloc(...) VINF_SUCCESS
+#define VbglR0GRPerform(...) VINF_SUCCESS
+#define VbglR0GRFree(...) do {} while(0)
+#endif
+#define VbglR0InitClient(...) VINF_SUCCESS
+#define vbglDriverOpen(...) VINF_SUCCESS
+#define vbglDriverClose(...) do {} while(0)
+#define vbglDriverIOCtl(...) VINF_SUCCESS
+#define qprocson(...) do {} while(0)
+#define qprocsoff(...) do {} while(0)
+#define flushq(...) do {} while(0)
+#define putnext(...) do {} while(0)
+#define ddi_get_instance(...) 0
+#define pci_config_setup(...) DDI_SUCCESS
+#define pci_config_teardown(...) do {} while(0)
+#define ddi_regs_map_setup(...) DDI_SUCCESS
+#define ddi_regs_map_free(...) do {} while(0)
+#define ddi_dev_regsize(...) DDI_SUCCESS
+#define ddi_create_minor_node(...) DDI_SUCCESS
+#define ddi_remove_minor_node(...) do {} while(0)
+#define ddi_intr_get_supported_types(...) DDI_SUCCESS
+#define ddi_intr_get_nintrs(...) DDI_SUCCESS
+#define ddi_intr_get_navail(...) DDI_SUCCESS
+#define ddi_intr_alloc(...) DDI_SUCCESS
+#define ddi_intr_free(...) do {} while(0)
+#define ddi_intr_get_pri(...) DDI_SUCCESS
+#define ddi_intr_enable(...) DDI_SUCCESS
+#define ddi_intr_disable(...) DDI_SUCCESS
+#define ddi_intr_add_handler(...) DDI_SUCCESS
+#define ddi_intr_remove_handler(...) DDI_SUCCESS
+#define mutex_init(...) do {} while(0)
+#define mutex_destroy(...) do {} while(0)
+#define mutex_enter(...) do {} while(0)
+#define mutex_exit(...) do {} while(0)
+#define uniqtime32(...) do {} while(0)
+#define canput(...) true
+#define putbq(...) do {} while(0)
+
+/* Externally defined helpers. */
+
+/** Flags set in the struct mblk b_flag member for verification purposes.
+ * @{ */
+/** miocpullup was called for this message. */
+#define F_TEST_PULLUP 1
+/** @} */
+
+extern void miocack(queue_t *pWriteQueue, mblk_t *pMBlk, int cbData, int rc);
+extern void miocnak(queue_t *pWriteQueue, mblk_t *pMBlk, int cbData, int iErr);
+extern int miocpullup(mblk_t *pMBlk, size_t cbMsg);
+extern void mcopyin(mblk_t *pMBlk, void *pvState, size_t cbData, void *pvUser);
+extern void mcopyout(mblk_t *pMBlk, void *pvState, size_t cbData, void *pvUser,
+ mblk_t *pMBlkData);
+extern void qreply(queue_t *pQueue, mblk_t *pMBlk);
+extern mblk_t *allocb(size_t cb, uint_t cPrio);
+extern void freemsg(mblk_t *pMsg);
+
+/* API stubs with simple logic */
+
+static modctl_t s_ModCtl;
+static void **s_pvLinkage;
+
+static inline modctl_t *mod_getctl(void **linkage)
+{
+ s_pvLinkage = linkage;
+ return s_pvLinkage ? &s_ModCtl : NULL;
+}
+
+#define mod_install(linkage) (s_pvLinkage && ((linkage) == s_pvLinkage) ? 0 : EINVAL)
+#define QREADR 0x00000010
+#define OTHERQ(q) ((q)->q_flag & QREADR ? (q) + 1 : (q) - 1)
+#define WR(q) ((q)->q_flag & QREADR ? (q) + 1 : (q))
+#define RD(q) ((q)->q_flag & QREADR ? (q) : (q) - 1)
+
+
+/* Basic initialisation of a queue structure pair for testing. */
+static inline void doInitQueues(queue_t aQueues[2])
+{
+ aQueues[0].q_flag = QREADR;
+}
+
+static inline dev_t makedevice(unsigned cMajor, unsigned cMinor)
+{
+ return cMajor * 4096 + cMinor;
+}
+
+static inline unsigned getmajor(dev_t device)
+{
+ return device / 4096;
+}
+
+/* API stubs with controllable logic */
+
+#endif /* !GA_INCLUDED_SRC_solaris_Mouse_testcase_solaris_h */
diff --git a/src/VBox/Additions/solaris/Mouse/testcase/tstVBoxMouse-solaris.c b/src/VBox/Additions/solaris/Mouse/testcase/tstVBoxMouse-solaris.c
new file mode 100644
index 00000000..f95fbd99
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/testcase/tstVBoxMouse-solaris.c
@@ -0,0 +1,170 @@
+/** @file
+ * VirtualBox Guest Additions Driver for Solaris - Solaris helper functions.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#include "solaris.h"
+#include <iprt/alloc.h>
+
+
+/*********************************************************************************************************************************
+* Helper functions *
+*********************************************************************************************************************************/
+
+void miocack(queue_t *pWriteQueue, mblk_t *pMBlk, int cbData, int rc)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+ pMBlk->b_datap->db_type = M_IOCACK;
+ pIOCBlk->ioc_count = cbData;
+ pIOCBlk->ioc_rval = rc;
+ pIOCBlk->ioc_error = 0;
+ qreply(pWriteQueue, pMBlk);
+}
+
+void miocnak(queue_t *pWriteQueue, mblk_t *pMBlk, int cbData, int iErr)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+ pMBlk->b_datap->db_type = M_IOCNAK;
+ pIOCBlk->ioc_count = cbData;
+ pIOCBlk->ioc_error = iErr ? iErr : EINVAL;
+ pIOCBlk->ioc_rval = 0;
+ qreply(pWriteQueue, pMBlk);
+}
+
+/* This does not work like the real version, but does some sanity testing
+ * and sets a flag. */
+int miocpullup(mblk_t *pMBlk, size_t cbMsg)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+ if (pIOCBlk->ioc_count == TRANSPARENT)
+ return EINVAL;
+ if ( !pMBlk->b_cont
+ || pMBlk->b_cont->b_wptr < pMBlk->b_cont->b_rptr + cbMsg)
+ return EINVAL;
+ pMBlk->b_flag |= F_TEST_PULLUP;
+ return 0;
+}
+
+void mcopyin(mblk_t *pMBlk, void *pvState, size_t cbData, void *pvUser)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+ struct copyreq *pCopyReq = (struct copyreq *)pMBlk->b_rptr;
+
+ AssertReturnVoid( pvUser
+ || ( pMBlk->b_datap->db_type == M_IOCTL
+ && pIOCBlk->ioc_count == TRANSPARENT
+ && pMBlk->b_cont->b_rptr));
+ pMBlk->b_datap->db_type = M_COPYIN;
+ pMBlk->b_wptr = pMBlk->b_rptr + sizeof(*pCopyReq);
+ pCopyReq->cq_private = pvState;
+ pCopyReq->cq_size = cbData;
+ pCopyReq->cq_addr = pvUser ? pvUser : *(void **)pMBlk->b_cont->b_rptr;
+ if (pMBlk->b_cont)
+ {
+ freemsg(pMBlk->b_cont);
+ pMBlk->b_cont = NULL;
+ }
+}
+
+void mcopyout(mblk_t *pMBlk, void *pvState, size_t cbData, void *pvUser,
+ mblk_t *pMBlkData)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+ struct copyreq *pCopyReq = (struct copyreq *)pMBlk->b_rptr;
+
+ AssertReturnVoid( pvUser
+ || ( pMBlk->b_datap->db_type == M_IOCTL
+ && pIOCBlk->ioc_count == TRANSPARENT
+ && pMBlk->b_cont->b_rptr));
+ pMBlk->b_datap->db_type = M_COPYOUT;
+ pMBlk->b_wptr = pMBlk->b_rptr + sizeof(*pCopyReq);
+ pCopyReq->cq_private = pvState;
+ pCopyReq->cq_size = cbData;
+ pCopyReq->cq_addr = pvUser ? pvUser : *(void **)pMBlk->b_cont->b_rptr;
+ if (pMBlkData)
+ {
+ if (pMBlk->b_cont)
+ freemsg(pMBlk->b_cont);
+ pMBlk->b_cont = pMBlkData;
+ pMBlkData->b_wptr = pMBlkData->b_rptr + cbData;
+ }
+}
+
+/* This does not work like the real version but is easy to test the result of.
+ */
+void qreply(queue_t *pQueue, mblk_t *pMBlk)
+{
+ OTHERQ(pQueue)->q_first = pMBlk;
+}
+
+/** @todo reference counting */
+mblk_t *allocb(size_t cb, uint_t cPrio)
+{
+ unsigned char *pch = RTMemAllocZ(cb);
+ struct msgb *pMBlk = (struct msgb *)RTMemAllocZ(sizeof(struct msgb));
+ struct datab *pDBlk = (struct datab *)RTMemAllocZ(sizeof(struct datab));
+ if (!pch || !pMBlk || !pDBlk)
+ {
+ RTMemFree(pch);
+ RTMemFree(pMBlk);
+ RTMemFree(pDBlk);
+ return NULL;
+ }
+ NOREF(cPrio);
+ pMBlk->b_rptr = pch;
+ pMBlk->b_wptr = pMBlk->b_rptr + cb;
+ pMBlk->b_datap = pDBlk;
+ pDBlk->db_base = pMBlk->b_rptr;
+ pDBlk->db_lim = pMBlk->b_wptr;
+ pDBlk->db_type = M_DATA;
+ return pMBlk;
+}
+
+/** @todo reference counting */
+void freemsg(mblk_t *pMBlk)
+{
+ if (!pMBlk)
+ return;
+ RTMemFree(pMBlk->b_rptr);
+ RTMemFree(pMBlk->b_datap);
+ freemsg(pMBlk->b_cont);
+ RTMemFree(pMBlk);
+}
diff --git a/src/VBox/Additions/solaris/Mouse/vboxms.c b/src/VBox/Additions/solaris/Mouse/vboxms.c
new file mode 100644
index 00000000..173db83e
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/vboxms.c
@@ -0,0 +1,1450 @@
+/* $Id: vboxms.c $ */
+/** @file
+ * VirtualBox Guest Additions Mouse Driver for Solaris.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_MOUSE
+#include <VBox/VMMDev.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+
+#ifndef TESTCASE
+# include <sys/modctl.h>
+# include <sys/msio.h>
+# include <sys/stat.h>
+# include <sys/ddi.h>
+# include <sys/strsun.h>
+# include <sys/stropts.h>
+# include <sys/sunddi.h>
+# include <sys/vuid_event.h>
+# include <sys/vuid_wheel.h>
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+#else /* TESTCASE */
+# undef IN_RING3
+# define IN_RING0
+#endif /* TESTCASE */
+
+#ifdef TESTCASE /* Include this last as we . */
+# include "testcase/solaris.h"
+# include <iprt/test.h>
+#endif /* TESTCASE */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** The module name. */
+#define DEVICE_NAME "vboxms"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC "VBoxMouseIntegr"
+
+
+/*********************************************************************************************************************************
+* Internal functions used in global structures *
+*********************************************************************************************************************************/
+
+static int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int vbmsSolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+static int vbmsSolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
+ void **ppvResult);
+static int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag,
+ int fMode, cred_t *pCred);
+static int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred);
+static int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk);
+
+
+/*********************************************************************************************************************************
+* Driver global structures *
+*********************************************************************************************************************************/
+
+#ifndef TESTCASE /* I see no value in including these in the test. */
+
+/*
+ * mod_info: STREAMS module information.
+ */
+static struct module_info g_vbmsSolModInfo =
+{
+ 0, /* module id number */
+ "vboxms",
+ 0, /* minimum packet size */
+ INFPSZ, /* maximum packet size accepted */
+ 512, /* high water mark for data flow control */
+ 128 /* low water mark */
+};
+
+/*
+ * rinit: read queue structure for handling messages coming from below. In
+ * our case this means the host and the virtual hardware, so we do not need
+ * the put and service procedures.
+ */
+static struct qinit g_vbmsSolRInit =
+{
+ NULL, /* put */
+ NULL, /* service thread procedure */
+ vbmsSolOpen,
+ vbmsSolClose,
+ NULL, /* reserved */
+ &g_vbmsSolModInfo,
+ NULL /* module statistics structure */
+};
+
+/*
+ * winit: write queue structure for handling messages coming from above. Above
+ * means user space applications: either Guest Additions user space tools or
+ * applications reading pointer input. Messages from the last most likely pass
+ * through at least the "consms" console mouse streams module which multiplexes
+ * hardware pointer drivers to a single virtual pointer.
+ */
+static struct qinit g_vbmsSolWInit =
+{
+ vbmsSolWPut,
+ NULL, /* service thread procedure */
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* reserved */
+ &g_vbmsSolModInfo,
+ NULL /* module statistics structure */
+};
+
+/**
+ * streamtab: for drivers that support char/block entry points.
+ */
+static struct streamtab g_vbmsSolStreamTab =
+{
+ &g_vbmsSolRInit,
+ &g_vbmsSolWInit,
+ NULL, /* MUX rinit */
+ NULL /* MUX winit */
+};
+
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_vbmsSolCbOps =
+{
+ nodev, /* open */
+ nodev, /* close */
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ nodev, /* c read */
+ nodev, /* c write */
+ nodev, /* c ioctl */
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ &g_vbmsSolStreamTab,
+ D_MP,
+ CB_REV /* revision */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_vbmsSolDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ vbmsSolGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ vbmsSolAttach,
+ vbmsSolDetach,
+ nodev, /* reset */
+ &g_vbmsSolCbOps,
+ NULL, /* bus operations */
+ nodev /* power */
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_vbmsSolModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_vbmsSolDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel.
+ */
+static struct modlinkage g_vbmsSolModLinkage =
+{
+ MODREV_1, /* loadable module system revision */
+ &g_vbmsSolModule,
+ NULL /* terminate array of linkage structures */
+};
+
+#else /* TESTCASE */
+static void *g_vbmsSolModLinkage;
+#endif /* TESTCASE */
+
+/**
+ * State info for each open file handle.
+ */
+typedef struct
+{
+ /** Device handle. */
+ dev_info_t *pDip;
+ /** Mutex protecting the guest library against multiple initialistation or
+ * uninitialisation. */
+ kmutex_t InitMtx;
+ /** Initialisation counter for the guest library. */
+ size_t cInits;
+ /** The STREAMS write queue which we need for sending messages up to
+ * user-space. */
+ queue_t *pWriteQueue;
+ /** Pre-allocated mouse status VMMDev request for use in the IRQ
+ * handler. */
+ VMMDevReqMouseStatus *pMouseStatusReq;
+ /* The current greatest horizontal pixel offset on the screen, used for
+ * absolute mouse position reporting.
+ */
+ int cMaxScreenX;
+ /* The current greatest vertical pixel offset on the screen, used for
+ * absolute mouse position reporting.
+ */
+ int cMaxScreenY;
+} VBMSSTATE, *PVBMSSTATE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** Global driver state. Actually this could be allocated dynamically. */
+static VBMSSTATE g_OpenNodeState /* = { 0 } */;
+
+
+/*********************************************************************************************************************************
+* Kernel entry points *
+*********************************************************************************************************************************/
+
+/** Driver initialisation. */
+int _init(void)
+{
+ int rc;
+ LogRelFlow((DEVICE_NAME ": built on " __DATE__ " at " __TIME__ "\n"));
+ mutex_init(&g_OpenNodeState.InitMtx, NULL, MUTEX_DRIVER, NULL);
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_vbmsSolModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ LogRel((DEVICE_NAME ": failed to disable autounloading!\n"));
+ rc = mod_install(&g_vbmsSolModLinkage);
+
+ LogRelFlow((DEVICE_NAME ": initialisation returning %d.\n", rc));
+ return rc;
+}
+
+
+#ifdef TESTCASE
+/** Simple test of the flow through _init. */
+static void test_init(RTTEST hTest)
+{
+ RTTestSub(hTest, "Testing _init");
+ RTTEST_CHECK(hTest, _init() == 0);
+}
+#endif
+
+
+/** Driver cleanup. */
+int _fini(void)
+{
+ int rc;
+
+ LogRelFlow((DEVICE_NAME ":_fini\n"));
+ rc = mod_remove(&g_vbmsSolModLinkage);
+ if (!rc)
+ mutex_destroy(&g_OpenNodeState.InitMtx);
+
+ return rc;
+}
+
+
+/** Driver identification. */
+int _info(struct modinfo *pModInfo)
+{
+ int rc;
+ LogRelFlow((DEVICE_NAME ":_info\n"));
+ rc = mod_info(&g_vbmsSolModLinkage, pModInfo);
+ LogRelFlow((DEVICE_NAME ":_info returning %d\n", rc));
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Initialisation entry points *
+*********************************************************************************************************************************/
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Attach type (ddi_attach_cmd_t)
+ *
+ * @return corresponding solaris error code.
+ */
+int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogRelFlow((DEVICE_NAME "::Attach\n"));
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ int rc;
+ /* Only one instance supported. */
+ if (!ASMAtomicCmpXchgPtr(&g_OpenNodeState.pDip, pDip, NULL))
+ return DDI_FAILURE;
+ rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, 0 /* flags */);
+ if (rc == DDI_SUCCESS)
+ return DDI_SUCCESS;
+ ASMAtomicWritePtr(&g_OpenNodeState.pDip, NULL);
+ return DDI_FAILURE;
+ }
+
+ case DDI_RESUME:
+ {
+ /** @todo implement resume for guest driver. */
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Detach entry point, to detach a device to the system or suspend it.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Attach type (ddi_attach_cmd_t)
+ *
+ * @return corresponding solaris error code.
+ */
+int vbmsSolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogRelFlow((DEVICE_NAME "::Detach\n"));
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ ddi_remove_minor_node(pDip, NULL);
+ ASMAtomicWritePtr(&g_OpenNodeState.pDip, NULL);
+ return DDI_SUCCESS;
+ }
+
+ case DDI_SUSPEND:
+ {
+ /** @todo implement suspend for guest driver. */
+ return DDI_SUCCESS;
+ }
+
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @return corresponding solaris error code.
+ */
+int vbmsSolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
+ void **ppvResult)
+{
+ LogRelFlow((DEVICE_NAME "::GetInfo\n"));
+
+ int rc = DDI_SUCCESS;
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ *ppvResult = (void *)g_OpenNodeState.pDip;
+ if (!*ppvResult)
+ rc = DDI_FAILURE;
+ break;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ break;
+ }
+
+ default:
+ rc = DDI_FAILURE;
+ break;
+ }
+
+ NOREF(pvArg);
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Main code *
+*********************************************************************************************************************************/
+
+static void vbmsSolNotify(void *pvState);
+static void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
+ int cValue);
+
+/**
+ * Open callback for the read queue, which we use as a generic device open
+ * handler.
+ */
+int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag, int fMode,
+ cred_t *pCred)
+{
+ PVBMSSTATE pState = NULL;
+ int rc = VINF_SUCCESS;
+
+ NOREF(fFlag);
+ NOREF(pCred);
+ LogRelFlow((DEVICE_NAME "::Open, pWriteQueue=%p\n", WR(pReadQueue)));
+
+ /*
+ * Sanity check on the mode parameter - only open as a driver, not a
+ * module, and we do cloning ourselves.
+ */
+ if (fMode)
+ {
+ LogRel(("::Open: invalid attempt to clone device."));
+ return EINVAL;
+ }
+
+ pState = &g_OpenNodeState;
+ mutex_enter(&pState->InitMtx);
+ /*
+ * Check and remember our STREAM queue.
+ */
+ if ( pState->pWriteQueue
+ && (pState->pWriteQueue != WR(pReadQueue)))
+ {
+ mutex_exit(&pState->InitMtx);
+ LogRel((DEVICE_NAME "::Open: unexpectedly called with a different queue to previous calls. Exiting.\n"));
+ return EINVAL;
+ }
+ if (!pState->cInits)
+ {
+ /*
+ * Initialize IPRT R0 driver, which internally calls OS-specific r0
+ * init, and create a new session.
+ */
+ rc = VbglR0InitClient();
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0GRAlloc((VMMDevRequestHeader **)
+ &pState->pMouseStatusReq,
+ sizeof(*pState->pMouseStatusReq),
+ VMMDevReq_GetMouseStatus);
+ if (RT_FAILURE(rc))
+ VbglR0TerminateClient();
+ else
+ {
+ int rc2;
+ /* Initialise user data for the queues to our state and
+ * vice-versa. */
+ pState->pWriteQueue = WR(pReadQueue);
+ WR(pReadQueue)->q_ptr = (char *)pState;
+ pReadQueue->q_ptr = (char *)pState;
+ qprocson(pReadQueue);
+ /* Enable our IRQ handler. */
+ rc2 = VbglR0SetMouseNotifyCallback(vbmsSolNotify, (void *)pState);
+ if (RT_FAILURE(rc2))
+ /* Log the failure. I may well have not understood what
+ * is going on here, and the logging may help me. */
+ LogRelFlow(("Failed to install the event handler call-back, rc=%Rrc\n",
+ rc2));
+ }
+ }
+ }
+ if (RT_SUCCESS(rc))
+ ++pState->cInits;
+ mutex_exit(&pState->InitMtx);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("open time initialisation failed. rc=%d\n", rc));
+ ASMAtomicWriteNullPtr(&pState->pWriteQueue);
+ return EINVAL;
+ }
+ return 0;
+}
+
+
+/**
+ * Notification callback, called when the VBoxGuest mouse pointer is moved.
+ * We send a VUID event up to user space. We may send a miscalculated event
+ * if a resolution change is half-way through, but that is pretty much to be
+ * expected, so we won't worry about it.
+ */
+void vbmsSolNotify(void *pvState)
+{
+ PVBMSSTATE pState = (PVBMSSTATE)pvState;
+ int rc;
+
+ pState->pMouseStatusReq->mouseFeatures = 0;
+ pState->pMouseStatusReq->pointerXPos = 0;
+ pState->pMouseStatusReq->pointerYPos = 0;
+ rc = VbglR0GRPerform(&pState->pMouseStatusReq->header);
+ if (RT_SUCCESS(rc))
+ {
+ int cMaxScreenX = pState->cMaxScreenX;
+ int cMaxScreenY = pState->cMaxScreenY;
+ int x = pState->pMouseStatusReq->pointerXPos;
+ int y = pState->pMouseStatusReq->pointerYPos;
+
+ if (cMaxScreenX && cMaxScreenY)
+ {
+ vbmsSolVUIDPutAbsEvent(pState, LOC_X_ABSOLUTE,
+ x * cMaxScreenX / VMMDEV_MOUSE_RANGE_MAX);
+ vbmsSolVUIDPutAbsEvent(pState, LOC_Y_ABSOLUTE,
+ y * cMaxScreenY / VMMDEV_MOUSE_RANGE_MAX);
+ }
+ }
+}
+
+
+void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
+ int cValue)
+{
+ queue_t *pReadQueue = RD(pState->pWriteQueue);
+ mblk_t *pMBlk = allocb(sizeof(Firm_event), BPRI_HI);
+ Firm_event *pEvent;
+ AssertReturnVoid(cEvent == LOC_X_ABSOLUTE || cEvent == LOC_Y_ABSOLUTE);
+ if (!pMBlk)
+ return; /* If kernel memory is short a missed event is acceptable! */
+ pEvent = (Firm_event *)pMBlk->b_wptr;
+ pEvent->id = cEvent;
+ pEvent->pair_type = FE_PAIR_DELTA;
+ pEvent->pair = cEvent == LOC_X_ABSOLUTE ? LOC_X_DELTA : LOC_Y_DELTA;
+ pEvent->value = cValue;
+ uniqtime32(&pEvent->time);
+ pMBlk->b_wptr += sizeof(Firm_event);
+ /* Put the message on the queue immediately if it is not blocked. */
+ if (canput(pReadQueue->q_next))
+ putnext(pReadQueue, pMBlk);
+ else
+ putq(pReadQueue, pMBlk);
+}
+
+
+/**
+ * Close callback for the read queue, which we use as a generic device close
+ * handler.
+ */
+int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred)
+{
+ PVBMSSTATE pState = (PVBMSSTATE)pReadQueue->q_ptr;
+
+ LogRelFlow((DEVICE_NAME "::Close, pWriteQueue=%p\n", WR(pReadQueue)));
+ NOREF(fFlag);
+ NOREF(pCred);
+
+ if (!pState)
+ {
+ Log((DEVICE_NAME "::Close: failed to get pState.\n"));
+ return EFAULT;
+ }
+
+ mutex_enter(&pState->InitMtx);
+ --pState->cInits;
+ if (!pState->cInits)
+ {
+ VbglR0SetMouseStatus(0);
+ /* Disable our IRQ handler. */
+ VbglR0SetMouseNotifyCallback(NULL, NULL);
+ qprocsoff(pReadQueue);
+
+ /*
+ * Close the session.
+ */
+ ASMAtomicWriteNullPtr(&pState->pWriteQueue);
+ pReadQueue->q_ptr = NULL;
+ VbglR0GRFree(&pState->pMouseStatusReq->header);
+ VbglR0TerminateClient();
+ }
+ mutex_exit(&pState->InitMtx);
+ return 0;
+}
+
+
+#ifdef TESTCASE
+/** Simple test of vbmsSolOpen and vbmsSolClose. */
+static void testOpenClose(RTTEST hTest)
+{
+ queue_t aQueues[2];
+ dev_t device = 0;
+ int rc;
+
+ RTTestSub(hTest, "Testing vbmsSolOpen and vbmsSolClose");
+ RT_ZERO(g_OpenNodeState);
+ RT_ZERO(aQueues);
+ doInitQueues(&aQueues[0]);
+ rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+ RTTEST_CHECK(hTest, rc == 0);
+ RTTEST_CHECK(hTest, g_OpenNodeState.pWriteQueue == WR(&aQueues[0]));
+ vbmsSolClose(RD(&aQueues[0]), 0, NULL);
+}
+#endif
+
+
+/* Helper for vbmsSolWPut. */
+static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk);
+
+/**
+ * Handler for messages sent from above (user-space and upper modules) which
+ * land in our write queue.
+ */
+int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk)
+{
+ PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
+ LogRelFlowFunc((DEVICE_NAME "::"));
+ switch (pMBlk->b_datap->db_type)
+ {
+ case M_FLUSH:
+ LogRelFlow(("M_FLUSH, FLUSHW=%RTbool, FLUSHR=%RTbool\n",
+ *pMBlk->b_rptr & FLUSHW, *pMBlk->b_rptr & FLUSHR));
+ /* Flush the write queue if so requested. */
+ if (*pMBlk->b_rptr & FLUSHW)
+ flushq(pWriteQueue, FLUSHDATA);
+
+ /* Flush the read queue if so requested. */
+ if (*pMBlk->b_rptr & FLUSHR)
+ flushq(RD(pWriteQueue), FLUSHDATA);
+
+ /* We have no one below us to pass the message on to. */
+ freemsg(pMBlk);
+ return 0;
+ /* M_IOCDATA is additional data attached to (at least) transparent
+ * IOCtls. We handle the two together here and separate them further
+ * down. */
+ case M_IOCTL:
+ case M_IOCDATA:
+ {
+ int err;
+
+ LogRelFlow(( pMBlk->b_datap->db_type == M_IOCTL
+ ? "M_IOCTL\n" : "M_IOCDATA\n"));
+ err = vbmsSolDispatchIOCtl(pState, pMBlk);
+ if (!err)
+ qreply(pWriteQueue, pMBlk);
+ else
+ miocnak(pWriteQueue, pMBlk, 0, err);
+ break;
+ }
+ default:
+ LogRelFlow(("Unknown command, not acknowledging.\n"));
+ }
+ return 0;
+}
+
+
+#ifdef TESTCASE
+/* Constants, definitions and test functions for testWPut. */
+static const int g_ccTestWPutFirmEvent = VUID_FIRM_EVENT;
+# define PVGFORMAT (&g_ccTestWPutFirmEvent)
+# define CBGFORMAT (sizeof(g_ccTestWPutFirmEvent))
+static const Ms_screen_resolution g_TestResolution = { 640, 480 };
+# define PMSIOSRES (&g_TestResolution)
+# define CBMSIOSRES (sizeof(g_TestResolution))
+
+static inline bool testSetResolution(RTTEST hTest, queue_t *pWriteQueue,
+ struct msgb *pMBlk)
+{
+ PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
+ RTTEST_CHECK_MSG_RET(hTest, pState->cMaxScreenX
+ == g_TestResolution.width - 1,
+ (hTest, "pState->cMaxScreenX=%d\n",
+ pState->cMaxScreenX), false);
+ RTTEST_CHECK_MSG_RET(hTest, pState->cMaxScreenY
+ == g_TestResolution.height - 1,
+ (hTest, "pState->cMaxScreenY=%d\n",
+ pState->cMaxScreenY), false);
+ return true;
+}
+
+/** Data table for testWPut. */
+static struct
+{
+ int iIOCCmd;
+ size_t cbData;
+ const void *pvDataIn;
+ size_t cbDataIn;
+ const void *pvDataOut;
+ size_t cbDataOut;
+ int rcExp;
+ bool (*pfnExtra)(RTTEST hTest, queue_t *pWriteQueue, struct msgb *pMBlk);
+ bool fCanTransparent;
+} g_asTestWPut[] =
+{
+ /* iIOCCmd cbData pvDataIn cbDataIn
+ pvDataOut cbDataOut rcExp pfnExtra fCanTransparent */
+ { VUIDGFORMAT, sizeof(int), NULL, 0,
+ PVGFORMAT, CBGFORMAT, 0, NULL, true },
+ { VUIDGFORMAT, sizeof(int) - 1, NULL, 0,
+ NULL, 0, EINVAL, NULL, false },
+ { VUIDGFORMAT, sizeof(int) + 1, NULL, 0,
+ PVGFORMAT, CBGFORMAT, 0, NULL, true },
+ { VUIDSFORMAT, sizeof(int), PVGFORMAT, CBGFORMAT,
+ NULL, 0, 0, NULL, true },
+ { MSIOSRESOLUTION, CBMSIOSRES, PMSIOSRES, CBMSIOSRES,
+ NULL, 0, 0, testSetResolution, true },
+ { VUIDGWHEELINFO, 0, NULL, 0,
+ NULL, 0, EINVAL, NULL, true }
+};
+
+# undef PVGFORMAT
+# undef CBGFORMAT
+# undef PMSIOSRES
+# undef CBMSIOSRES
+
+/* Helpers for testWPut. */
+static void testWPutStreams(RTTEST hTest, unsigned i);
+static void testWPutTransparent(RTTEST hTest, unsigned i);
+static void testWPutIOCDataIn(RTTEST hTest, unsigned i);
+static void testWPutIOCDataOut(RTTEST hTest, unsigned i);
+
+/** Test WPut's handling of different IOCtls, which is bulk of the logic in
+ * this file. */
+static void testWPut(RTTEST hTest)
+{
+ unsigned i;
+
+ RTTestSub(hTest, "Testing vbmsWPut");
+ for (i = 0; i < RT_ELEMENTS(g_asTestWPut); ++i)
+ {
+ AssertReturnVoid(g_asTestWPut[i].cbDataIn <= g_asTestWPut[i].cbData);
+ AssertReturnVoid(g_asTestWPut[i].cbDataOut <= g_asTestWPut[i].cbData);
+ testWPutStreams(hTest, i);
+ if (g_asTestWPut[i].fCanTransparent)
+ testWPutTransparent(hTest, i);
+ if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataIn)
+ testWPutIOCDataIn(hTest, i);
+ if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataOut)
+ testWPutIOCDataOut(hTest, i);
+ }
+}
+
+
+#define MSG_DATA_SIZE 1024
+
+/** Simulate sending a streams IOCtl to WPut with the parameters from table
+ * line @a i. */
+void testWPutStreams(RTTEST hTest, unsigned i)
+{
+ queue_t aQueues[2];
+ dev_t device = 0;
+ struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
+ struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
+ struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
+ int rc, cFormat = 0;
+
+ AssertReturnVoid(pMBlk);
+ AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
+ RT_ZERO(aQueues);
+ doInitQueues(&aQueues[0]);
+ rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+ RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
+ RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
+ == WR(&aQueues[0]), (hTest, "i=%u\n", i));
+ pMBlk->b_datap->db_type = M_IOCTL;
+ pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
+ pIOCBlk->ioc_count = g_asTestWPut[i].cbData;
+ AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
+ memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn,
+ g_asTestWPut[i].cbDataIn);
+ pMBlk->b_cont = pMBlkCont;
+ rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
+ RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
+ (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
+ pIOCBlk->ioc_error));
+ RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_count == g_asTestWPut[i].cbDataOut,
+ (hTest, "i=%u, ioc_count=%u\n", i, pIOCBlk->ioc_count));
+ RTTEST_CHECK_MSG(hTest, !memcmp(pMBlkCont->b_rptr,
+ g_asTestWPut[i].pvDataOut,
+ g_asTestWPut[i].cbDataOut),
+ (hTest, "i=%u\n", i));
+ /* Hack to ensure that miocpullup() gets called when needed. */
+ if (g_asTestWPut[i].cbData > 0)
+ RTTEST_CHECK_MSG(hTest, pMBlk->b_flag == 1, (hTest, "i=%u\n", i));
+ if (!g_asTestWPut[i].rcExp)
+ RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
+ (hTest, "i=%u\n", i));
+ if (g_asTestWPut[i].pfnExtra)
+ if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
+ __PRETTY_FUNCTION__);
+ vbmsSolClose(RD(&aQueues[1]), 0, NULL);
+ freemsg(pMBlk);
+}
+
+
+#define USER_ADDRESS 0xfeedbacc
+
+/** Simulate sending a transparent IOCtl to WPut with the parameters from table
+ * line @a i. */
+void testWPutTransparent(RTTEST hTest, unsigned i)
+{
+ queue_t aQueues[2];
+ dev_t device = 0;
+ struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
+ struct msgb *pMBlkCont = allocb(sizeof(void *), BPRI_MED);
+ struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
+ struct copyreq *pCopyReq;
+ int rc, cFormat = 0;
+
+ /* if (g_asTestWPut[i].cbDataIn == 0 && g_asTestWPut[i].cbDataOut != 0)
+ return; */ /* This case will be handled once the current ones work. */
+ AssertReturnVoid(pMBlk);
+ AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
+ RT_ZERO(aQueues);
+ doInitQueues(&aQueues[0]);
+ rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+ RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
+ RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
+ == WR(&aQueues[0]), (hTest, "i=%u\n", i));
+ pMBlk->b_datap->db_type = M_IOCTL;
+ pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
+ pIOCBlk->ioc_count = TRANSPARENT;
+ *(void **)pMBlkCont->b_rptr = (void *)USER_ADDRESS;
+ pMBlk->b_cont = pMBlkCont;
+ rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
+ pCopyReq = (struct copyreq *)pMBlk->b_rptr;
+ RTTEST_CHECK_MSG(hTest, ( ( g_asTestWPut[i].cbDataIn
+ && (pMBlk->b_datap->db_type == M_COPYIN))
+ || ( g_asTestWPut[i].cbDataOut
+ && (pMBlk->b_datap->db_type == M_COPYOUT))
+ || ( (g_asTestWPut[i].rcExp == 0)
+ && pMBlk->b_datap->db_type == M_IOCACK)
+ || (pMBlk->b_datap->db_type == M_IOCNAK)),
+ (hTest, "i=%u, db_type=%u\n", i,
+ (unsigned) pMBlk->b_datap->db_type));
+ /* Our TRANSPARENT IOCtls can only return non-zero if they have no payload.
+ * Others should either return zero or be non-TRANSPARENT only. */
+ if (pMBlk->b_datap->db_type == M_IOCNAK)
+ RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
+ (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
+ pIOCBlk->ioc_error));
+ if (g_asTestWPut[i].cbData)
+ {
+ RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)USER_ADDRESS,
+ (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
+ RTTEST_CHECK_MSG( hTest, pCopyReq->cq_size
+ == g_asTestWPut[i].cbDataIn
+ ? g_asTestWPut[i].cbDataIn
+ : g_asTestWPut[i].cbDataOut,
+ (hTest, "i=%u, cq_size=%llu\n", i,
+ (unsigned long long)pCopyReq->cq_size));
+ }
+ /* Implementation detail - check that the private pointer is correctly
+ * set to the user address *for two direction IOCtls* or NULL otherwise. */
+ if (g_asTestWPut[i].cbDataIn && g_asTestWPut[i].cbDataOut)
+ RTTEST_CHECK_MSG(hTest, pCopyReq->cq_private == (mblk_t *)USER_ADDRESS,
+ (hTest, "i=%u, cq_private=%p\n", i,
+ pCopyReq->cq_private));
+ else if ( (pMBlk->b_datap->db_type == M_COPYIN)
+ || (pMBlk->b_datap->db_type == M_COPYOUT))
+ RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
+ (hTest, "i=%u, cq_private=%p\n", i,
+ pCopyReq->cq_private));
+ if (!g_asTestWPut[i].rcExp)
+ RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
+ (hTest, "i=%u\n", i));
+ if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbData)
+ if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
+ __PRETTY_FUNCTION__);
+ vbmsSolClose(RD(&aQueues[1]), 0, NULL);
+ freemsg(pMBlk);
+}
+
+
+/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
+ * with the parameters from table line @a i. */
+void testWPutIOCDataIn(RTTEST hTest, unsigned i)
+{
+ queue_t aQueues[2];
+ dev_t device = 0;
+ struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
+ struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
+ struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
+ : NULL;
+ void *pvData = pMBlkCont ? pMBlkCont->b_rptr : NULL;
+ struct copyreq *pCopyReq;
+ int rc, cFormat = 0;
+
+ AssertReturnVoid(pMBlk);
+ AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
+ i);
+ AssertReturnVoidStmt(g_asTestWPut[i].cbDataIn, freemsg(pMBlk));
+ RT_ZERO(aQueues);
+ doInitQueues(&aQueues[0]);
+ rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+ RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
+ RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
+ == WR(&aQueues[0]), (hTest, "i=%u\n", i));
+ pMBlk->b_datap->db_type = M_IOCDATA;
+ pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
+ if (g_asTestWPut[i].cbDataOut)
+ pCopyResp->cp_private = USER_ADDRESS;
+ AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
+ memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn, g_asTestWPut[i].cbDataIn);
+ pMBlk->b_cont = pMBlkCont;
+ rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
+ pCopyReq = (struct copyreq *)pMBlk->b_rptr;
+ RTTEST_CHECK_MSG(hTest, ( ( g_asTestWPut[i].cbDataOut
+ && (pMBlk->b_datap->db_type == M_COPYOUT))
+ || ( (g_asTestWPut[i].rcExp == 0)
+ && pMBlk->b_datap->db_type == M_IOCACK)
+ || (pMBlk->b_datap->db_type == M_IOCNAK)),
+ (hTest, "i=%u, db_type=%u\n", i,
+ (unsigned) pMBlk->b_datap->db_type));
+ if (g_asTestWPut[i].cbDataOut)
+ {
+ RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)pvData,
+ (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
+ RTTEST_CHECK_MSG(hTest, pCopyReq->cq_size == g_asTestWPut[i].cbData,
+ (hTest, "i=%u, cq_size=%llu\n", i,
+ (unsigned long long)pCopyReq->cq_size));
+ RTTEST_CHECK_MSG(hTest, !memcmp(pvData, g_asTestWPut[i].pvDataOut,
+ g_asTestWPut[i].cbDataOut),
+ (hTest, "i=%u\n", i));
+ }
+ RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
+ (hTest, "i=%u, cq_private=%p\n", i,
+ pCopyReq->cq_private));
+ if (!g_asTestWPut[i].rcExp)
+ RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
+ (hTest, "i=%u\n", i));
+ if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbDataOut)
+ if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
+ __PRETTY_FUNCTION__);
+ vbmsSolClose(RD(&aQueues[1]), 0, NULL);
+ freemsg(pMBlk);
+}
+
+
+/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
+ * with the parameters from table line @a i. */
+void testWPutIOCDataOut(RTTEST hTest, unsigned i)
+{
+ queue_t aQueues[2];
+ dev_t device = 0;
+ struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
+ struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
+ : NULL;
+ int rc, cFormat = 0;
+
+ AssertReturnVoid(pMBlk);
+ AssertReturnVoidStmt(g_asTestWPut[i].cbDataOut, freemsg(pMBlk));
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
+ i);
+ RT_ZERO(aQueues);
+ doInitQueues(&aQueues[0]);
+ rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+ RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
+ RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
+ == WR(&aQueues[0]), (hTest, "i=%u\n", i));
+ pMBlk->b_datap->db_type = M_IOCDATA;
+ pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
+ rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
+ RTTEST_CHECK_MSG(hTest, pMBlk->b_datap->db_type == M_IOCACK,
+ (hTest, "i=%u, db_type=%u\n", i,
+ (unsigned) pMBlk->b_datap->db_type));
+ if (!g_asTestWPut[i].rcExp)
+ RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
+ (hTest, "i=%u\n", i));
+ vbmsSolClose(RD(&aQueues[1]), 0, NULL);
+ freemsg(pMBlk);
+}
+#endif
+
+
+/** Data transfer direction of an IOCtl. This is used for describing
+ * transparent IOCtls, and @a UNSPECIFIED is not a valid value for them. */
+enum IOCTLDIRECTION
+{
+ /** This IOCtl transfers no data. */
+ NONE,
+ /** This IOCtl only transfers data from user to kernel. */
+ IN,
+ /** This IOCtl only transfers data from kernel to user. */
+ OUT,
+ /** This IOCtl transfers data from user to kernel and back. */
+ BOTH,
+ /** We aren't saying anything about how the IOCtl transfers data. */
+ UNSPECIFIED
+};
+
+/**
+ * IOCtl handler function.
+ * @returns 0 on success, error code on failure.
+ * @param iCmd The IOCtl command number.
+ * @param pvData Buffer for the user data.
+ * @param cbBuffer Size of the buffer in @a pvData or zero.
+ * @param pcbData Where to set the size of the data returned. Required for
+ * handlers which return data.
+ * @param prc Where to store the return code. Default is zero. Only
+ * used for IOCtls without data for convenience of
+ * implemention.
+ */
+typedef int FNVBMSSOLIOCTL(PVBMSSTATE pState, int iCmd, void *pvData,
+ size_t cbBuffer, size_t *pcbData, int *prc);
+typedef FNVBMSSOLIOCTL *PFNVBMSSOLIOCTL;
+
+/* Helpers for vbmsSolDispatchIOCtl. */
+static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+ PFNVBMSSOLIOCTL pfnHandler,
+ int iCmd, size_t cbCmd,
+ enum IOCTLDIRECTION enmDirection);
+static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
+ size_t cbBuffer, size_t *pcbData, int *prc);
+
+/** Table of supported VUID IOCtls. */
+struct
+{
+ /** The IOCtl number. */
+ int iCmd;
+ /** The size of the buffer which needs to be copied between user and kernel
+ * space, or zero if unknown (must be known for tranparent IOCtls). */
+ size_t cbBuffer;
+ /** The direction the buffer data needs to be copied. This must be
+ * specified for transparent IOCtls. */
+ enum IOCTLDIRECTION enmDirection;
+} g_aVUIDIOCtlDescriptions[] =
+{
+ { VUIDGFORMAT, sizeof(int), OUT },
+ { VUIDSFORMAT, sizeof(int), IN },
+ { VUIDGADDR, 0, UNSPECIFIED },
+ { VUIDGADDR, 0, UNSPECIFIED },
+ { MSIOGETPARMS, sizeof(Ms_parms), OUT },
+ { MSIOSETPARMS, sizeof(Ms_parms), IN },
+ { MSIOSRESOLUTION, sizeof(Ms_screen_resolution), IN },
+ { MSIOBUTTONS, sizeof(int), OUT },
+ { VUIDGWHEELCOUNT, sizeof(int), OUT },
+ { VUIDGWHEELINFO, 0, UNSPECIFIED },
+ { VUIDGWHEELSTATE, 0, UNSPECIFIED },
+ { VUIDSWHEELSTATE, 0, UNSPECIFIED }
+};
+
+/**
+ * Handle a STREAMS IOCtl message for our driver on the write stream. This
+ * function takes care of the IOCtl logic only and does not call qreply() or
+ * miocnak() at all - the caller must call these on success or failure
+ * respectively.
+ * @returns 0 on success or the IOCtl error code on failure.
+ * @param pState pointer to the state structure.
+ * @param pMBlk pointer to the STREAMS message block structure.
+ */
+static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+ int iCmd = pIOCBlk->ioc_cmd, iCmdType = iCmd & (0xff << 8);
+ size_t cbBuffer;
+ enum IOCTLDIRECTION enmDirection;
+
+ LogRelFlowFunc((DEVICE_NAME "::pIOCBlk=%p, iCmdType=%c, iCmd=0x%x\n",
+ pIOCBlk, (char) (iCmdType >> 8), (unsigned)iCmd));
+ switch (iCmdType)
+ {
+ case MSIOC:
+ case VUIOC:
+ {
+ unsigned i;
+
+ for (i = 0; i < RT_ELEMENTS(g_aVUIDIOCtlDescriptions); ++i)
+ if (g_aVUIDIOCtlDescriptions[i].iCmd == iCmd)
+ {
+ cbBuffer = g_aVUIDIOCtlDescriptions[i].cbBuffer;
+ enmDirection = g_aVUIDIOCtlDescriptions[i].enmDirection;
+ return vbmsSolHandleIOCtl(pState, pMBlk,
+ vbmsSolVUIDIOCtl, iCmd,
+ cbBuffer, enmDirection);
+ }
+ return EINVAL;
+ }
+ default:
+ return ENOTTY;
+ }
+}
+
+
+/* Helpers for vbmsSolHandleIOCtl. */
+static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
+ PFNVBMSSOLIOCTL pfnHandler, int iCmd,
+ size_t cbCmd,
+ enum IOCTLDIRECTION enmDirection);
+
+static int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+ PFNVBMSSOLIOCTL pfnHandler,
+ int iCmd, size_t cbCmd,
+ enum IOCTLDIRECTION enmDirection);
+
+static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+ PFNVBMSSOLIOCTL pfnHandler, int iCmd);
+
+static void vbmsSolAcknowledgeIOCtl(mblk_t *pMBlk, int cbData, int rc)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+ pMBlk->b_datap->db_type = M_IOCACK;
+ pIOCBlk->ioc_count = cbData;
+ pIOCBlk->ioc_rval = rc;
+ pIOCBlk->ioc_error = 0;
+}
+
+/**
+ * Generic code for handling STREAMS-specific IOCtl logic and boilerplate. It
+ * calls the IOCtl handler passed to it without the handler having to be aware
+ * of STREAMS structures, or whether this is a transparent (traditional) or an
+ * I_STR (using a STREAMS structure to describe the data) IOCtl. With the
+ * caveat that we only support transparent IOCtls which pass all data in a
+ * single buffer of a fixed size (I_STR IOCtls are restricted to a single
+ * buffer anyway, but the caller can choose the buffer size).
+ * @returns 0 on success or the IOCtl error code on failure.
+ * @param pState pointer to the state structure.
+ * @param pMBlk pointer to the STREAMS message block structure.
+ * @param pfnHandler pointer to the right IOCtl handler function for this
+ * IOCtl number.
+ * @param iCmd IOCtl command number.
+ * @param cbCmd size of the user space buffer for this IOCtl number,
+ * used for processing transparent IOCtls. Pass zero
+ * for IOCtls with no maximum buffer size (which will
+ * not be able to be handled as transparent) or with
+ * no argument.
+ * @param enmDirection data transfer direction of the IOCtl.
+ */
+static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+ PFNVBMSSOLIOCTL pfnHandler, int iCmd,
+ size_t cbCmd, enum IOCTLDIRECTION enmDirection)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+ LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
+ (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
+ if (pMBlk->b_datap->db_type == M_IOCDATA)
+ return vbmsSolHandleIOCtlData(pState, pMBlk, pfnHandler, iCmd,
+ cbCmd, enmDirection);
+ else if ( pMBlk->b_datap->db_type == M_IOCTL
+ && pIOCBlk->ioc_count == TRANSPARENT)
+ return vbmsSolHandleTransparentIOCtl(pState, pMBlk, pfnHandler,
+ iCmd, cbCmd, enmDirection);
+ else if (pMBlk->b_datap->db_type == M_IOCTL)
+ return vbmsSolHandleIStrIOCtl(pState, pMBlk, pfnHandler, iCmd);
+ return EINVAL;
+}
+
+
+/**
+ * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
+ * code is basically the standard boilerplate for handling any streams IOCtl
+ * additional data, which we currently only use for transparent IOCtls.
+ * @copydoc vbmsSolHandleIOCtl
+ */
+static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
+ PFNVBMSSOLIOCTL pfnHandler, int iCmd,
+ size_t cbCmd,
+ enum IOCTLDIRECTION enmDirection)
+{
+ struct copyresp *pCopyResp = (struct copyresp *)pMBlk->b_rptr;
+
+ LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d, cp_rval=%d, cp_private=%p\n",
+ (unsigned)iCmd, (int)cbCmd, (int)enmDirection,
+ (int)(uintptr_t)pCopyResp->cp_rval,
+ (void *)pCopyResp->cp_private));
+ if (pCopyResp->cp_rval) /* cp_rval is a pointer used as a boolean. */
+ return EAGAIN;
+ if ((pCopyResp->cp_private && enmDirection == BOTH) || enmDirection == IN)
+ {
+ size_t cbData = 0;
+ void *pvData = NULL;
+ int err;
+
+ if (!pMBlk->b_cont)
+ return EINVAL;
+ pvData = pMBlk->b_cont->b_rptr;
+ err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
+ if (!err && enmDirection == BOTH)
+ mcopyout(pMBlk, NULL, cbData, pCopyResp->cp_private, NULL);
+ else if (!err && enmDirection == IN)
+ vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
+ if ((err || enmDirection == IN) && pCopyResp->cp_private)
+ freemsg(pCopyResp->cp_private);
+ return err;
+ }
+ else
+ {
+ if (pCopyResp->cp_private)
+ freemsg(pCopyResp->cp_private);
+ AssertReturn(enmDirection == OUT || enmDirection == BOTH, EINVAL);
+ vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
+ return 0;
+ }
+}
+
+/**
+ * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
+ * code is basically the standard boilerplate for handling transparent IOCtls,
+ * that is, IOCtls which are not re-packed inside STREAMS IOCtls.
+ * @copydoc vbmsSolHandleIOCtl
+ */
+int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+ PFNVBMSSOLIOCTL pfnHandler, int iCmd,
+ size_t cbCmd,
+ enum IOCTLDIRECTION enmDirection)
+{
+ int err = 0, rc = 0;
+ size_t cbData = 0;
+
+ LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
+ (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
+ if ( (enmDirection != NONE && !pMBlk->b_cont)
+ || enmDirection == UNSPECIFIED)
+ return EINVAL;
+ if (enmDirection == IN || enmDirection == BOTH)
+ {
+ void *pUserAddr = NULL;
+ /* We only need state data if there is something to copy back. */
+ if (enmDirection == BOTH)
+ pUserAddr = *(void **)pMBlk->b_cont->b_rptr;
+ mcopyin(pMBlk, pUserAddr /* state data */, cbCmd, NULL);
+ }
+ else if (enmDirection == OUT)
+ {
+ mblk_t *pMBlkOut = allocb(cbCmd, BPRI_MED);
+ void *pvData;
+
+ if (!pMBlkOut)
+ return EAGAIN;
+ pvData = pMBlkOut->b_rptr;
+ err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
+ if (!err)
+ mcopyout(pMBlk, NULL, cbData, NULL, pMBlkOut);
+ else
+ freemsg(pMBlkOut);
+ }
+ else
+ {
+ AssertReturn(enmDirection == NONE, EINVAL);
+ err = pfnHandler(pState, iCmd, NULL, 0, NULL, &rc);
+ if (!err)
+ vbmsSolAcknowledgeIOCtl(pMBlk, 0, rc);
+ }
+ return err;
+}
+
+/**
+ * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
+ * code is basically the standard boilerplate for handling any streams IOCtl.
+ * @copydoc vbmsSolHandleIOCtl
+ */
+static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+ PFNVBMSSOLIOCTL pfnHandler, int iCmd)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+ uint_t cbBuffer = pIOCBlk->ioc_count;
+ void *pvData = NULL;
+ int err, rc = 0;
+ size_t cbData = 0;
+
+ LogFlowFunc(("iCmd=0x%x, cbBuffer=%u, b_cont=%p\n",
+ (unsigned)iCmd, cbBuffer, (void *)pMBlk->b_cont));
+ if (cbBuffer && !pMBlk->b_cont)
+ return EINVAL;
+ /* Repack the whole buffer into a single message block if needed. */
+ if (cbBuffer)
+ {
+ err = miocpullup(pMBlk, cbBuffer);
+ if (err)
+ return err;
+ pvData = pMBlk->b_cont->b_rptr;
+ }
+ else if (pMBlk->b_cont) /* consms forgets to set ioc_count. */
+ {
+ pvData = pMBlk->b_cont->b_rptr;
+ cbBuffer = pMBlk->b_cont->b_datap->db_lim
+ - pMBlk->b_cont->b_datap->db_base;
+ }
+ err = pfnHandler(pState, iCmd, pvData, cbBuffer, &cbData, &rc);
+ if (!err)
+ {
+ LogRelFlowFunc(("pMBlk=%p, pMBlk->b_datap=%p, pMBlk->b_rptr=%p\n",
+ pMBlk, pMBlk->b_datap, pMBlk->b_rptr));
+ vbmsSolAcknowledgeIOCtl(pMBlk, cbData, rc);
+ }
+ return err;
+}
+
+
+/**
+ * Handle a VUID input device IOCtl.
+ * @copydoc FNVBMSSOLIOCTL
+ */
+static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
+ size_t cbBuffer, size_t *pcbData, int *prc)
+{
+ LogRelFlowFunc((DEVICE_NAME "::pvData=%p " /* no '\n' */, pvData));
+ switch (iCmd)
+ {
+ case VUIDGFORMAT:
+ {
+ LogRelFlowFunc(("VUIDGFORMAT\n"));
+ if (cbBuffer < sizeof(int))
+ return EINVAL;
+ *(int *)pvData = VUID_FIRM_EVENT;
+ *pcbData = sizeof(int);
+ return 0;
+ }
+ case VUIDSFORMAT:
+ LogRelFlowFunc(("VUIDSFORMAT\n"));
+ /* We define our native format to be VUID_FIRM_EVENT, so there
+ * is nothing more to do and we exit here on success or on
+ * failure. */
+ return 0;
+ case VUIDGADDR:
+ case VUIDSADDR:
+ LogRelFlowFunc(("VUIDGADDR/VUIDSADDR\n"));
+ return ENOTTY;
+ case MSIOGETPARMS:
+ {
+ Ms_parms parms = { 0 };
+
+ LogRelFlowFunc(("MSIOGETPARMS\n"));
+ if (cbBuffer < sizeof(Ms_parms))
+ return EINVAL;
+ *(Ms_parms *)pvData = parms;
+ *pcbData = sizeof(Ms_parms);
+ return 0;
+ }
+ case MSIOSETPARMS:
+ LogRelFlowFunc(("MSIOSETPARMS\n"));
+ return 0;
+ case MSIOSRESOLUTION:
+ {
+ Ms_screen_resolution *pResolution = (Ms_screen_resolution *)pvData;
+ int rc;
+
+ LogRelFlowFunc(("MSIOSRESOLUTION, cbBuffer=%d, sizeof(Ms_screen_resolution)=%d\n",
+ (int) cbBuffer,
+ (int) sizeof(Ms_screen_resolution)));
+ if (cbBuffer < sizeof(Ms_screen_resolution))
+ return EINVAL;
+ LogRelFlowFunc(("%dx%d\n", pResolution->width,
+ pResolution->height));
+ pState->cMaxScreenX = pResolution->width - 1;
+ pState->cMaxScreenY = pResolution->height - 1;
+ /* Note: we don't disable this again until session close. */
+ rc = VbglR0SetMouseStatus( VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
+ | VMMDEV_MOUSE_NEW_PROTOCOL);
+ if (RT_SUCCESS(rc))
+ return 0;
+ pState->cMaxScreenX = 0;
+ pState->cMaxScreenY = 0;
+ return ENODEV;
+ }
+ case MSIOBUTTONS:
+ {
+ LogRelFlowFunc(("MSIOBUTTONS\n"));
+ if (cbBuffer < sizeof(int))
+ return EINVAL;
+ *(int *)pvData = 0;
+ *pcbData = sizeof(int);
+ return 0;
+ }
+ case VUIDGWHEELCOUNT:
+ {
+ LogRelFlowFunc(("VUIDGWHEELCOUNT\n"));
+ if (cbBuffer < sizeof(int))
+ return EINVAL;
+ *(int *)pvData = 0;
+ *pcbData = sizeof(int);
+ return 0;
+ }
+ case VUIDGWHEELINFO:
+ case VUIDGWHEELSTATE:
+ case VUIDSWHEELSTATE:
+ LogRelFlowFunc(("VUIDGWHEELINFO/VUIDGWHEELSTATE/VUIDSWHEELSTATE\n"));
+ return EINVAL;
+ default:
+ LogRelFlowFunc(("Invalid IOCtl command %x\n", iCmd));
+ return EINVAL;
+ }
+}
+
+
+#ifdef TESTCASE
+int main(void)
+{
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("tstVBoxGuest-solaris", &hTest);
+ if (rc)
+ return rc;
+ RTTestBanner(hTest);
+ test_init(hTest);
+ testOpenClose(hTest);
+ testWPut(hTest);
+
+ /*
+ * Summary.
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+#endif
+
diff --git a/src/VBox/Additions/solaris/Mouse/vboxms.conf b/src/VBox/Additions/solaris/Mouse/vboxms.conf
new file mode 100644
index 00000000..eb180e18
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/vboxms.conf
@@ -0,0 +1,42 @@
+# $Id: vboxms.conf $
+## @file
+# OpenSolaris Guest Mouse Driver Configuration
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# This needs to go into /usr/kernel/drv,
+# while the 64-bit driver object goes into the amd64
+# subdirectory (32-bit drivers goes into the same
+# directory).
+#
+name="vboxms" parent="pseudo" instance=0;
diff --git a/src/VBox/Additions/solaris/Mouse/vboxmslnk.c b/src/VBox/Additions/solaris/Mouse/vboxmslnk.c
new file mode 100644
index 00000000..12c5ad34
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/vboxmslnk.c
@@ -0,0 +1,217 @@
+/* $Id: vboxmslnk.c $ */
+/** @file
+ * VirtualBox Guest Additions Mouse Driver for Solaris: user space loader tool.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <VBox/version.h>
+#include <iprt/buildconfig.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <getopt.h>
+
+#define VBOXMSLNK_MUXID_FILE "/system/volatile/vboxmslnk.muxid"
+
+static const char *g_pszProgName;
+
+
+static void vboxmslnk_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ if (fmt[strlen(fmt) - 1] != '\n')
+ (void) fprintf(stderr, " The error reported was: %s\n", strerror(errno));
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+static void vboxmslnk_start(bool fNoLogo)
+{
+ /* Open our pointer integration driver (vboxms). */
+ int hVBoxMS = open("/dev/vboxms", O_RDWR);
+ if (hVBoxMS < 0)
+ vboxmslnk_fatal("Failed to open /dev/vboxms - please make sure that the node exists and that\n"
+ "you have permission to open it.");
+
+ /* Open the Solaris virtual mouse driver (consms). */
+ int hConsMS = open("/dev/mouse", O_RDWR);
+ if (hConsMS < 0)
+ vboxmslnk_fatal("Failed to open /dev/mouse - please make sure that the node exists and that\n"
+ "you have permission to open it.");
+
+ /* Link vboxms to consms from below. What this means is that vboxms is
+ * added to the list of input sources multiplexed by consms, and vboxms
+ * will receive any control messages (such as information about guest
+ * resolution changes) sent to consms. The link can only be broken
+ * explicitly using the connection ID returned from the IOCtl. */
+ int idConnection = ioctl(hConsMS, I_PLINK, hVBoxMS);
+ if (idConnection < 0)
+ vboxmslnk_fatal("Failed to add /dev/vboxms (the pointer integration driver) to /dev/mouse\n"
+ "(the Solaris virtual master mouse).");
+
+ (void) close(hVBoxMS);
+ (void) close(hConsMS);
+
+ if (!fNoLogo)
+ (void) printf("Successfully enabled pointer integration. Connection ID number to the\n"
+ "Solaris virtual master mouse is:\n");
+ (void) printf("%d\n", idConnection);
+
+ /* Save the connection ID (aka mux ID) so it can be retrieved later. */
+ FILE *fp = fopen(VBOXMSLNK_MUXID_FILE, "w");
+ if (fp == NULL)
+ vboxmslnk_fatal("Failed to open %s for writing the connection ID.", VBOXMSLNK_MUXID_FILE);
+ int rc = fprintf(fp, "%d\n", idConnection);
+ if (rc <= 0)
+ vboxmslnk_fatal("Failed to write the connection ID to %s.", VBOXMSLNK_MUXID_FILE);
+ (void) fclose(fp);
+}
+
+static void vboxmslnk_stop()
+{
+ /* Open the Solaris virtual mouse driver (consms). */
+ int hConsMS = open("/dev/mouse", O_RDWR);
+ if (hConsMS < 0)
+ vboxmslnk_fatal("Failed to open /dev/mouse - please make sure that the node exists and that\n"
+ "you have permission to open it.");
+
+ /* Open the vboxmslnk.muxid file and retrieve the saved mux ID. */
+ FILE *fp = fopen(VBOXMSLNK_MUXID_FILE, "r");
+ if (fp == NULL)
+ vboxmslnk_fatal("Failed to open %s for reading the connection ID.", VBOXMSLNK_MUXID_FILE);
+ int idConnection;
+ int rc = fscanf(fp, "%d\n", &idConnection);
+ if (rc <= 0)
+ vboxmslnk_fatal("Failed to read the connection ID from %s.", VBOXMSLNK_MUXID_FILE);
+ (void) fclose(fp);
+ (void) unlink(VBOXMSLNK_MUXID_FILE);
+
+ /* Unlink vboxms from consms so that vboxms is able to be unloaded. */
+ rc = ioctl(hConsMS, I_PUNLINK, idConnection);
+ if (rc < 0)
+ vboxmslnk_fatal("Failed to disconnect /dev/vboxms (the pointer integration driver) from\n"
+ "/dev/mouse (the Solaris virtual master mouse).");
+ (void) close(hConsMS);
+}
+
+static void vboxmslnk_usage()
+{
+ (void) printf("Usage:\n"
+ " %s [--nologo] <--start | --stop>\n"
+ " %s [-V|--version]\n\n"
+ " -V|--version print the tool version.\n"
+ " --nologo do not display the logo text and only output the connection\n"
+ " ID number needed to disable pointer integration\n"
+ " again.\n"
+ " --start Connect the VirtualBox pointer integration kernel module\n"
+ " to the Solaris mouse driver kernel module.\n"
+ " --stop Disconnect the VirtualBox pointer integration kernel module\n"
+ " from the Solaris mouse driver kernel module.\n"
+ " -h|--help display this help text.\n",
+ g_pszProgName, g_pszProgName);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ bool fShowVersion = false, fNoLogo = false, fStart = false, fStop = false;
+ int c;
+
+ g_pszProgName = basename(argv[0]);
+
+ static const struct option vboxmslnk_lopts[] = {
+ {"version", no_argument, 0, 'V' },
+ {"nologo", no_argument, 0, 'n' },
+ {"start", no_argument, 0, 's' },
+ {"stop", no_argument, 0, 't' },
+ {"help", no_argument, 0, 'h' },
+ { 0, 0, 0, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "Vh", vboxmslnk_lopts, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'V':
+ fShowVersion = true;
+ break;
+ case 'n':
+ fNoLogo = true;
+ break;
+ case 's':
+ fStart = true;
+ break;
+ case 't':
+ fStop = true;
+ break;
+ case 'h':
+ default:
+ vboxmslnk_usage();
+ }
+ }
+
+ if ( (!fStart && !fStop && !fShowVersion)
+ || (fStart && fStop)
+ || (fShowVersion && (fNoLogo || fStart || fStop)))
+ vboxmslnk_usage();
+
+ if (fShowVersion)
+ {
+ (void) printf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!fNoLogo)
+ (void) printf(VBOX_PRODUCT
+ " Guest Additions utility for enabling Solaris pointer\nintegration Version "
+ VBOX_VERSION_STRING "\n"
+ "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
+
+ if (fStart)
+ vboxmslnk_start(fNoLogo);
+
+ if (fStop)
+ vboxmslnk_stop();
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml b/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml
new file mode 100644
index 00000000..59662494
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml
@@ -0,0 +1,92 @@
+<?xml version='1.0'?>
+<!--
+#
+# Solaris SMF service manifest for VBoxService (timesync).
+#
+-->
+<!--
+ Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ The contents of this file may alternatively be used under the terms
+ of the Common Development and Distribution License Version 1.0
+ (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ in the VirtualBox distribution, in which case the provisions of the
+ CDDL are applicable instead of those of the GPL.
+
+ You may elect to license modified versions of this file under the
+ terms and conditions of either the GPL or the CDDL or both.
+
+ SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+-->
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+
+<service_bundle type='manifest' name='SUNWvboxguest:vboxmslnk'>
+
+<service
+ name='application/virtualbox/vboxmslnk'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance/>
+
+ <!-- Wait for devices to be initialized as we depend on vboxms (pseudo) -->
+ <dependency
+ name='milestone'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/milestone/devices:default' />
+ </dependency>
+
+ <!-- Wait for local filesystems to be mounted (just to be safe, don't start too early) -->
+ <dependency
+ name='filesystem-local'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/local:default' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/sbin/vboxmslnk --start'
+ timeout_seconds='30' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/usr/sbin/vboxmslnk --stop'
+ timeout_seconds='60' />
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>VirtualBox Mouse module link.</loctext>
+ </common_name>
+ </template>
+</service>
+
+</service_bundle>
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/Makefile.kmk b/src/VBox/Additions/solaris/SharedFolders/Makefile.kmk
new file mode 100644
index 00000000..9568ad75
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/Makefile.kmk
@@ -0,0 +1,113 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Solaris Shared folder kernel module.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#ifneq ($(KBUILD_HOST),solaris)
+#$(error "The Solaris guest additions can only be built on Solaris!")
+#endif
+
+#
+# vboxfs - The Shared Folder Driver
+#
+SYSMODS.solaris += vboxfs
+vboxfs_TEMPLATE = VBoxGuestR0Drv
+vboxfs_DEFS = VBOX_WITH_HGCM VBOX_SVN_REV=$(VBOX_SVN_REV)
+vboxfs_DEPS += $(VBOX_SVN_REV_KMK)
+vboxfs_INCS := \
+ .
+vboxfs_SOURCES = \
+ vboxfs_vfs.c \
+ vboxfs_vnode.c \
+ vboxfs_prov.c
+vboxfs_LIBS = \
+ $(VBOX_LIB_VBGL_R0)
+ifeq ($(KBUILD_HOST),solaris)
+ vboxfs_LDFLAGS.solaris += -N drv/vboxguest -N misc/ctf
+else
+ vboxfs_SOURCES += deps.asm
+ vboxfs_deps.asm_ASFLAGS = -f bin -g null
+endif
+if ($(VBOX_SOLARIS_11_UPDATE_VERSION) > 1 \
+ || ($(VBOX_SOLARIS_11_UPDATE_VERSION) == 1 && $(VBOX_SOLARIS_11_BUILD_VERSION) >= 10))
+ vboxfs_DEFS += VBOX_VFS_EXTENDED_POLICY
+endif
+
+
+ifndef VBOX_OSE
+ #
+ # vboxfs_s10 - The Shared Folder Driver for Solaris 10
+ #
+ SYSMODS.solaris += vboxfs_s10
+ vboxfs_s10_TEMPLATE = VBoxGuestR0Drv
+ vboxfs_s10_DEFS = VBOX_WITH_HGCM VBOX_VFS_SOLARIS_10U6 VBOX_SVN_REV=$(VBOX_SVN_REV)
+ vboxfs_s10_DEPS += $(VBOX_SVN_REV_KMK)
+ vboxfs_s10_INCS := solaris10/
+ vboxfs_s10_SOURCES = \
+ vboxfs_vfs.c \
+ vboxfs_vnode.c \
+ vboxfs_prov.c
+ vboxfs_s10_LIBS = \
+ $(VBOX_LIB_VBGL_R0) \
+ $(VBOX_LIB_IPRT_GUEST_R0)
+ ifeq ($(KBUILD_HOST),solaris)
+ vboxfs_s10_LDFLAGS += -N drv/vboxguest -N misc/ctf
+ else
+ vboxfs_s10_SOURCES += deps.asm
+ vboxfs_s10_deps.asm_ASFLAGS = -f bin -g null
+ endif
+endif # VBOX_OSE
+
+
+#
+# mount - Userland mount wrapper for vboxfs
+#
+PROGRAMS += vboxfsmount
+vboxfsmount_TEMPLATE = VBoxGuestR3Exe
+vboxfsmount_SOURCES = vboxfs_mount.c
+
+
+#
+# Load script.
+#
+INSTALLS += vboxfsload
+vboxfsload_TEMPLATE = VBoxGuestR0Drv
+vboxfsload_EXEC_SOURCES = loadfs.sh
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/deps.asm b/src/VBox/Additions/solaris/SharedFolders/deps.asm
new file mode 100644
index 00000000..d01557ed
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/deps.asm
@@ -0,0 +1,49 @@
+; $Id: deps.asm $
+;; @file
+; Solaris kernel module dependency
+;
+
+;
+; Copyright (C) 2012-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/solaris/kmoddeps.mac"
+
+kmoddeps_header ; ELF header, section table and shared string table
+
+kmoddeps_dynstr_start ; ELF .dynstr section
+kmoddeps_dynstr_string str_misc_ctf, "misc/ctf"
+kmoddeps_dynstr_string str_drv_vboxguest, "drv/vboxguest"
+kmoddeps_dynstr_end
+
+kmoddeps_dynamic_start ; ELF .dynamic section
+kmoddeps_dynamic_needed str_misc_ctf
+kmoddeps_dynamic_needed str_drv_vboxguest
+kmoddeps_dynamic_end
diff --git a/src/VBox/Additions/solaris/SharedFolders/loadfs.sh b/src/VBox/Additions/solaris/SharedFolders/loadfs.sh
new file mode 100755
index 00000000..7a5c1006
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/loadfs.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+# $Id: loadfs.sh $
+## @file
+# For GA development.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+DRVNAME="vboxfs"
+MOUNTHLP="vboxfsmount"
+
+DRVFILE=`dirname "$0"`
+DRVFILE=`cd "$DRVFILE" && pwd`
+MOUNTHLPFILE="$DRVFILE/$MOUNTHLP"
+DRVFILE="$DRVFILE/$DRVNAME"
+if [ ! -f "$DRVFILE" ]; then
+ echo "loadfs.sh: Cannot find $DRVFILE or it's not a file..."
+ exit 1;
+fi
+if [ ! -f "$MOUNTHLPFILE" ]; then
+ echo "load.sh: Cannot find $MOUNTHLPFILE or it's not a file..."
+ exit 1;
+fi
+
+SUDO=sudo
+#set -x
+
+# Unload the driver if loaded.
+for drv in $DRVNAME;
+do
+ LOADED=`modinfo | grep -w "$drv"`
+ if test -n "$LOADED"; then
+ MODID=`echo "$LOADED" | cut -d ' ' -f 1`
+ $SUDO modunload -i $MODID;
+ LOADED=`modinfo | grep -w "$drv"`;
+ if test -n "$LOADED"; then
+ echo "load.sh: failed to unload $drv";
+ dmesg | tail
+ exit 1;
+ fi
+ fi
+done
+
+#
+# Remove old stuff.
+#
+MY_RC=1
+set -e
+$SUDO rm -f \
+ "/usr/kernel/fs/${DRVNAME}" \
+ "/usr/kernel/fs/amd64/${DRVNAME}" \
+ "/etc/fs/vboxfs/mount"
+sync
+set +e
+
+#
+# Install the mount program.
+#
+if [ ! -d /etc/fs/vboxfs ]; then
+ $SUDO mkdir -p /etc/fs/vboxfs
+fi
+$SUDO ln -sf "$MOUNTHLPFILE" /etc/fs/vboxfs/mount
+
+#
+# Load the module. We can load it without copying it to /usr/kernel/fs/.
+#
+if $SUDO modload "$DRVFILE"; then
+ sync
+ MY_RC=0
+else
+ dmesg | tail
+ echo "load.sh: add_drv failed."
+fi
+
+exit $MY_RC;
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs.h
new file mode 100644
index 00000000..db72f7a7
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs.h
@@ -0,0 +1,106 @@
+/* $Id: vboxfs.h $ */
+/** @file
+ * VirtualBox File System Driver for Solaris Guests, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_h
+#define GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_HOST_NAME 256
+#define MAX_NLS_NAME 32
+/** Default stat cache ttl (in ms) */
+#define DEF_STAT_TTL_MS 200
+
+/** The module name. */
+#define DEVICE_NAME "vboxfs"
+
+#ifdef _KERNEL
+
+#include <VBox/VBoxGuestLibSharedFolders.h>
+#include <sys/vfs.h>
+
+/** VNode for VBoxVFS */
+typedef struct vboxvfs_vnode
+{
+ vnode_t *pVNode;
+ vattr_t Attr;
+ SHFLSTRING *pPath;
+ kmutex_t MtxContents;
+} vboxvfs_vnode_t;
+
+
+/** Per-file system mount instance data. */
+typedef struct vboxvfs_globinfo
+{
+ VBGLSFMAP Map;
+ int Ttl;
+ int Uid;
+ int Gid;
+ vfs_t *pVFS;
+ vboxvfs_vnode_t *pVNodeRoot;
+ kmutex_t MtxFS;
+} vboxvfs_globinfo_t;
+
+extern struct vnodeops *g_pVBoxVFS_vnodeops;
+extern const fs_operation_def_t g_VBoxVFS_vnodeops_template[];
+extern VBGLSFCLIENT g_VBoxVFSClient;
+
+/** Helper functions */
+extern int vboxvfs_Stat(const char *pszCaller, vboxvfs_globinfo_t *pVBoxVFSGlobalInfo, SHFLSTRING *pPath,
+ PSHFLFSOBJINFO pResult, boolean_t fAllowFailure);
+extern void vboxvfs_InitVNode(vboxvfs_globinfo_t *pVBoxVFSGlobalInfo, vboxvfs_vnode_t *pVBoxVNode,
+ PSHFLFSOBJINFO pFSInfo);
+
+
+/** Helper macros */
+#define VFS_TO_VBOXVFS(vfs) ((vboxvfs_globinfo_t *)((vfs)->vfs_data))
+#define VBOXVFS_TO_VFS(vboxvfs) ((vboxvfs)->pVFS)
+#define VN_TO_VBOXVN(vnode) ((vboxvfs_vnode_t *)((vnode)->v_data))
+#define VBOXVN_TO_VN(vboxvnode) ((vboxvnode)->pVNode)
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_h */
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c
new file mode 100644
index 00000000..08505afb
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_mount.c
@@ -0,0 +1,179 @@
+/* $Id: vboxfs_mount.c $ */
+/** @file
+ * VirtualBox File System Mount Helper, Solaris host.
+ * Userspace mount wrapper that parses mount (or user-specified) options
+ * and passes it to mount(2) syscall
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/vfs.h>
+#include <sys/mount.h>
+
+#include "vboxfs.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static char g_achOptBuf[MAX_MNTOPT_STR] = { '\0', };
+static const int g_RetErr = 33;
+static const int g_RetMagic = 2;
+static const int g_RetOK = 0;
+
+static void Usage(char *pszName)
+{
+ fprintf(stderr, "Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
+ "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
+ "\n"
+ " -w mount the shared folder writable (the default)\n"
+ " -r mount the shared folder read-only\n"
+ " -o OPTION[,OPTION...] use the mount options specified\n"
+ "\n", pszName);
+ fprintf(stderr, "Available mount options are:\n"
+ "\n"
+ " rw mount writable (the default)\n"
+ " ro mount read only\n"
+ " uid=UID set the default file owner user id to UID\n"
+ " gid=GID set the default file owner group id to GID\n");
+ fprintf(stderr,
+ " dmode=MODE override the mode for all directories (octal) to MODE\n"
+ " fmode=MODE override the mode for all regular files (octal) to MODE\n"
+ " umask=UMASK set the umask (bitmask of permissions not present) in (octal) UMASK\n"
+ " dmask=UMASK set the umask applied to directories only in (octal) UMASK\n"
+ " fmask=UMASK set the umask applied to regular files only in (octal) UMASK\n"
+ " stat_ttl=TTL set the \"time to live\" (in ms) for the stat caches (default %d)\n", DEF_STAT_TTL_MS);
+ fprintf(stderr,
+ " fsync honor fsync calls instead of ignoring them\n"
+ " ttl=TTL set the \"time to live\" to TID for the dentry\n"
+ " iocharset CHARSET use the character set CHARSET for i/o operations (default utf8)\n"
+ " convertcp CHARSET convert the shared folder name from the character set CHARSET to utf8\n\n"
+ "Less common used options:\n"
+ " noexec,exec,nodev,dev,nosuid,suid\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ char *pszName = NULL;
+ char *pszSpecial = NULL;
+ char *pszMount = NULL;
+ char achType[MAXFIDSZ];
+ int c = '?';
+ int rc = -1;
+ int parseError = 0;
+ int mntFlags = 0;
+ int quietFlag = 0;
+
+ pszName = strrchr(argv[0], '/');
+ pszName = pszName ? pszName + 1 : argv[0];
+ snprintf(achType, sizeof(achType), "%s_%s", DEVICE_NAME, pszName);
+
+ while ((c = getopt(argc, argv, "o:rmoQ")) != EOF)
+ {
+ switch (c)
+ {
+ case '?':
+ {
+ parseError = 1;
+ break;
+ }
+
+ case 'q':
+ {
+ quietFlag = 1;
+ break;
+ }
+
+ case 'r':
+ {
+ mntFlags |= MS_RDONLY;
+ break;
+ }
+
+ case 'O':
+ {
+ mntFlags |= MS_OVERLAY;
+ break;
+ }
+
+ case 'm':
+ {
+ mntFlags |= MS_NOMNTTAB;
+ break;
+ }
+
+ case 'o':
+ {
+ if (strlcpy(g_achOptBuf, optarg, sizeof(g_achOptBuf)) >= sizeof(g_achOptBuf))
+ {
+ fprintf(stderr, "%s: invalid argument: %s\n", pszName, optarg);
+ return g_RetMagic;
+ }
+ break;
+ }
+
+ default:
+ {
+ Usage(pszName);
+ break;
+ }
+ }
+ }
+
+ if ( argc - optind != 2
+ || parseError)
+ {
+ Usage(pszName);
+ }
+
+ pszSpecial = argv[argc - 2];
+ pszMount = argv[argc - 1];
+
+ rc = mount(pszSpecial, pszMount, mntFlags | MS_OPTIONSTR, DEVICE_NAME, NULL, 0, g_achOptBuf, sizeof(g_achOptBuf));
+ if (rc)
+ {
+ fprintf(stderr, "mount:");
+ perror(pszSpecial);
+ return g_RetErr;
+ }
+
+ return g_RetOK;
+}
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
new file mode 100644
index 00000000..94251d63
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.c
@@ -0,0 +1,1070 @@
+/* $Id: vboxfs_prov.c $ */
+/** @file
+ * VirtualBox File System for Solaris Guests, provider implementation.
+ * Portions contributed by: Ronald.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/*
+ * Provider interfaces for shared folder file system.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mntent.h>
+#include <sys/param.h>
+#include <sys/modctl.h>
+#include <sys/mount.h>
+#include <sys/policy.h>
+#include <sys/atomic.h>
+#include <sys/sysmacros.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/dirent.h>
+#include <sys/file.h>
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+
+#include "vboxfs_prov.h"
+#include <iprt/err.h>
+
+#define SFPROV_VERSION 1
+
+static VBGLSFCLIENT vbox_client;
+
+static int sfprov_vbox2errno(int rc)
+{
+ if (rc == VERR_ACCESS_DENIED)
+ return (EACCES);
+ if (rc == VERR_INVALID_NAME)
+ return (ENOENT);
+ return (RTErrConvertToErrno(rc));
+}
+
+/*
+ * utility to create strings
+ */
+static SHFLSTRING *
+sfprov_string(char *path, int *sz)
+{
+ SHFLSTRING *str;
+ int len = strlen(path);
+
+ *sz = len + 1 + sizeof (*str) - sizeof (str->String);
+ str = kmem_zalloc(*sz, KM_SLEEP);
+ str->u16Size = len + 1;
+ str->u16Length = len;
+ strcpy(str->String.utf8, path);
+ return (str);
+}
+
+sfp_connection_t *
+sfprov_connect(int version)
+{
+ /*
+ * only one version for now, so must match
+ */
+ int rc = -1;
+ if (version != SFPROV_VERSION)
+ {
+ cmn_err(CE_WARN, "sfprov_connect: wrong version. version=%d expected=%d\n", version, SFPROV_VERSION);
+ return NULL;
+ }
+ rc = VbglR0SfInit();
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfConnect(&vbox_client);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfSetUtf8(&vbox_client);
+ if (RT_SUCCESS(rc))
+ {
+ return ((sfp_connection_t *)&vbox_client);
+ }
+ else
+ cmn_err(CE_WARN, "sfprov_connect: VbglR0SfSetUtf8() failed\n");
+
+ VbglR0SfDisconnect(&vbox_client);
+ }
+ else
+ cmn_err(CE_WARN, "sfprov_connect: VbglR0SfConnect() failed rc=%d\n", rc);
+ VbglR0SfTerm();
+ }
+ else
+ cmn_err(CE_WARN, "sfprov_connect: VbglR0SfInit() failed rc=%d\n", rc);
+ return (NULL);
+}
+
+void
+sfprov_disconnect(sfp_connection_t *conn)
+{
+ if (conn != (sfp_connection_t *)&vbox_client)
+ cmn_err(CE_WARN, "sfprov_disconnect: bad argument\n");
+ VbglR0SfDisconnect(&vbox_client);
+ VbglR0SfTerm();
+}
+
+
+int
+sfprov_mount(sfp_connection_t *conn, char *path, sfp_mount_t **mnt)
+{
+ sfp_mount_t *m;
+ SHFLSTRING *str;
+ int size;
+ int rc;
+
+ m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+ str = sfprov_string(path, &size);
+ rc = VbglR0SfMapFolder(&vbox_client, str, &m->map);
+ if (RT_FAILURE(rc)) {
+ cmn_err(CE_WARN, "sfprov_mount: VbglR0SfMapFolder() failed. path=%s rc=%d\n", path, rc);
+ kmem_free(m, sizeof (*m));
+ *mnt = NULL;
+ rc = EINVAL;
+ } else {
+ *mnt = m;
+ rc = 0;
+ }
+ kmem_free(str, size);
+ return (rc);
+}
+
+int
+sfprov_unmount(sfp_mount_t *mnt)
+{
+ int rc;
+
+ rc = VbglR0SfUnmapFolder(&vbox_client, &mnt->map);
+ if (RT_FAILURE(rc)) {
+ cmn_err(CE_WARN, "sfprov_mount: VbglR0SfUnmapFolder() failed rc=%d\n", rc);
+ rc = EINVAL;
+ } else {
+ rc = 0;
+ }
+ kmem_free(mnt, sizeof (*mnt));
+ return (rc);
+}
+
+/*
+ * query information about a mounted file system
+ */
+int
+sfprov_get_fsinfo(sfp_mount_t *mnt, sffs_fsinfo_t *fsinfo)
+{
+ int rc;
+ SHFLVOLINFO info;
+ uint32_t bytes = sizeof(SHFLVOLINFO);
+
+ rc = VbglR0SfFsInfo(&vbox_client, &mnt->map, 0, SHFL_INFO_GET | SHFL_INFO_VOLUME,
+ &bytes, (SHFLDIRINFO *)&info);
+ if (RT_FAILURE(rc))
+ return (EINVAL);
+
+ fsinfo->blksize = info.ulBytesPerAllocationUnit;
+ fsinfo->blksused = (info.ullTotalAllocationBytes - info.ullAvailableAllocationBytes) / info.ulBytesPerAllocationUnit;
+ fsinfo->blksavail = info.ullAvailableAllocationBytes / info.ulBytesPerAllocationUnit;
+ fsinfo->maxnamesize = info.fsProperties.cbMaxComponent;
+ fsinfo->readonly = info.fsProperties.fReadOnly;
+ return (0);
+}
+
+/*
+ * file/directory information conversions.
+ */
+static void
+sfprov_fmode_from_mode(RTFMODE *fMode, mode_t mode)
+{
+ RTFMODE m = 0;
+
+#define mode_set(r) ((mode) & (S_##r)) ? RTFS_UNIX_##r : 0
+ m = mode_set (ISUID);
+ m |= mode_set (ISGID);
+ m |= (mode & S_ISVTX) ? RTFS_UNIX_ISTXT : 0;
+
+ m |= mode_set (IRUSR);
+ m |= mode_set (IWUSR);
+ m |= mode_set (IXUSR);
+
+ m |= mode_set (IRGRP);
+ m |= mode_set (IWGRP);
+ m |= mode_set (IXGRP);
+
+ m |= mode_set (IROTH);
+ m |= mode_set (IWOTH);
+ m |= mode_set (IXOTH);
+#undef mode_set
+
+ if (S_ISDIR(mode))
+ m |= RTFS_TYPE_DIRECTORY;
+ else if (S_ISREG(mode))
+ m |= RTFS_TYPE_FILE;
+ else if (S_ISFIFO(mode))
+ m |= RTFS_TYPE_FIFO;
+ else if (S_ISCHR(mode))
+ m |= RTFS_TYPE_DEV_CHAR;
+ else if (S_ISBLK(mode))
+ m |= RTFS_TYPE_DEV_BLOCK;
+ else if (S_ISLNK(mode))
+ m |= RTFS_TYPE_SYMLINK;
+ else if (S_ISSOCK(mode))
+ m |= RTFS_TYPE_SOCKET;
+ else
+ m |= RTFS_TYPE_FILE;
+
+ *fMode = m;
+}
+
+static void
+sfprov_mode_from_fmode(sfp_mount_t *mnt, mode_t *mode, RTFMODE fMode)
+{
+ mode_t m = 0;
+
+#define mode_set_from_rt(r) ((fMode) & (RTFS_UNIX_##r)) ? (S_##r) : 0;
+ m = mode_set_from_rt(ISUID);
+ m |= mode_set_from_rt(ISGID);
+ m |= (fMode & RTFS_UNIX_ISTXT) ? S_ISVTX : 0;
+
+ m |= mode_set_from_rt(IRUSR);
+ m |= mode_set_from_rt(IWUSR);
+ m |= mode_set_from_rt(IXUSR);
+
+ m |= mode_set_from_rt(IRGRP);
+ m |= mode_set_from_rt(IWGRP);
+ m |= mode_set_from_rt(IXGRP);
+
+ m |= mode_set_from_rt(IROTH);
+ m |= mode_set_from_rt(IWOTH);
+ m |= mode_set_from_rt(IXOTH);
+#undef mode_set_from_rt
+
+ if (RTFS_IS_DIRECTORY(fMode))
+ {
+ m = mnt->sf_dmode != ~0U ? (mnt->sf_dmode & PERMMASK) : m;
+ m &= ~mnt->sf_dmask;
+ m |= S_IFDIR;
+ }
+ else
+ {
+ m = mnt->sf_fmode != ~0U ? (mnt->sf_fmode & PERMMASK) : m;
+ m &= ~mnt->sf_fmask;
+
+ if (RTFS_IS_FILE(fMode))
+ m |= S_IFREG;
+ else if (RTFS_IS_SYMLINK(fMode))
+ m |= S_IFLNK;
+ else if (RTFS_IS_FIFO(fMode))
+ m |= S_IFIFO;
+ else if (RTFS_IS_DEV_CHAR(fMode))
+ m |= S_IFCHR;
+ else if (RTFS_IS_DEV_BLOCK(fMode))
+ m |= S_IFBLK;
+ else if (RTFS_IS_SOCKET(fMode))
+ m |= S_IFSOCK;
+ }
+
+ *mode = m;
+}
+
+static void
+sfprov_ftime_from_timespec(timestruc_t *time, RTTIMESPEC *ts)
+{
+ uint64_t nanosec = RTTimeSpecGetNano(ts);
+ time->tv_sec = nanosec / UINT64_C(1000000000);
+ time->tv_nsec = nanosec % UINT64_C(1000000000);
+}
+
+static void
+sfprov_stat_from_info(sfp_mount_t *mnt, sffs_stat_t *stat, SHFLFSOBJINFO *info)
+{
+ sfprov_mode_from_fmode(mnt, &stat->sf_mode, info->Attr.fMode);
+ stat->sf_size = info->cbObject;
+ stat->sf_alloc = info->cbAllocated;
+ sfprov_ftime_from_timespec(&stat->sf_atime, &info->AccessTime);
+ sfprov_ftime_from_timespec(&stat->sf_mtime, &info->ModificationTime);
+ sfprov_ftime_from_timespec(&stat->sf_ctime, &info->ChangeTime);
+}
+
+/*
+ * File operations: open/close/read/write/etc.
+ *
+ * open/create can return any relevant errno, however ENOENT
+ * generally means that the host file didn't exist.
+ */
+struct sfp_file {
+ SHFLHANDLE handle;
+ VBGLSFMAP map; /**< need this again for the close operation */
+};
+
+int
+sfprov_create(
+ sfp_mount_t *mnt,
+ char *path,
+ mode_t mode,
+ sfp_file_t **fp,
+ sffs_stat_t *stat)
+{
+
+ int rc;
+ SHFLCREATEPARMS parms;
+ SHFLSTRING *str;
+ int size;
+ sfp_file_t *newfp;
+
+ str = sfprov_string(path, &size);
+ parms.Handle = SHFL_HANDLE_NIL;
+ parms.Info.cbObject = 0;
+ sfprov_fmode_from_mode(&parms.Info.Attr.fMode, mode);
+ parms.CreateFlags = SHFL_CF_ACT_CREATE_IF_NEW |
+ SHFL_CF_ACT_REPLACE_IF_EXISTS | SHFL_CF_ACCESS_READWRITE;
+ rc = VbglR0SfCreate(&vbox_client, &mnt->map, str, &parms);
+ kmem_free(str, size);
+
+ if (RT_FAILURE(rc))
+ {
+ if (rc != VERR_ACCESS_DENIED && rc != VERR_WRITE_PROTECT)
+ cmn_err(CE_WARN, "sfprov_create: VbglR0SfCreate failed! path=%s rc=%d\n", path, rc);
+ return (sfprov_vbox2errno(rc));
+ }
+ if (parms.Handle == SHFL_HANDLE_NIL) {
+ if (parms.Result == SHFL_FILE_EXISTS)
+ return (EEXIST);
+ return (ENOENT);
+ }
+ newfp = kmem_alloc(sizeof(sfp_file_t), KM_SLEEP);
+ newfp->handle = parms.Handle;
+ newfp->map = mnt->map;
+ *fp = newfp;
+ sfprov_stat_from_info(mnt, stat, &parms.Info);
+ return (0);
+}
+
+int
+sfprov_diropen(sfp_mount_t *mnt, char *path, sfp_file_t **fp)
+{
+ int rc;
+ SHFLCREATEPARMS parms;
+ SHFLSTRING *str;
+ int size;
+ sfp_file_t *newfp;
+
+ bzero(&parms, sizeof(parms));
+ str = sfprov_string(path, &size);
+ parms.Handle = SHFL_HANDLE_NIL;
+ parms.Info.cbObject = 0;
+ parms.CreateFlags = SHFL_CF_DIRECTORY
+ | SHFL_CF_ACCESS_READ
+ | SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW;
+
+ /*
+ * Open the host directory.
+ */
+ rc = VbglR0SfCreate(&vbox_client, &mnt->map, str, &parms);
+
+ /*
+ * Our VBoxFS interface here isn't very clear regarding failure and informational status.
+ * Check the file-handle as well as the return code to make sure the operation succeeded.
+ */
+ if (RT_FAILURE(rc)) {
+ kmem_free(str, size);
+ return (sfprov_vbox2errno(rc));
+ }
+
+ if (parms.Handle == SHFL_HANDLE_NIL) {
+ kmem_free(str, size);
+ return (ENOENT);
+ }
+
+ newfp = kmem_alloc(sizeof(sfp_file_t), KM_SLEEP);
+ newfp->handle = parms.Handle;
+ newfp->map = mnt->map;
+ *fp = newfp;
+ return (0);
+}
+
+int
+sfprov_open(sfp_mount_t *mnt, char *path, sfp_file_t **fp, int flag)
+{
+ int rc;
+ SHFLCREATEPARMS parms;
+ SHFLSTRING *str;
+ int size;
+ sfp_file_t *newfp;
+
+ bzero(&parms, sizeof(parms));
+ str = sfprov_string(path, &size);
+ parms.Handle = SHFL_HANDLE_NIL;
+ parms.Info.cbObject = 0;
+
+ /*
+ * Translate file modes.
+ */
+ if (flag & FCREAT) {
+ parms.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW;
+ if (!(flag & FTRUNC))
+ parms.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ }
+ else
+ parms.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
+
+ if (flag & FTRUNC)
+ parms.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS | SHFL_CF_ACCESS_WRITE;
+ if (flag & FWRITE)
+ parms.CreateFlags |= SHFL_CF_ACCESS_WRITE;
+ if (flag & FREAD)
+ parms.CreateFlags |= SHFL_CF_ACCESS_READ;
+ if (flag & FAPPEND)
+ parms.CreateFlags |= SHFL_CF_ACCESS_APPEND;
+
+ /*
+ * Open/create the host file.
+ */
+ rc = VbglR0SfCreate(&vbox_client, &mnt->map, str, &parms);
+
+ /*
+ * Our VBoxFS interface here isn't very clear regarding failure and informational status.
+ * Check the file-handle as well as the return code to make sure the operation succeeded.
+ */
+ if (RT_FAILURE(rc)) {
+ kmem_free(str, size);
+ return (sfprov_vbox2errno(rc));
+ }
+
+ if (parms.Handle == SHFL_HANDLE_NIL) {
+ kmem_free(str, size);
+ return (ENOENT);
+ }
+
+ newfp = kmem_alloc(sizeof(sfp_file_t), KM_SLEEP);
+ newfp->handle = parms.Handle;
+ newfp->map = mnt->map;
+ *fp = newfp;
+ return (0);
+}
+
+int
+sfprov_close(sfp_file_t *fp)
+{
+ int rc;
+
+ rc = VbglR0SfClose(&vbox_client, &fp->map, fp->handle);
+ kmem_free(fp, sizeof(sfp_file_t));
+ return (0);
+}
+
+int
+sfprov_read(sfp_file_t *fp, char *buffer, uint64_t offset, uint32_t *numbytes)
+{
+ int rc;
+
+ rc = VbglR0SfRead(&vbox_client, &fp->map, fp->handle, offset,
+ numbytes, (uint8_t *)buffer, 0 /*fLocked*/);
+ if (RT_FAILURE(rc))
+ return (EINVAL);
+ return (0);
+}
+
+int
+sfprov_write(sfp_file_t *fp, char *buffer, uint64_t offset, uint32_t *numbytes)
+{
+ int rc;
+
+ rc = VbglR0SfWrite(&vbox_client, &fp->map, fp->handle, offset,
+ numbytes, (uint8_t *)buffer, 0 /*fLocked*/);
+ if (RT_FAILURE(rc))
+ return (EINVAL);
+ return (0);
+}
+
+int
+sfprov_fsync(sfp_file_t *fp)
+{
+ int rc;
+
+ rc = VbglR0SfFlush(&vbox_client, &fp->map, fp->handle);
+ if (RT_FAILURE(rc))
+ return (EIO);
+ return (0);
+}
+
+
+static int
+sfprov_getinfo(sfp_mount_t *mnt, char *path, PSHFLFSOBJINFO info)
+{
+ int rc;
+ SHFLCREATEPARMS parms;
+ SHFLSTRING *str;
+ int size;
+
+ str = sfprov_string(path, &size);
+ parms.Handle = 0;
+ parms.Info.cbObject = 0;
+ parms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
+ rc = VbglR0SfCreate(&vbox_client, &mnt->map, str, &parms);
+ kmem_free(str, size);
+
+ if (RT_FAILURE(rc))
+ return (EINVAL);
+ if (parms.Result != SHFL_FILE_EXISTS)
+ return (ENOENT);
+ *info = parms.Info;
+ return (0);
+}
+
+/*
+ * get information about a file (or directory)
+ */
+int
+sfprov_get_mode(sfp_mount_t *mnt, char *path, mode_t *mode)
+{
+ int rc;
+ SHFLFSOBJINFO info;
+
+ rc = sfprov_getinfo(mnt, path, &info);
+ if (rc)
+ return (rc);
+ sfprov_mode_from_fmode(mnt, mode, info.Attr.fMode);
+ return (0);
+}
+
+int
+sfprov_get_size(sfp_mount_t *mnt, char *path, uint64_t *size)
+{
+ int rc;
+ SHFLFSOBJINFO info;
+
+ rc = sfprov_getinfo(mnt, path, &info);
+ if (rc)
+ return (rc);
+ *size = info.cbObject;
+ return (0);
+}
+
+
+int
+sfprov_get_atime(sfp_mount_t *mnt, char *path, timestruc_t *time)
+{
+ int rc;
+ SHFLFSOBJINFO info;
+
+ rc = sfprov_getinfo(mnt, path, &info);
+ if (rc)
+ return (rc);
+ sfprov_ftime_from_timespec(time, &info.AccessTime);
+ return (0);
+}
+
+int
+sfprov_get_mtime(sfp_mount_t *mnt, char *path, timestruc_t *time)
+{
+ int rc;
+ SHFLFSOBJINFO info;
+
+ rc = sfprov_getinfo(mnt, path, &info);
+ if (rc)
+ return (rc);
+ sfprov_ftime_from_timespec(time, &info.ModificationTime);
+ return (0);
+}
+
+int
+sfprov_get_ctime(sfp_mount_t *mnt, char *path, timestruc_t *time)
+{
+ int rc;
+ SHFLFSOBJINFO info;
+
+ rc = sfprov_getinfo(mnt, path, &info);
+ if (rc)
+ return (rc);
+ sfprov_ftime_from_timespec(time, &info.ChangeTime);
+ return (0);
+}
+
+int
+sfprov_get_attr(sfp_mount_t *mnt, char *path, sffs_stat_t *attr)
+{
+ int rc;
+ SHFLFSOBJINFO info;
+
+ rc = sfprov_getinfo(mnt, path, &info);
+ if (rc)
+ return (rc);
+ sfprov_stat_from_info(mnt, attr, &info);
+ return (0);
+}
+
+static void
+sfprov_timespec_from_ftime(RTTIMESPEC *ts, timestruc_t time)
+{
+ uint64_t nanosec = UINT64_C(1000000000) * time.tv_sec + time.tv_nsec;
+ RTTimeSpecSetNano(ts, nanosec);
+}
+
+int
+sfprov_set_attr(
+ sfp_mount_t *mnt,
+ char *path,
+ uint_t mask,
+ mode_t mode,
+ timestruc_t atime,
+ timestruc_t mtime,
+ timestruc_t ctime)
+{
+ int rc, err;
+ SHFLCREATEPARMS parms;
+ SHFLSTRING *str;
+ SHFLFSOBJINFO info;
+ uint32_t bytes;
+ int str_size;
+
+ str = sfprov_string(path, &str_size);
+ parms.Handle = 0;
+ parms.Info.cbObject = 0;
+ parms.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW
+ | SHFL_CF_ACCESS_ATTR_WRITE;
+
+ rc = VbglR0SfCreate(&vbox_client, &mnt->map, str, &parms);
+
+ if (RT_FAILURE(rc)) {
+ cmn_err(CE_WARN, "sfprov_set_attr: VbglR0SfCreate(%s) failed rc=%d\n",
+ path, rc);
+ err = EINVAL;
+ goto fail2;
+ }
+ if (parms.Result != SHFL_FILE_EXISTS) {
+ err = ENOENT;
+ goto fail1;
+ }
+
+ RT_ZERO(info);
+ if (mask & AT_MODE)
+ sfprov_fmode_from_mode(&info.Attr.fMode, mode);
+ if (mask & AT_ATIME)
+ sfprov_timespec_from_ftime(&info.AccessTime, atime);
+ if (mask & AT_MTIME)
+ sfprov_timespec_from_ftime(&info.ModificationTime, mtime);
+ if (mask & AT_CTIME)
+ sfprov_timespec_from_ftime(&info.ChangeTime, ctime);
+
+ bytes = sizeof(info);
+ rc = VbglR0SfFsInfo(&vbox_client, &mnt->map, parms.Handle, SHFL_INFO_SET | SHFL_INFO_FILE,
+ &bytes, (SHFLDIRINFO *)&info);
+ if (RT_FAILURE(rc)) {
+ if (rc != VERR_ACCESS_DENIED && rc != VERR_WRITE_PROTECT)
+ {
+ cmn_err(CE_WARN, "sfprov_set_attr: VbglR0SfFsInfo(%s, FILE) failed rc=%d\n",
+ path, rc);
+ }
+ err = sfprov_vbox2errno(rc);
+ goto fail1;
+ }
+
+ err = 0;
+
+fail1:
+ rc = VbglR0SfClose(&vbox_client, &mnt->map, parms.Handle);
+ if (RT_FAILURE(rc)) {
+ cmn_err(CE_WARN, "sfprov_set_attr: VbglR0SfClose(%s) failed rc=%d\n",
+ path, rc);
+ }
+fail2:
+ kmem_free(str, str_size);
+ return err;
+}
+
+int
+sfprov_set_size(sfp_mount_t *mnt, char *path, uint64_t size)
+{
+ int rc, err;
+ SHFLCREATEPARMS parms;
+ SHFLSTRING *str;
+ SHFLFSOBJINFO info;
+ uint32_t bytes;
+ int str_size;
+
+ str = sfprov_string(path, &str_size);
+ parms.Handle = 0;
+ parms.Info.cbObject = 0;
+ parms.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW
+ | SHFL_CF_ACCESS_WRITE;
+
+ rc = VbglR0SfCreate(&vbox_client, &mnt->map, str, &parms);
+
+ if (RT_FAILURE(rc)) {
+ cmn_err(CE_WARN, "sfprov_set_size: VbglR0SfCreate(%s) failed rc=%d\n",
+ path, rc);
+ err = EINVAL;
+ goto fail2;
+ }
+ if (parms.Result != SHFL_FILE_EXISTS) {
+ err = ENOENT;
+ goto fail1;
+ }
+
+ RT_ZERO(info);
+ info.cbObject = size;
+ bytes = sizeof(info);
+ rc = VbglR0SfFsInfo(&vbox_client, &mnt->map, parms.Handle, SHFL_INFO_SET | SHFL_INFO_SIZE,
+ &bytes, (SHFLDIRINFO *)&info);
+ if (RT_FAILURE(rc)) {
+ cmn_err(CE_WARN, "sfprov_set_size: VbglR0SfFsInfo(%s, SIZE) failed rc=%d\n",
+ path, rc);
+ err = sfprov_vbox2errno(rc);
+ goto fail1;
+ }
+
+ err = 0;
+
+fail1:
+ rc = VbglR0SfClose(&vbox_client, &mnt->map, parms.Handle);
+ if (RT_FAILURE(rc)) {
+ cmn_err(CE_WARN, "sfprov_set_size: VbglR0SfClose(%s) failed rc=%d\n",
+ path, rc);
+ }
+fail2:
+ kmem_free(str, str_size);
+ return err;
+}
+
+/*
+ * Directory operations
+ */
+int
+sfprov_mkdir(
+ sfp_mount_t *mnt,
+ char *path,
+ mode_t mode,
+ sfp_file_t **fp,
+ sffs_stat_t *stat)
+{
+ int rc;
+ SHFLCREATEPARMS parms;
+ SHFLSTRING *str;
+ int size;
+ sfp_file_t *newfp;
+
+ str = sfprov_string(path, &size);
+ parms.Handle = SHFL_HANDLE_NIL;
+ parms.Info.cbObject = 0;
+ sfprov_fmode_from_mode(&parms.Info.Attr.fMode, mode);
+ parms.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_CREATE_IF_NEW |
+ SHFL_CF_ACT_FAIL_IF_EXISTS | SHFL_CF_ACCESS_READ;
+ rc = VbglR0SfCreate(&vbox_client, &mnt->map, str, &parms);
+ kmem_free(str, size);
+
+ if (RT_FAILURE(rc))
+ return (sfprov_vbox2errno(rc));
+ if (parms.Handle == SHFL_HANDLE_NIL) {
+ if (parms.Result == SHFL_FILE_EXISTS)
+ return (EEXIST);
+ return (ENOENT);
+ }
+ newfp = kmem_alloc(sizeof(sfp_file_t), KM_SLEEP);
+ newfp->handle = parms.Handle;
+ newfp->map = mnt->map;
+ *fp = newfp;
+ sfprov_stat_from_info(mnt, stat, &parms.Info);
+ return (0);
+}
+
+int
+sfprov_set_show_symlinks(void)
+{
+ int rc;
+
+ rc = VbglR0SfSetSymlinks(&vbox_client);
+ if (RT_FAILURE(rc))
+ return (sfprov_vbox2errno(rc));
+
+ return (0);
+}
+
+int
+sfprov_remove(sfp_mount_t *mnt, char *path, uint_t is_link)
+{
+ int rc;
+ SHFLSTRING *str;
+ int size;
+
+ str = sfprov_string(path, &size);
+ rc = VbglR0SfRemove(&vbox_client, &mnt->map, str,
+ SHFL_REMOVE_FILE | (is_link ? SHFL_REMOVE_SYMLINK : 0));
+ kmem_free(str, size);
+ if (RT_FAILURE(rc))
+ return (sfprov_vbox2errno(rc));
+ return (0);
+}
+
+int
+sfprov_readlink(
+ sfp_mount_t *mnt,
+ char *path,
+ char *target,
+ size_t tgt_size)
+{
+ int rc;
+ SHFLSTRING *str;
+ int size;
+
+ str = sfprov_string(path, &size);
+
+ rc = VbglR0SfReadLink(&vbox_client, &mnt->map, str, (uint32_t) tgt_size,
+ target);
+ if (RT_FAILURE(rc))
+ rc = sfprov_vbox2errno(rc);
+
+ kmem_free(str, size);
+ return (rc);
+}
+
+int
+sfprov_symlink(
+ sfp_mount_t *mnt,
+ char *linkname,
+ char *target,
+ sffs_stat_t *stat)
+{
+ int rc;
+ SHFLSTRING *lnk, *tgt;
+ int lnk_size, tgt_size;
+ SHFLFSOBJINFO info;
+
+ lnk = sfprov_string(linkname, &lnk_size);
+ tgt = sfprov_string(target, &tgt_size);
+
+ rc = VbglR0SfSymlink(&vbox_client, &mnt->map, lnk, tgt, &info);
+ if (RT_FAILURE(rc)) {
+ rc = sfprov_vbox2errno(rc);
+ goto done;
+ }
+
+ if (stat != NULL)
+ sfprov_stat_from_info(mnt, stat, &info);
+
+done:
+ kmem_free(lnk, lnk_size);
+ kmem_free(tgt, tgt_size);
+
+ return (rc);
+}
+
+int
+sfprov_rmdir(sfp_mount_t *mnt, char *path)
+{
+ int rc;
+ SHFLSTRING *str;
+ int size;
+
+ str = sfprov_string(path, &size);
+ rc = VbglR0SfRemove(&vbox_client, &mnt->map, str, SHFL_REMOVE_DIR);
+ kmem_free(str, size);
+ if (RT_FAILURE(rc))
+ return (sfprov_vbox2errno(rc));
+ return (0);
+}
+
+int
+sfprov_rename(sfp_mount_t *mnt, char *from, char *to, uint_t is_dir)
+{
+ int rc;
+ SHFLSTRING *old, *new;
+ int old_size, new_size;
+
+ old = sfprov_string(from, &old_size);
+ new = sfprov_string(to, &new_size);
+ rc = VbglR0SfRename(&vbox_client, &mnt->map, old, new,
+ (is_dir ? SHFL_RENAME_DIR : SHFL_RENAME_FILE) | SHFL_RENAME_REPLACE_IF_EXISTS);
+ kmem_free(old, old_size);
+ kmem_free(new, new_size);
+ if (RT_FAILURE(rc))
+ return (sfprov_vbox2errno(rc));
+ return (0);
+}
+
+
+/*
+ * Read all filenames in a directory.
+ *
+ * - success - all entries read and returned
+ * - ENOENT - Couldn't open the directory for reading
+ * - EINVAL - Internal error of some kind
+ *
+ * On successful return, *dirents points to a list of sffs_dirents_t;
+ * for each dirent, all fields except the d_ino will be set appropriately.
+ * The caller is responsible for freeing the dirents buffer.
+ */
+int
+sfprov_readdir(
+ sfp_mount_t *mnt,
+ char *path,
+ sffs_dirents_t **dirents,
+ int flag)
+{
+ int error;
+ char *cp;
+ int len;
+ SHFLSTRING *mask_str = NULL; /* must be path with "/ *" appended */
+ int mask_size;
+ sfp_file_t *fp;
+ uint32_t infobuff_alloc = 16384;
+ SHFLDIRINFO *infobuff = NULL, *info;
+ uint32_t numbytes;
+ uint32_t nents;
+ uint32_t size;
+ off_t offset;
+ sffs_dirents_t *cur_buf;
+ struct sffs_dirent *dirent;
+ unsigned short reclen;
+ unsigned short entlen;
+
+ *dirents = NULL;
+
+ error = sfprov_diropen(mnt, path, &fp);
+ if (error != 0)
+ return (ENOENT);
+
+ /*
+ * Allocate the first dirents buffers.
+ */
+ *dirents = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
+ if (*dirents == NULL) {
+ error = (ENOSPC);
+ goto done;
+ }
+ cur_buf = *dirents;
+ cur_buf->sf_next = NULL;
+ cur_buf->sf_len = 0;
+
+ /*
+ * Create mask that VBox expects. This needs to be the directory path,
+ * plus a "*" wildcard to get all files.
+ */
+ len = strlen(path) + 3;
+ cp = kmem_alloc(len, KM_SLEEP);
+ if (cp == NULL) {
+ error = (ENOSPC);
+ goto done;
+ }
+ strcpy(cp, path);
+ strcat(cp, "/*");
+ mask_str = sfprov_string(cp, &mask_size);
+ kmem_free(cp, len);
+
+ /*
+ * Now loop using VbglR0SfDirInfo
+ */
+ infobuff = kmem_alloc(infobuff_alloc, KM_SLEEP);
+ if (infobuff == NULL) {
+ error = (ENOSPC);
+ goto done;
+ }
+
+ offset = 0;
+ for (;;) {
+ numbytes = infobuff_alloc;
+ error = VbglR0SfDirInfo(&vbox_client, &fp->map, fp->handle,
+ mask_str, 0, 0, &numbytes, infobuff, &nents);
+ switch (error) {
+
+ case VINF_SUCCESS:
+ /* fallthrough */
+ case VERR_NO_MORE_FILES:
+ break;
+
+ case VERR_NO_TRANSLATION:
+ /* XXX ??? */
+ break;
+
+ default:
+ error = sfprov_vbox2errno(error);
+ goto done;
+ }
+
+ /*
+ * Create the dirent_t's and save the stats for each name
+ */
+ for (info = infobuff; (char *) info < (char *) infobuff + numbytes; nents--) {
+ /* expand buffers if we need more space */
+ reclen = DIRENT64_RECLEN(strlen(info->name.String.utf8));
+ entlen = sizeof(sffs_stat_t) + reclen;
+ if (SFFS_DIRENTS_OFF + cur_buf->sf_len + entlen > SFFS_DIRENTS_SIZE) {
+ cur_buf->sf_next = kmem_alloc(SFFS_DIRENTS_SIZE, KM_SLEEP);
+ if (cur_buf->sf_next == NULL) {
+ error = ENOSPC;
+ goto done;
+ }
+ cur_buf = cur_buf->sf_next;
+ cur_buf->sf_next = NULL;
+ cur_buf->sf_len = 0;
+ }
+
+ /* create the dirent with the name, offset, and len */
+ dirent = (struct sffs_dirent *)
+ (((char *) &cur_buf->sf_entries[0]) + cur_buf->sf_len);
+ strncpy(&dirent->sf_entry.d_name[0], info->name.String.utf8, DIRENT64_NAMELEN(reclen));
+ dirent->sf_entry.d_reclen = reclen;
+ offset += entlen;
+ dirent->sf_entry.d_off = offset;
+
+ /* save the stats */
+ sfprov_stat_from_info(mnt, &dirent->sf_stat, &info->Info);
+
+ /* next info */
+ cur_buf->sf_len += entlen;
+ size = offsetof (SHFLDIRINFO, name.String) + info->name.u16Size;
+ info = (SHFLDIRINFO *) ((uintptr_t) info + size);
+ }
+ ASSERT(nents == 0);
+ ASSERT((char *) info == (char *) infobuff + numbytes);
+
+ if (error == VERR_NO_MORE_FILES)
+ break;
+ }
+ error = 0;
+
+done:
+ if (error != 0) {
+ while (*dirents) {
+ cur_buf = (*dirents)->sf_next;
+ kmem_free(*dirents, SFFS_DIRENTS_SIZE);
+ *dirents = cur_buf;
+ }
+ }
+ if (infobuff != NULL)
+ kmem_free(infobuff, infobuff_alloc);
+ if (mask_str != NULL)
+ kmem_free(mask_str, mask_size);
+ sfprov_close(fp);
+ return (error);
+}
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
new file mode 100644
index 00000000..b7bada48
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_prov.h
@@ -0,0 +1,200 @@
+/* $Id: vboxfs_prov.h $ */
+/** @file
+ * VirtualBox File System for Solaris Guests, provider header.
+ * Portions contributed by: Ronald.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_prov_h
+#define GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_prov_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/VBoxGuestLibSharedFolders.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * These are the provider interfaces used by sffs to access the underlying
+ * shared file system.
+ */
+#define SFPROV_VERSION 1
+
+/*
+ * Initialization and termination.
+ * sfprov_connect() is called once before any other interfaces and returns
+ * a handle used in further calls. The argument should be SFPROV_VERSION
+ * from above. On failure it returns a NULL pointer.
+ *
+ * sfprov_disconnect() must only be called after all sf file systems have been
+ * unmounted.
+ */
+typedef struct sfp_connection sfp_connection_t;
+
+extern sfp_connection_t *sfprov_connect(int);
+extern void sfprov_disconnect(sfp_connection_t *);
+
+
+/*
+ * Mount / Unmount a shared folder.
+ *
+ * sfprov_mount() takes as input the connection pointer and the name of
+ * the shared folder. On success, it returns zero and supplies an
+ * sfp_mount_t handle. On failure it returns any relevant errno value.
+ *
+ * sfprov_unmount() unmounts the mounted file system. It returns 0 on
+ * success and any relevant errno on failure.
+ *
+ * spf_mount_t is the representation of an active mount point.
+ */
+typedef struct spf_mount_t {
+ VBGLSFMAP map; /**< guest<->host mapping */
+ uid_t sf_uid; /**< owner of the mount point */
+ gid_t sf_gid; /**< group of the mount point */
+ mode_t sf_dmode; /**< mode of all directories if != ~0U */
+ mode_t sf_fmode; /**< mode of all files if != ~0U */
+ mode_t sf_dmask; /**< mask of all directories */
+ mode_t sf_fmask; /**< mask of all files */
+} sfp_mount_t;
+
+extern int sfprov_mount(sfp_connection_t *, char *, sfp_mount_t **);
+extern int sfprov_unmount(sfp_mount_t *);
+
+/*
+ * query information about a mounted file system
+ */
+typedef struct sffs_fsinfo {
+ uint64_t blksize;
+ uint64_t blksused;
+ uint64_t blksavail;
+ uint32_t maxnamesize;
+ uint32_t readonly;
+} sffs_fsinfo_t;
+
+extern int sfprov_get_fsinfo(sfp_mount_t *, sffs_fsinfo_t *);
+
+/*
+ * File operations: open/close/read/write/etc.
+ *
+ * open/create can return any relevant errno, however ENOENT
+ * generally means that the host file didn't exist.
+ */
+typedef struct sffs_stat {
+ mode_t sf_mode;
+ off_t sf_size;
+ off_t sf_alloc;
+ timestruc_t sf_atime;
+ timestruc_t sf_mtime;
+ timestruc_t sf_ctime;
+} sffs_stat_t;
+
+typedef struct sfp_file sfp_file_t;
+
+extern int sfprov_create(sfp_mount_t *, char *path, mode_t mode,
+ sfp_file_t **fp, sffs_stat_t *stat);
+extern int sfprov_diropen(sfp_mount_t *mnt, char *path, sfp_file_t **fp);
+extern int sfprov_open(sfp_mount_t *, char *path, sfp_file_t **fp, int flag);
+extern int sfprov_close(sfp_file_t *fp);
+extern int sfprov_read(sfp_file_t *, char * buffer, uint64_t offset,
+ uint32_t *numbytes);
+extern int sfprov_write(sfp_file_t *, char * buffer, uint64_t offset,
+ uint32_t *numbytes);
+extern int sfprov_fsync(sfp_file_t *fp);
+
+
+/*
+ * get/set information about a file (or directory) using pathname
+ */
+extern int sfprov_get_mode(sfp_mount_t *, char *, mode_t *);
+extern int sfprov_get_size(sfp_mount_t *, char *, uint64_t *);
+extern int sfprov_get_atime(sfp_mount_t *, char *, timestruc_t *);
+extern int sfprov_get_mtime(sfp_mount_t *, char *, timestruc_t *);
+extern int sfprov_get_ctime(sfp_mount_t *, char *, timestruc_t *);
+extern int sfprov_get_attr(sfp_mount_t *, char *, sffs_stat_t *);
+extern int sfprov_set_attr(sfp_mount_t *, char *, uint_t, mode_t,
+ timestruc_t, timestruc_t, timestruc_t);
+extern int sfprov_set_size(sfp_mount_t *, char *, uint64_t);
+
+
+/*
+ * File/Directory operations
+ */
+extern int sfprov_remove(sfp_mount_t *, char *path, uint_t is_link);
+extern int sfprov_mkdir(sfp_mount_t *, char *path, mode_t mode,
+ sfp_file_t **fp, sffs_stat_t *stat);
+extern int sfprov_rmdir(sfp_mount_t *, char *path);
+extern int sfprov_rename(sfp_mount_t *, char *from, char *to, uint_t is_dir);
+
+
+/*
+ * Symbolic link operations
+ */
+extern int sfprov_set_show_symlinks(void);
+extern int sfprov_readlink(sfp_mount_t *, char *path, char *target,
+ size_t tgt_size);
+extern int sfprov_symlink(sfp_mount_t *, char *linkname, char *target,
+ sffs_stat_t *stat);
+
+
+/*
+ * Read directory entries.
+ */
+/*
+ * a singly linked list of buffers, each containing an array of stat's+dirent's.
+ * sf_len is length of the sf_entries array, in bytes.
+ */
+typedef struct sffs_dirents {
+ struct sffs_dirents *sf_next;
+ len_t sf_len;
+ struct sffs_dirent {
+ sffs_stat_t sf_stat;
+ dirent64_t sf_entry; /* this is variable length */
+ } sf_entries[1];
+} sffs_dirents_t;
+
+#define SFFS_DIRENTS_SIZE 8192
+#define SFFS_DIRENTS_OFF (offsetof(sffs_dirents_t, sf_entries[0]))
+
+extern int sfprov_readdir(sfp_mount_t *mnt, char *path,
+ sffs_dirents_t **dirents, int flag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_prov_h */
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
new file mode 100644
index 00000000..2f2d9d17
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.c
@@ -0,0 +1,641 @@
+/* $Id: vboxfs_vfs.c $ */
+/** @file
+ * VirtualBox File System for Solaris Guests, VFS implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <iprt/dbg.h>
+
+#include <sys/types.h>
+#include <sys/mntent.h>
+#include <sys/param.h>
+#include <sys/modctl.h>
+#include <sys/mount.h>
+#include <sys/policy.h>
+#include <sys/atomic.h>
+#include <sys/sysmacros.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/vfs.h>
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+# include <sys/vfs_opreg.h>
+#endif
+#include <sys/pathname.h>
+#include <sys/cmn_err.h>
+#include <sys/vmsystm.h>
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+
+#include "vboxfs_prov.h"
+#include "vboxfs_vnode.h"
+#include "vboxfs_vfs.h"
+#include "vboxfs.h"
+
+
+#define VBOXSOLQUOTE2(x) #x
+#define VBOXSOLQUOTE(x) VBOXSOLQUOTE2(x)
+/** The module name. */
+#define DEVICE_NAME "vboxfs"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC "VirtualBox ShrdFS"
+
+
+/*
+ * Shared Folders filesystem implementation of the Solaris VFS interfaces.
+ * Much of this is cookie cutter code for Solaris filesystem implementation.
+ */
+
+/* forward declarations */
+static int sffs_init(int fstype, char *name);
+static int sffs_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
+static int sffs_unmount(vfs_t *vfsp, int flag, cred_t *cr);
+static int sffs_root(vfs_t *vfsp, vnode_t **vpp);
+static int sffs_statvfs(vfs_t *vfsp, statvfs64_t *sbp);
+
+static mntopt_t sffs_options[] = {
+ /* Option Cancels Opt Arg Flags Data */
+ {"uid", NULL, NULL, MO_HASVALUE, NULL},
+ {"gid", NULL, NULL, MO_HASVALUE, NULL},
+ {"dmode", NULL, NULL, MO_HASVALUE, NULL},
+ {"fmode", NULL, NULL, MO_HASVALUE, NULL},
+ {"dmask", NULL, NULL, MO_HASVALUE, NULL},
+ {"fmask", NULL, NULL, MO_HASVALUE, NULL},
+ {"stat_ttl", NULL, NULL, MO_HASVALUE, NULL},
+ {"fsync", NULL, NULL, 0, NULL},
+ {"tag", NULL, NULL, MO_HASVALUE, NULL}
+};
+
+static mntopts_t sffs_options_table = {
+ sizeof (sffs_options) / sizeof (mntopt_t),
+ sffs_options
+};
+
+static vfsdef_t sffs_vfsdef = {
+ VFSDEF_VERSION,
+ DEVICE_NAME,
+ sffs_init,
+ VSW_HASPROTO,
+ &sffs_options_table
+};
+
+static int sffs_fstype;
+static int sffs_major; /* major number for device */
+
+kmutex_t sffs_minor_lock;
+int sffs_minor; /* minor number for device */
+
+/** Whether to use the old-style map_addr()/choose_addr() routines. */
+bool g_fVBoxVFS_SolOldAddrMap;
+/** The map_addr()/choose_addr() hooks callout table structure. */
+VBoxVFS_SolAddrMap g_VBoxVFS_SolAddrMap;
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+ &mod_fsops,
+ DEVICE_DESC " " VBOX_VERSION_STRING "r" VBOXSOLQUOTE(VBOX_SVN_REV),
+ &sffs_vfsdef
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlfs, NULL
+};
+
+static sfp_connection_t *sfprov = NULL;
+
+int
+_init()
+{
+ RTDBGKRNLINFO hKrnlDbgInfo;
+ int rc = RTR0DbgKrnlInfoOpen(&hKrnlDbgInfo, 0 /* fFlags */);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTR0DbgKrnlInfoQuerySymbol(hKrnlDbgInfo, NULL /* pszModule */, "plat_map_align_amount", NULL /* ppvSymbol */);
+ if (RT_SUCCESS(rc))
+ {
+#if defined(VBOX_VFS_SOLARIS_10U6)
+ g_VBoxVFS_SolAddrMap.MapAddr.pfnSol_map_addr = (void *)map_addr;
+#else
+ g_VBoxVFS_SolAddrMap.ChooseAddr.pfnSol_choose_addr = (void *)choose_addr;
+#endif
+ }
+ else
+ {
+ g_fVBoxVFS_SolOldAddrMap = true;
+#if defined(VBOX_VFS_SOLARIS_10U6)
+ g_VBoxVFS_SolAddrMap.MapAddr.pfnSol_map_addr_old = (void *)map_addr;
+#else
+ g_VBoxVFS_SolAddrMap.ChooseAddr.pfnSol_choose_addr_old = (void *)choose_addr;
+#endif
+ }
+
+ RTR0DbgKrnlInfoRelease(hKrnlDbgInfo);
+ }
+ else
+ {
+ cmn_err(CE_NOTE, "RTR0DbgKrnlInfoOpen failed. rc=%d\n", rc);
+ return rc;
+ }
+
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+int
+_fini()
+{
+ int error;
+
+ error = mod_remove(&modlinkage);
+ if (error)
+ return (error);
+
+ /*
+ * Tear down the operations vectors
+ */
+ sffs_vnode_fini();
+ (void) vfs_freevfsops_by_type(sffs_fstype);
+
+ /*
+ * close connection to the provider
+ */
+ sfprov_disconnect(sfprov);
+ return (0);
+}
+
+
+static int
+sffs_init(int fstype, char *name)
+{
+#if defined(VBOX_VFS_SOLARIS_10U6)
+ static const fs_operation_def_t sffs_vfsops_template[] = {
+ VFSNAME_MOUNT, sffs_mount,
+ VFSNAME_UNMOUNT, sffs_unmount,
+ VFSNAME_ROOT, sffs_root,
+ VFSNAME_STATVFS, sffs_statvfs,
+ NULL, NULL
+ };
+#else
+ static const fs_operation_def_t sffs_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = sffs_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = sffs_unmount },
+ VFSNAME_ROOT, { .vfs_root = sffs_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = sffs_statvfs },
+ NULL, NULL
+ };
+#endif
+ int error;
+
+ ASSERT(fstype != 0);
+ sffs_fstype = fstype;
+ LogFlowFunc(("sffs_init() name=%s\n", name));
+
+ /*
+ * This may seem a silly way to do things for now. But the code
+ * is structured to easily allow it to be used on other hypervisors
+ * which would have a different implementation of the provider.
+ * Hopefully that'll never happen. :)
+ */
+ sfprov = sfprov_connect(SFPROV_VERSION);
+ if (sfprov == NULL) {
+ cmn_err(CE_WARN, "sffs_init: couldn't init sffs provider");
+ return (ENODEV);
+ }
+
+ error = sfprov_set_show_symlinks();
+ if (error != 0) {
+ cmn_err(CE_WARN, "sffs_init: host unable to show symlinks, "
+ "rc=%d\n", error);
+ }
+
+ error = vfs_setfsops(fstype, sffs_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "sffs_init: bad vfs ops template");
+ return (error);
+ }
+
+ error = sffs_vnode_init();
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "sffs_init: bad vnode ops template");
+ return (error);
+ }
+
+ if ((sffs_major = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "sffs_init: Can't get unique device number.");
+ sffs_major = 0;
+ }
+ mutex_init(&sffs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
+ return (0);
+}
+
+/*
+ * wrapper for pn_get
+ */
+static int
+sf_pn_get(char *rawpath, struct mounta *uap, char **outpath)
+{
+ pathname_t path;
+ int error;
+
+ error = pn_get(rawpath, (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE :
+ UIO_USERSPACE, &path);
+ if (error) {
+ LogFlowFunc(("pn_get(%s) failed\n", rawpath));
+ return (error);
+ }
+ *outpath = kmem_alloc(path.pn_pathlen + 1, KM_SLEEP);
+ strcpy(*outpath, path.pn_path);
+ pn_free(&path);
+ return (0);
+}
+
+#ifdef DEBUG_ramshankar
+static void
+sffs_print(sffs_data_t *sffs)
+{
+ cmn_err(CE_NOTE, "sffs_data_t at 0x%p\n", sffs);
+ cmn_err(CE_NOTE, " vfs_t *sf_vfsp = 0x%p\n", sffs->sf_vfsp);
+ cmn_err(CE_NOTE, " vnode_t *sf_rootnode = 0x%p\n", sffs->sf_rootnode);
+ cmn_err(CE_NOTE, " uid_t sf_uid = 0x%lu\n", (ulong_t)sffs->sf_handle->sf_uid);
+ cmn_err(CE_NOTE, " gid_t sf_gid = 0x%lu\n", (ulong_t)sffs->sf_handle->sf_gid);
+ cmn_err(CE_NOTE, " mode_t sf_dmode = 0x%lu\n", (ulong_t)sffs->sf_handle->sf_dmode);
+ cmn_err(CE_NOTE, " mode_t sf_fmode = 0x%lu\n", (ulong_t)sffs->sf_handle->sf_fmode);
+ cmn_err(CE_NOTE, " mode_t sf_dmask = 0x%lu\n", (ulong_t)sffs->sf_handle->sf_dmask);
+ cmn_err(CE_NOTE, " mode_t sf_fmask = 0x%lu\n", (ulong_t)sffs->sf_handle->sf_fmask);
+ cmn_err(CE_NOTE, " char *sf_share_name = %s\n", sffs->sf_share_name);
+ cmn_err(CE_NOTE, " char *sf_mntpath = %s\n", sffs->sf_mntpath);
+ cmn_err(CE_NOTE, " sfp_mount_t *sf_handle = 0x%p\n", sffs->sf_handle);
+}
+#endif
+
+static int
+sffs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ sffs_data_t *sffs;
+ char *mount_point = NULL;
+ char *share_name = NULL;
+ int error;
+ dev_t dev;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ mode_t dmode = ~0U;
+ mode_t fmode = ~0U;
+ mode_t dmask = 0;
+ mode_t fmask = 0;
+ int stat_ttl = DEF_STAT_TTL_MS;
+ int fsync = 0;
+ char *optval;
+ long val;
+ char *path;
+ sfp_mount_t *handle;
+ sfnode_t *sfnode;
+
+ /*
+ * check we have permission to do the mount
+ */
+ LogFlowFunc(("sffs_mount() started\n"));
+ error = secpolicy_fs_mount(cr, mvp, vfsp);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Mount point must be a directory
+ */
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * no support for remount (what is it?)
+ */
+ if (uap->flags & MS_REMOUNT)
+ return (ENOTSUP);
+
+ /*
+ * Ensure that nothing else is actively in/under the mount point
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /*
+ * check for read only has to be done early
+ */
+ if (uap->flags & MS_RDONLY) {
+ vfsp->vfs_flag |= VFS_RDONLY;
+ vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
+ }
+
+ /*
+ * UID to use for all files
+ */
+ if (vfs_optionisset(vfsp, "uid", &optval) &&
+ ddi_strtol(optval, NULL, 10, &val) == 0 &&
+ (uid_t)val == val)
+ uid = val;
+
+ /*
+ * GID to use for all files
+ */
+ if (vfs_optionisset(vfsp, "gid", &optval) &&
+ ddi_strtol(optval, NULL, 10, &val) == 0 &&
+ (gid_t)val == val)
+ gid = val;
+
+ /*
+ * dmode to use for all directories
+ */
+ if (vfs_optionisset(vfsp, "dmode", &optval) &&
+ ddi_strtol(optval, NULL, 8, &val) == 0 &&
+ (mode_t)val == val)
+ dmode = val;
+
+ /*
+ * fmode to use for all files
+ */
+ if (vfs_optionisset(vfsp, "fmode", &optval) &&
+ ddi_strtol(optval, NULL, 8, &val) == 0 &&
+ (mode_t)val == val)
+ fmode = val;
+
+ /*
+ * dmask to use for all directories
+ */
+ if (vfs_optionisset(vfsp, "dmask", &optval) &&
+ ddi_strtol(optval, NULL, 8, &val) == 0 &&
+ (mode_t)val == val)
+ dmask = val;
+
+ /*
+ * fmask to use for all files
+ */
+ if (vfs_optionisset(vfsp, "fmask", &optval) &&
+ ddi_strtol(optval, NULL, 8, &val) == 0 &&
+ (mode_t)val == val)
+ fmask = val;
+
+ /*
+ * umask to use for all directories & files
+ */
+ if (vfs_optionisset(vfsp, "umask", &optval) &&
+ ddi_strtol(optval, NULL, 8, &val) == 0 &&
+ (mode_t)val == val)
+ dmask = fmask = val;
+
+ /*
+ * ttl to use for stat caches
+ */
+ if (vfs_optionisset(vfsp, "stat_ttl", &optval) &&
+ ddi_strtol(optval, NULL, 10, &val) == 0 &&
+ (int)val == val)
+ {
+ stat_ttl = val;
+ }
+ else
+ vfs_setmntopt(vfsp, "stat_ttl", VBOXSOLQUOTE(DEF_STAT_TTL_MS), 0);
+
+ /*
+ * whether to honor fsync
+ */
+ if (vfs_optionisset(vfsp, "fsync", &optval))
+ fsync = 1;
+
+ /*
+ * Any unknown options are an error
+ */
+ if ((uap->flags & MS_DATA) && uap->datalen > 0) {
+ cmn_err(CE_WARN, "sffs: unknown mount options specified");
+ return (EINVAL);
+ }
+
+ /*
+ * get the mount point pathname
+ */
+ error = sf_pn_get(uap->dir, uap, &mount_point);
+ if (error)
+ return (error);
+
+ /*
+ * find what we are mounting
+ */
+ error = sf_pn_get(uap->spec, uap, &share_name);
+ if (error) {
+ kmem_free(mount_point, strlen(mount_point) + 1);
+ return (error);
+ }
+
+ /*
+ * Invoke Hypervisor mount interface before proceeding
+ */
+ error = sfprov_mount(sfprov, share_name, &handle);
+ if (error) {
+ kmem_free(share_name, strlen(share_name) + 1);
+ kmem_free(mount_point, strlen(mount_point) + 1);
+ return (error);
+ }
+
+ /*
+ * find an available minor device number for this mount
+ */
+ mutex_enter(&sffs_minor_lock);
+ do {
+ sffs_minor = (sffs_minor + 1) & L_MAXMIN32;
+ dev = makedevice(sffs_major, sffs_minor);
+ } while (vfs_devismounted(dev));
+ mutex_exit(&sffs_minor_lock);
+
+ /*
+ * allocate and fill in the sffs structure
+ */
+ sffs = kmem_alloc(sizeof (*sffs), KM_SLEEP);
+ sffs->sf_vfsp = vfsp;
+ sffs->sf_handle = handle;
+ sffs->sf_handle->sf_uid = uid;
+ sffs->sf_handle->sf_gid = gid;
+ sffs->sf_handle->sf_dmode = dmode;
+ sffs->sf_handle->sf_fmode = fmode;
+ sffs->sf_handle->sf_dmask = dmask;
+ sffs->sf_handle->sf_fmask = fmask;
+ sffs->sf_stat_ttl = stat_ttl;
+ sffs->sf_fsync = fsync;
+ sffs->sf_share_name = share_name;
+ sffs->sf_mntpath = mount_point;
+ sffs->sf_ino = 3; /* root mount point is always '3' */
+
+ /*
+ * fill in the vfs structure
+ */
+ vfsp->vfs_data = (caddr_t)sffs;
+ vfsp->vfs_fstype = sffs_fstype;
+ vfsp->vfs_dev = dev;
+ vfsp->vfs_bsize = PAGESIZE; /* HERE JOE ??? */
+ vfsp->vfs_flag |= VFS_NOTRUNC; /* HERE JOE ???? */
+ vfs_make_fsid(&vfsp->vfs_fsid, dev, sffs_fstype);
+
+ /*
+ * create the root vnode.
+ * XXX JOE What should the path be here? is "/" really right?
+ * other options?
+ */
+ path = kmem_alloc(2, KM_SLEEP);
+ strcpy(path, ".");
+ mutex_enter(&sffs_lock);
+ sfnode = sfnode_make(sffs, path, VDIR, NULL, NULL, NULL, 0);
+ sffs->sf_rootnode = sfnode_get_vnode(sfnode);
+ sffs->sf_rootnode->v_flag |= VROOT;
+ sffs->sf_rootnode->v_vfsp = vfsp;
+ mutex_exit(&sffs_lock);
+
+ LogFlowFunc(("sffs_mount() success sffs=0x%p\n", sffs));
+#ifdef DEBUG_ramshankar
+ sffs_print(sffs);
+#endif
+ return (error);
+}
+
+static int
+sffs_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ sffs_data_t *sffs = (sffs_data_t *)vfsp->vfs_data;
+ int error;
+
+ /*
+ * generic security check
+ */
+ LogFlowFunc(("sffs_unmount() of sffs=0x%p\n", sffs));
+ if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+ return (error);
+
+ /*
+ * forced unmount is not supported by this file system
+ * and thus, ENOTSUP, is being returned.
+ */
+ if (flag & MS_FORCE) {
+ LogFlowFunc(("sffs_unmount(MS_FORCE) returns ENOSUP\n"));
+ return (ENOTSUP);
+ }
+
+ /*
+ * Mark the file system unmounted.
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+
+ /*
+ * Make sure nothing is still in use.
+ */
+ if (sffs_purge(sffs) != 0) {
+ vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+ LogFlowFunc(("sffs_unmount() returns EBUSY\n"));
+ return (EBUSY);
+ }
+
+ /*
+ * Invoke Hypervisor unmount interface before proceeding
+ */
+ error = sfprov_unmount(sffs->sf_handle);
+ if (error != 0) {
+ /* TBD anything here? */
+ }
+
+ kmem_free(sffs->sf_share_name, strlen(sffs->sf_share_name) + 1);
+ kmem_free(sffs->sf_mntpath, strlen(sffs->sf_mntpath) + 1);
+ kmem_free(sffs, sizeof(*sffs));
+ LogFlowFunc(("sffs_unmount() done\n"));
+ return (0);
+}
+
+/*
+ * return the vnode for the root of the mounted file system
+ */
+static int
+sffs_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ sffs_data_t *sffs = (sffs_data_t *)vfsp->vfs_data;
+ vnode_t *vp = sffs->sf_rootnode;
+
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * get some stats.. fake up the rest
+ */
+static int
+sffs_statvfs(vfs_t *vfsp, statvfs64_t *sbp)
+{
+ sffs_data_t *sffs = (sffs_data_t *)vfsp->vfs_data;
+ sffs_fsinfo_t fsinfo;
+ dev32_t d32;
+ int error;
+
+ bzero(sbp, sizeof(*sbp));
+ error = sfprov_get_fsinfo(sffs->sf_handle, &fsinfo);
+ if (error != 0)
+ return (error);
+
+ sbp->f_bsize = fsinfo.blksize;
+ sbp->f_frsize = fsinfo.blksize;
+
+ sbp->f_bfree = fsinfo.blksavail;
+ sbp->f_bavail = fsinfo.blksavail;
+ sbp->f_files = fsinfo.blksavail / 4; /* some kind of reasonable value */
+ sbp->f_ffree = fsinfo.blksavail / 4;
+ sbp->f_favail = fsinfo.blksavail / 4;
+
+ sbp->f_blocks = fsinfo.blksused + sbp->f_bavail;
+
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sbp->f_fsid = d32;
+ strcpy(&sbp->f_basetype[0], "sffs");
+ sbp->f_flag |= ST_NOSUID;
+
+ if (fsinfo.readonly)
+ sbp->f_flag |= ST_RDONLY;
+
+ sbp->f_namemax = fsinfo.maxnamesize;
+ return (0);
+}
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h
new file mode 100644
index 00000000..6338d95d
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vfs.h
@@ -0,0 +1,90 @@
+/* $Id: vboxfs_vfs.h $ */
+/** @file
+ * VirtualBox File System for Solaris Guests, VFS header.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_vfs_h
+#define GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_vfs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Shared Folders filesystem per-mount data structure.
+ */
+typedef struct sffs_data {
+ vfs_t *sf_vfsp; /* filesystem's vfs struct */
+ vnode_t *sf_rootnode; /* of vnode of the root directory */
+ int sf_stat_ttl; /* ttl for stat caches (in ms) */
+ int sf_fsync; /* whether to honor fsync or not */
+ char *sf_share_name;
+ char *sf_mntpath; /* name of mount point */
+ sfp_mount_t *sf_handle;
+ uint64_t sf_ino; /* per FS ino generator */
+} sffs_data_t;
+
+/*
+ * Workaround for older Solaris versions which called map_addr()/choose_addr()/
+ * map_addr_proc() with an 'alignment' argument that was removed in Solaris
+ * 11.4.
+ */
+typedef struct VBoxVFS_SolAddrMap
+{
+ union
+ {
+ void *(*pfnSol_map_addr) (caddr_t *, size_t, offset_t, uint_t);
+ void *(*pfnSol_map_addr_old) (caddr_t *, size_t, offset_t, int, uint_t);
+ } MapAddr;
+
+ union
+ {
+ int (*pfnSol_choose_addr) (struct as *, caddr_t *, size_t, offset_t, uint_t);
+ int (*pfnSol_choose_addr_old) (struct as *, caddr_t *, size_t, offset_t, int, uint_t);
+ } ChooseAddr;
+} VBoxVFS_SolAddrMap;
+typedef VBoxVFS_SolAddrMap *pVBoxVFS_SolAddrMap;
+
+extern bool g_fVBoxVFS_SolOldAddrMap;
+extern VBoxVFS_SolAddrMap g_VBoxVFS_SolAddrMap;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_vfs_h */
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
new file mode 100644
index 00000000..e639468c
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c
@@ -0,0 +1,2500 @@
+/* $Id: vboxfs_vnode.c $ */
+/** @file
+ * VirtualBox File System for Solaris Guests, vnode implementation.
+ * Portions contributed by: Ronald.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/*
+ * Shared Folder File System is used from Solaris when run as a guest operating
+ * system on VirtualBox, though is meant to be usable with any hypervisor that
+ * can provide similar functionality. The sffs code handles all the Solaris
+ * specific semantics and relies on a provider module to actually access
+ * directories, files, etc. The provider interfaces are described in
+ * "vboxfs_prov.h" and the module implementing them is shipped as part of the
+ * VirtualBox Guest Additions for Solaris.
+ *
+ * The shared folder file system is similar to a networked file system,
+ * but with some caveats. The sffs code caches minimal information and proxies
+ * out to the provider whenever possible. Here are some things that are
+ * handled in this code and not by the proxy:
+ *
+ * - a way to open ".." from any already open directory
+ * - st_ino numbers
+ * - detecting directory changes that happened on the host.
+ *
+ * The implementation builds a cache of information for every file/directory
+ * ever accessed in all mounted sffs filesystems using sf_node structures.
+ *
+ * This information for both open or closed files can become invalid if
+ * asynchronous changes are made on the host. Solaris should not panic() in
+ * this event, but some file system operations may return unexpected errors.
+ * Information for such directories or files while they have active vnodes
+ * is removed from the regular cache and stored in a "stale" bucket until
+ * the vnode becomes completely inactive.
+ *
+ * We suppport only read-only mmap (VBOXVFS_WITH_MMAP) i.e. MAP_SHARED,
+ * MAP_PRIVATE in PROT_READ, this data caching would not be coherent with
+ * normal simultaneous read()/write() operations, nor will it be coherent
+ * with data access on the host. Writable mmap(MAP_SHARED) access is not
+ * implemented, as guaranteeing any kind of coherency with concurrent
+ * activity on the host would be near impossible with the existing
+ * interfaces.
+ *
+ * A note about locking. sffs is not a high performance file system.
+ * No fine grained locking is done. The one sffs_lock protects just about
+ * everything.
+ */
+
+#include <VBox/log.h>
+#include <iprt/asm.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mntent.h>
+#include <sys/param.h>
+#include <sys/modctl.h>
+#include <sys/mount.h>
+#include <sys/policy.h>
+#include <sys/atomic.h>
+#include <sys/sysmacros.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/vfs.h>
+#include <sys/vmsystm.h>
+#include <vm/seg_kpm.h>
+#include <vm/pvn.h>
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+# include <sys/vfs_opreg.h>
+#endif
+#include <sys/pathname.h>
+#include <sys/dirent.h>
+#include <sys/fs_subr.h>
+#include <sys/time.h>
+#include <sys/cmn_err.h>
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+
+#include "vboxfs_prov.h"
+#include "vboxfs_vnode.h"
+#include "vboxfs_vfs.h"
+
+/*
+ * Solaris 11u1b10 Extended Policy putback CR 7121445 removes secpolicy_vnode_access from sys/policy.h
+ */
+#ifdef VBOX_VFS_EXTENDED_POLICY
+int secpolicy_vnode_access(const cred_t *, vnode_t *, uid_t, mode_t);
+#endif
+
+#define VBOXVFS_WITH_MMAP
+
+static struct vnodeops *sffs_ops = NULL;
+
+kmutex_t sffs_lock;
+static avl_tree_t sfnodes;
+static avl_tree_t stale_sfnodes;
+
+/*
+ * For now we'll use an I/O buffer that doesn't page fault for VirtualBox
+ * to transfer data into.
+ */
+char *sffs_buffer;
+
+/*
+ * sfnode_compare() is needed for AVL tree functionality.
+ * The nodes are sorted by mounted filesystem, then path. If the
+ * nodes are stale, the node pointer itself is used to force uniqueness.
+ */
+static int
+sfnode_compare(const void *a, const void *b)
+{
+ sfnode_t *x = (sfnode_t *)a;
+ sfnode_t *y = (sfnode_t *)b;
+ int diff;
+
+ if (x->sf_is_stale) {
+ ASSERT(y->sf_is_stale);
+ diff = strcmp(x->sf_path, y->sf_path);
+ if (diff == 0)
+ diff = (uintptr_t)y - (uintptr_t)x;
+ } else {
+ ASSERT(!y->sf_is_stale);
+ diff = (uintptr_t)y->sf_sffs - (uintptr_t)x->sf_sffs;
+ if (diff == 0)
+ diff = strcmp(x->sf_path, y->sf_path);
+ }
+ if (diff < 0)
+ return (-1);
+ if (diff > 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * Construct a new pathname given an sfnode plus an optional tail component.
+ * This handles ".." and "."
+ */
+static char *
+sfnode_construct_path(sfnode_t *node, char *tail)
+{
+ char *p;
+
+ if (strcmp(tail, ".") == 0 || strcmp(tail, "..") == 0)
+ panic("construct path for %s", tail);
+ p = kmem_alloc(strlen(node->sf_path) + 1 + strlen(tail) + 1, KM_SLEEP);
+ strcpy(p, node->sf_path);
+ strcat(p, "/");
+ strcat(p, tail);
+ return (p);
+}
+
+/*
+ * Clears the (cached) directory listing for the node.
+ */
+static void
+sfnode_clear_dir_list(sfnode_t *node)
+{
+ ASSERT(MUTEX_HELD(&sffs_lock));
+
+ while (node->sf_dir_list != NULL) {
+ sffs_dirents_t *next = node->sf_dir_list->sf_next;
+ kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
+ node->sf_dir_list = next;
+ }
+}
+
+/*
+ * Open the provider file associated with a vnode. Holding the file open is
+ * the only way we have of trying to have a vnode continue to refer to the
+ * same host file in the host in light of the possibility of host side renames.
+ */
+static void
+sfnode_open(sfnode_t *node, int flag)
+{
+ int error;
+ sfp_file_t *fp;
+
+ if (node->sf_file != NULL)
+ return;
+ error = sfprov_open(node->sf_sffs->sf_handle, node->sf_path, &fp, flag);
+ if (error == 0)
+ {
+ node->sf_file = fp;
+ node->sf_flag = flag;
+ }
+ else
+ node->sf_flag = ~0;
+}
+
+/*
+ * get a new vnode reference for an sfnode
+ */
+vnode_t *
+sfnode_get_vnode(sfnode_t *node)
+{
+ vnode_t *vp;
+
+ if (node->sf_vnode != NULL) {
+ VN_HOLD(node->sf_vnode);
+ } else {
+ vp = vn_alloc(KM_SLEEP);
+ LogFlowFunc((" %s gets vnode 0x%p\n", node->sf_path, vp));
+ vp->v_type = node->sf_type;
+ vp->v_vfsp = node->sf_sffs->sf_vfsp;
+ vn_setops(vp, sffs_ops);
+ vp->v_flag = VNOSWAP;
+#ifndef VBOXVFS_WITH_MMAP
+ vp->v_flag |= VNOMAP;
+#endif
+ vn_exists(vp);
+ vp->v_data = node;
+ node->sf_vnode = vp;
+ }
+ return (node->sf_vnode);
+}
+
+/*
+ * Allocate and initialize a new sfnode and assign it a vnode
+ */
+sfnode_t *
+sfnode_make(
+ sffs_data_t *sffs,
+ char *path,
+ vtype_t type,
+ sfp_file_t *fp,
+ sfnode_t *parent, /* can be NULL for root */
+ sffs_stat_t *stat,
+ uint64_t stat_time)
+{
+ sfnode_t *node;
+ avl_index_t where;
+
+ ASSERT(MUTEX_HELD(&sffs_lock));
+ ASSERT(path != NULL);
+
+ /*
+ * build the sfnode
+ */
+ LogFlowFunc(("sffs_make(%s)\n", path));
+ node = kmem_alloc(sizeof (*node), KM_SLEEP);
+ node->sf_sffs = sffs;
+ VFS_HOLD(node->sf_sffs->sf_vfsp);
+ node->sf_path = path;
+ node->sf_ino = sffs->sf_ino++;
+ node->sf_type = type;
+ node->sf_is_stale = 0; /* never stale at creation */
+ node->sf_file = fp;
+ node->sf_flag = ~0;
+ node->sf_vnode = NULL; /* do this before any sfnode_get_vnode() */
+ node->sf_children = 0;
+ node->sf_parent = parent;
+ if (parent)
+ ++parent->sf_children;
+ node->sf_dir_list = NULL;
+ if (stat != NULL) {
+ node->sf_stat = *stat;
+ node->sf_stat_time = stat_time;
+ } else {
+ node->sf_stat_time = 0;
+ }
+
+ /*
+ * add the new node to our cache
+ */
+ if (avl_find(&sfnodes, node, &where) != NULL)
+ panic("sffs_create_sfnode(%s): duplicate sfnode_t", path);
+ avl_insert(&sfnodes, node, where);
+ return (node);
+}
+
+/*
+ * destroy an sfnode
+ */
+static void
+sfnode_destroy(sfnode_t *node)
+{
+ avl_index_t where;
+ avl_tree_t *tree;
+ sfnode_t *parent;
+top:
+ parent = node->sf_parent;
+ ASSERT(MUTEX_HELD(&sffs_lock));
+ ASSERT(node->sf_path != NULL);
+ LogFlowFunc(("sffs_destroy(%s)%s\n", node->sf_path, node->sf_is_stale ? " stale": ""));
+ if (node->sf_children != 0)
+ panic("sfnode_destroy(%s) has %d children", node->sf_path, node->sf_children);
+ if (node->sf_vnode != NULL)
+ panic("sfnode_destroy(%s) has active vnode", node->sf_path);
+
+ if (node->sf_is_stale)
+ tree = &stale_sfnodes;
+ else
+ tree = &sfnodes;
+ if (avl_find(tree, node, &where) == NULL)
+ panic("sfnode_destroy(%s) not found", node->sf_path);
+ avl_remove(tree, node);
+
+ VFS_RELE(node->sf_sffs->sf_vfsp);
+ sfnode_clear_dir_list(node);
+ kmem_free(node->sf_path, strlen(node->sf_path) + 1);
+ kmem_free(node, sizeof (*node));
+ if (parent != NULL) {
+ sfnode_clear_dir_list(parent);
+ if (parent->sf_children == 0)
+ panic("sfnode_destroy parent (%s) has no child", parent->sf_path);
+ --parent->sf_children;
+ if (parent->sf_children == 0 &&
+ parent->sf_is_stale &&
+ parent->sf_vnode == NULL) {
+ node = parent;
+ goto top;
+ }
+ }
+}
+
+/*
+ * Some sort of host operation on an sfnode has failed or it has been
+ * deleted. Mark this node and any children as stale, deleting knowledge
+ * about any which do not have active vnodes or children
+ * This also handle deleting an inactive node that was already stale.
+ */
+static void
+sfnode_make_stale(sfnode_t *node)
+{
+ sfnode_t *n;
+ int len;
+ ASSERT(MUTEX_HELD(&sffs_lock));
+ avl_index_t where;
+
+ /*
+ * First deal with any children of a directory node.
+ * If a directory becomes stale, anything below it becomes stale too.
+ */
+ if (!node->sf_is_stale && node->sf_type == VDIR) {
+ len = strlen(node->sf_path);
+
+ n = node;
+ while ((n = AVL_NEXT(&sfnodes, node)) != NULL) {
+ ASSERT(!n->sf_is_stale);
+
+ /*
+ * quit when no longer seeing children of node
+ */
+ if (n->sf_sffs != node->sf_sffs ||
+ strncmp(node->sf_path, n->sf_path, len) != 0 ||
+ n->sf_path[len] != '/')
+ break;
+
+ /*
+ * Either mark the child as stale or destroy it
+ */
+ if (n->sf_vnode == NULL && n->sf_children == 0) {
+ sfnode_destroy(n);
+ } else {
+ LogFlowFunc(("sffs_make_stale(%s) sub\n", n->sf_path));
+ sfnode_clear_dir_list(n);
+ if (avl_find(&sfnodes, n, &where) == NULL)
+ panic("sfnode_make_stale(%s)"
+ " not in sfnodes", n->sf_path);
+ avl_remove(&sfnodes, n);
+ n->sf_is_stale = 1;
+ if (avl_find(&stale_sfnodes, n, &where) != NULL)
+ panic("sffs_make_stale(%s) duplicates",
+ n->sf_path);
+ avl_insert(&stale_sfnodes, n, where);
+ }
+ }
+ }
+
+ /*
+ * Now deal with the given node.
+ */
+ if (node->sf_vnode == NULL && node->sf_children == 0) {
+ sfnode_destroy(node);
+ } else if (!node->sf_is_stale) {
+ LogFlowFunc(("sffs_make_stale(%s)\n", node->sf_path));
+ sfnode_clear_dir_list(node);
+ if (node->sf_parent)
+ sfnode_clear_dir_list(node->sf_parent);
+ if (avl_find(&sfnodes, node, &where) == NULL)
+ panic("sfnode_make_stale(%s) not in sfnodes",
+ node->sf_path);
+ avl_remove(&sfnodes, node);
+ node->sf_is_stale = 1;
+ if (avl_find(&stale_sfnodes, node, &where) != NULL)
+ panic("sffs_make_stale(%s) duplicates", node->sf_path);
+ avl_insert(&stale_sfnodes, node, where);
+ }
+}
+
+static uint64_t
+sfnode_cur_time_usec(void)
+{
+ clock_t now = drv_hztousec(ddi_get_lbolt());
+ return now;
+}
+
+static int
+sfnode_stat_cached(sfnode_t *node)
+{
+ return (sfnode_cur_time_usec() - node->sf_stat_time) <
+ node->sf_sffs->sf_stat_ttl * 1000L;
+}
+
+static void
+sfnode_invalidate_stat_cache(sfnode_t *node)
+{
+ node->sf_stat_time = 0;
+}
+
+static int
+sfnode_update_stat_cache(sfnode_t *node)
+{
+ int error;
+
+ error = sfprov_get_attr(node->sf_sffs->sf_handle, node->sf_path,
+ &node->sf_stat);
+ if (error == ENOENT)
+ sfnode_make_stale(node);
+ if (error == 0)
+ node->sf_stat_time = sfnode_cur_time_usec();
+
+ return (error);
+}
+
+/*
+ * Rename a file or a directory
+ */
+static void
+sfnode_rename(sfnode_t *node, sfnode_t *newparent, char *path)
+{
+ sfnode_t *n;
+ sfnode_t template;
+ avl_index_t where;
+ int len = strlen(path);
+ int old_len;
+ char *new_path;
+ char *tail;
+ ASSERT(MUTEX_HELD(&sffs_lock));
+
+ ASSERT(!node->sf_is_stale);
+
+ /*
+ * Have to remove anything existing that had the new name.
+ */
+ template.sf_sffs = node->sf_sffs;
+ template.sf_path = path;
+ template.sf_is_stale = 0;
+ n = avl_find(&sfnodes, &template, &where);
+ if (n != NULL)
+ sfnode_make_stale(n);
+
+ /*
+ * Do the renaming, deal with any children of this node first.
+ */
+ if (node->sf_type == VDIR) {
+ old_len = strlen(node->sf_path);
+ while ((n = AVL_NEXT(&sfnodes, node)) != NULL) {
+
+ /*
+ * quit when no longer seeing children of node
+ */
+ if (n->sf_sffs != node->sf_sffs ||
+ strncmp(node->sf_path, n->sf_path, old_len) != 0 ||
+ n->sf_path[old_len] != '/')
+ break;
+
+ /*
+ * Rename the child:
+ * - build the new path name
+ * - unlink the AVL node
+ * - assign the new name
+ * - re-insert the AVL name
+ */
+ ASSERT(strlen(n->sf_path) > old_len);
+ tail = n->sf_path + old_len; /* includes initial "/" */
+ new_path = kmem_alloc(len + strlen(tail) + 1,
+ KM_SLEEP);
+ strcpy(new_path, path);
+ strcat(new_path, tail);
+ if (avl_find(&sfnodes, n, &where) == NULL)
+ panic("sfnode_rename(%s) not in sfnodes",
+ n->sf_path);
+ avl_remove(&sfnodes, n);
+ LogFlowFunc(("sfnode_rname(%s to %s) sub\n", n->sf_path, new_path));
+ kmem_free(n->sf_path, strlen(n->sf_path) + 1);
+ n->sf_path = new_path;
+ if (avl_find(&sfnodes, n, &where) != NULL)
+ panic("sfnode_rename(%s) duplicates",
+ n->sf_path);
+ avl_insert(&sfnodes, n, where);
+ }
+ }
+
+ /*
+ * Deal with the given node.
+ */
+ if (avl_find(&sfnodes, node, &where) == NULL)
+ panic("sfnode_rename(%s) not in sfnodes", node->sf_path);
+ avl_remove(&sfnodes, node);
+ LogFlowFunc(("sfnode_rname(%s to %s)\n", node->sf_path, path));
+ kmem_free(node->sf_path, strlen(node->sf_path) + 1);
+ node->sf_path = path;
+ if (avl_find(&sfnodes, node, &where) != NULL)
+ panic("sfnode_rename(%s) duplicates", node->sf_path);
+ avl_insert(&sfnodes, node, where);
+
+ /*
+ * change the parent
+ */
+ if (node->sf_parent == NULL)
+ panic("sfnode_rename(%s) no parent", node->sf_path);
+ if (node->sf_parent->sf_children == 0)
+ panic("sfnode_rename(%s) parent has no child", node->sf_path);
+ sfnode_clear_dir_list(node->sf_parent);
+ sfnode_clear_dir_list(newparent);
+ --node->sf_parent->sf_children;
+ node->sf_parent = newparent;
+ ++newparent->sf_children;
+}
+
+/*
+ * Look for a cached node, if not found either handle ".." or try looking
+ * via the provider. Create an entry in sfnodes if found but not cached yet.
+ * If the create flag is set, a file or directory is created. If the file
+ * already existed, an error is returned.
+ * Nodes returned from this routine always have a vnode with its ref count
+ * bumped by 1.
+ */
+static sfnode_t *
+sfnode_lookup(
+ sfnode_t *dir,
+ char *name,
+ vtype_t create,
+ mode_t c_mode,
+ sffs_stat_t *stat,
+ uint64_t stat_time,
+ int *err)
+{
+ avl_index_t where;
+ sfnode_t template;
+ sfnode_t *node;
+ int error = 0;
+ int type;
+ char *fullpath;
+ sfp_file_t *fp;
+ sffs_stat_t tmp_stat;
+
+ ASSERT(MUTEX_HELD(&sffs_lock));
+
+ if (err)
+ *err = error;
+
+ /*
+ * handle referencing myself
+ */
+ if (strcmp(name, "") == 0 || strcmp(name, ".") == 0)
+ return (dir);
+
+ /*
+ * deal with parent
+ */
+ if (strcmp(name, "..") == 0)
+ return (dir->sf_parent);
+
+ /*
+ * Look for an existing node.
+ */
+ fullpath = sfnode_construct_path(dir, name);
+ template.sf_sffs = dir->sf_sffs;
+ template.sf_path = fullpath;
+ template.sf_is_stale = 0;
+ node = avl_find(&sfnodes, &template, &where);
+ if (node != NULL) {
+ kmem_free(fullpath, strlen(fullpath) + 1);
+ if (create != VNON)
+ return (NULL);
+ return (node);
+ }
+
+ /*
+ * No entry for this path currently.
+ * Check if the file exists with the provider and get the type from
+ * there.
+ */
+ if (create == VREG) {
+ type = VREG;
+ stat = &tmp_stat;
+ error = sfprov_create(dir->sf_sffs->sf_handle, fullpath, c_mode,
+ &fp, stat);
+ stat_time = sfnode_cur_time_usec();
+ } else if (create == VDIR) {
+ type = VDIR;
+ stat = &tmp_stat;
+ error = sfprov_mkdir(dir->sf_sffs->sf_handle, fullpath, c_mode,
+ &fp, stat);
+ stat_time = sfnode_cur_time_usec();
+ } else {
+ mode_t m;
+ fp = NULL;
+ type = VNON;
+ if (stat == NULL) {
+ stat = &tmp_stat;
+ error = sfprov_get_attr(dir->sf_sffs->sf_handle,
+ fullpath, stat);
+ stat_time = sfnode_cur_time_usec();
+ } else {
+ error = 0;
+ }
+ m = stat->sf_mode;
+ if (error != 0)
+ error = ENOENT;
+ else if (S_ISDIR(m))
+ type = VDIR;
+ else if (S_ISREG(m))
+ type = VREG;
+ else if (S_ISLNK(m))
+ type = VLNK;
+ }
+
+ if (err)
+ *err = error;
+
+ /*
+ * If no errors, make a new node and return it.
+ */
+ if (error) {
+ kmem_free(fullpath, strlen(fullpath) + 1);
+ return (NULL);
+ }
+ node = sfnode_make(dir->sf_sffs, fullpath, type, fp, dir, stat,
+ stat_time);
+ return (node);
+}
+
+
+/*
+ * uid and gid in sffs determine owner and group for all files.
+ */
+static int
+sfnode_access(sfnode_t *node, mode_t mode, cred_t *cr)
+{
+ sffs_data_t *sffs = node->sf_sffs;
+ mode_t m;
+ int shift = 0;
+ int error;
+ vnode_t *vp;
+
+ ASSERT(MUTEX_HELD(&sffs_lock));
+
+ /*
+ * get the mode from the cache or provider
+ */
+ if (sfnode_stat_cached(node))
+ error = 0;
+ else
+ error = sfnode_update_stat_cache(node);
+ m = (error == 0) ? (node->sf_stat.sf_mode & MODEMASK) : 0;
+
+ /*
+ * mask off the permissions based on uid/gid
+ */
+ if (crgetuid(cr) != sffs->sf_handle->sf_uid) {
+ shift += 3;
+ if (groupmember(sffs->sf_handle->sf_gid, cr) == 0)
+ shift += 3;
+ }
+ mode &= ~(m << shift);
+
+ if (mode == 0) {
+ error = 0;
+ } else {
+ /** @todo r=ramshankar: This can probably be optimized by holding static vnode
+ * templates for dir/file, as it only checks the type rather than
+ * fetching/allocating the real vnode. */
+ vp = sfnode_get_vnode(node);
+ error = secpolicy_vnode_access(cr, vp, sffs->sf_handle->sf_uid, mode);
+ VN_RELE(vp);
+ }
+ return (error);
+}
+
+
+/*
+ *
+ * Everything below this point are the vnode operations used by Solaris VFS
+ */
+static int
+sffs_readdir(
+ vnode_t *vp,
+ uio_t *uiop,
+ cred_t *cred,
+ int *eofp,
+ caller_context_t *ct,
+ int flag)
+{
+ sfnode_t *dir = VN2SFN(vp);
+ sfnode_t *node;
+ struct sffs_dirent *dirent = NULL;
+ sffs_dirents_t *cur_buf;
+ offset_t offset = 0;
+ offset_t orig_off = uiop->uio_loffset;
+ int dummy_eof;
+ int error = 0;
+
+ if (uiop->uio_iovcnt != 1)
+ return (EINVAL);
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if (eofp == NULL)
+ eofp = &dummy_eof;
+ *eofp = 0;
+
+ if (uiop->uio_loffset >= MAXOFFSET_T) {
+ *eofp = 1;
+ return (0);
+ }
+
+ /*
+ * Get the directory entry names from the host. This gets all
+ * entries. These are stored in a linked list of sffs_dirents_t
+ * buffers, each of which contains a list of dirent64_t's.
+ */
+ mutex_enter(&sffs_lock);
+
+ if (dir->sf_dir_list == NULL) {
+ error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
+ &dir->sf_dir_list, flag);
+ if (error != 0)
+ goto done;
+ }
+
+ /*
+ * Validate and skip to the desired offset.
+ */
+ cur_buf = dir->sf_dir_list;
+ offset = 0;
+
+ while (cur_buf != NULL &&
+ offset + cur_buf->sf_len <= uiop->uio_loffset) {
+ offset += cur_buf->sf_len;
+ cur_buf = cur_buf->sf_next;
+ }
+
+ if (cur_buf == NULL && offset != uiop->uio_loffset) {
+ error = EINVAL;
+ goto done;
+ }
+ if (cur_buf != NULL && offset != uiop->uio_loffset) {
+ offset_t off = offset;
+ int step;
+ dirent = &cur_buf->sf_entries[0];
+
+ while (off < uiop->uio_loffset) {
+ if (dirent->sf_entry.d_off == uiop->uio_loffset)
+ break;
+ step = sizeof(sffs_stat_t) + dirent->sf_entry.d_reclen;
+ dirent = (struct sffs_dirent *) (((char *) dirent) + step);
+ off += step;
+ }
+
+ if (off >= uiop->uio_loffset) {
+ error = EINVAL;
+ goto done;
+ }
+ }
+
+ offset = uiop->uio_loffset - offset;
+
+ /*
+ * Lookup each of the names, so that we have ino's, and copy to
+ * result buffer.
+ */
+ while (cur_buf != NULL) {
+ if (offset >= cur_buf->sf_len) {
+ cur_buf = cur_buf->sf_next;
+ offset = 0;
+ continue;
+ }
+
+ dirent = (struct sffs_dirent *)
+ (((char *) &cur_buf->sf_entries[0]) + offset);
+ if (dirent->sf_entry.d_reclen > uiop->uio_resid)
+ break;
+
+ if (strcmp(dirent->sf_entry.d_name, ".") == 0) {
+ node = dir;
+ } else if (strcmp(dirent->sf_entry.d_name, "..") == 0) {
+ node = dir->sf_parent;
+ if (node == NULL)
+ node = dir;
+ } else {
+ node = sfnode_lookup(dir, dirent->sf_entry.d_name, VNON,
+ 0, &dirent->sf_stat, sfnode_cur_time_usec(), NULL);
+ if (node == NULL)
+ panic("sffs_readdir() lookup failed");
+ }
+ dirent->sf_entry.d_ino = node->sf_ino;
+
+ error = uiomove(&dirent->sf_entry, dirent->sf_entry.d_reclen, UIO_READ, uiop);
+ if (error != 0)
+ break;
+
+ uiop->uio_loffset= dirent->sf_entry.d_off;
+ offset += sizeof(sffs_stat_t) + dirent->sf_entry.d_reclen;
+ }
+ if (error == 0 && cur_buf == NULL)
+ *eofp = 1;
+done:
+ mutex_exit(&sffs_lock);
+ if (error != 0)
+ uiop->uio_loffset = orig_off;
+ return (error);
+}
+
+
+#if defined(VBOX_VFS_SOLARIS_10U6)
+/*
+ * HERE JOE.. this may need more logic, need to look at other file systems
+ */
+static int
+sffs_pathconf(
+ vnode_t *vp,
+ int cmd,
+ ulong_t *valp,
+ cred_t *cr)
+{
+ return (fs_pathconf(vp, cmd, valp, cr));
+}
+#else
+/*
+ * HERE JOE.. this may need more logic, need to look at other file systems
+ */
+static int
+sffs_pathconf(
+ vnode_t *vp,
+ int cmd,
+ ulong_t *valp,
+ cred_t *cr,
+ caller_context_t *ct)
+{
+ return (fs_pathconf(vp, cmd, valp, cr, ct));
+}
+#endif
+
+static int
+sffs_getattr(
+ vnode_t *vp,
+ vattr_t *vap,
+ int flags,
+ cred_t *cred,
+ caller_context_t *ct)
+{
+ sfnode_t *node = VN2SFN(vp);
+ sffs_data_t *sffs = node->sf_sffs;
+ mode_t mode;
+ int error = 0;
+
+ mutex_enter(&sffs_lock);
+ vap->va_type = vp->v_type;
+ vap->va_uid = sffs->sf_handle->sf_uid;
+ vap->va_gid = sffs->sf_handle->sf_gid;
+ vap->va_fsid = sffs->sf_vfsp->vfs_dev;
+ vap->va_nodeid = node->sf_ino;
+ vap->va_nlink = 1;
+ vap->va_rdev = sffs->sf_vfsp->vfs_dev;
+ vap->va_seq = 0;
+
+ if (!sfnode_stat_cached(node)) {
+ error = sfnode_update_stat_cache(node);
+ if (error != 0)
+ goto done;
+ }
+
+ vap->va_atime = node->sf_stat.sf_atime;
+ vap->va_mtime = node->sf_stat.sf_mtime;
+ vap->va_ctime = node->sf_stat.sf_ctime;
+
+ mode = node->sf_stat.sf_mode;
+ vap->va_mode = mode & MODEMASK;
+
+ vap->va_size = node->sf_stat.sf_size;
+ vap->va_blksize = 512;
+ vap->va_nblocks = (node->sf_stat.sf_alloc + 511) / 512;
+
+done:
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+static int
+sffs_setattr(
+ vnode_t *vp,
+ vattr_t *vap,
+ int flags,
+ cred_t *cred,
+ caller_context_t *ct)
+{
+ sfnode_t *node = VN2SFN(vp);
+ int error;
+ mode_t mode;
+
+ mode = vap->va_mode;
+ if (vp->v_type == VREG)
+ mode |= S_IFREG;
+ else if (vp->v_type == VDIR)
+ mode |= S_IFDIR;
+ else if (vp->v_type == VBLK)
+ mode |= S_IFBLK;
+ else if (vp->v_type == VCHR)
+ mode |= S_IFCHR;
+ else if (vp->v_type == VLNK)
+ mode |= S_IFLNK;
+ else if (vp->v_type == VFIFO)
+ mode |= S_IFIFO;
+ else if (vp->v_type == VSOCK)
+ mode |= S_IFSOCK;
+
+ mutex_enter(&sffs_lock);
+
+ sfnode_invalidate_stat_cache(node);
+ error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
+ vap->va_mask, mode, vap->va_atime, vap->va_mtime, vap->va_ctime);
+ if (error == ENOENT)
+ sfnode_make_stale(node);
+
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+static int
+sffs_space(
+ vnode_t *vp,
+ int cmd,
+ struct flock64 *bfp,
+ int flags,
+ offset_t off,
+ cred_t *cred,
+ caller_context_t *ct)
+{
+ sfnode_t *node = VN2SFN(vp);
+ int error;
+
+ /* we only support changing the length of the file */
+ if (bfp->l_whence != SEEK_SET || bfp->l_len != 0)
+ return ENOSYS;
+
+ mutex_enter(&sffs_lock);
+
+ sfnode_invalidate_stat_cache(node);
+
+ error = sfprov_set_size(node->sf_sffs->sf_handle, node->sf_path,
+ bfp->l_start);
+ if (error == ENOENT)
+ sfnode_make_stale(node);
+
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+sffs_read(
+ vnode_t *vp,
+ struct uio *uio,
+ int ioflag,
+ cred_t *cred,
+ caller_context_t *ct)
+{
+ sfnode_t *node = VN2SFN(vp);
+ int error = 0;
+ uint32_t bytes;
+ uint32_t done;
+ ulong_t offset;
+ ssize_t total;
+
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ if (vp->v_type != VREG)
+ return (EINVAL);
+ if (uio->uio_loffset >= MAXOFFSET_T)
+ return (0);
+ if (uio->uio_loffset < 0)
+ return (EINVAL);
+ total = uio->uio_resid;
+ if (total == 0)
+ return (0);
+
+ mutex_enter(&sffs_lock);
+ if (node->sf_file == NULL) {
+ ASSERT(node->sf_flag != ~0);
+ sfnode_open(node, node->sf_flag);
+ if (node->sf_file == NULL)
+ return (EBADF);
+ }
+
+ do {
+ offset = uio->uio_offset;
+ done = bytes = MIN(PAGESIZE, uio->uio_resid);
+ error = sfprov_read(node->sf_file, sffs_buffer, offset, &done);
+ if (error == 0 && done > 0)
+ error = uiomove(sffs_buffer, done, UIO_READ, uio);
+ } while (error == 0 && uio->uio_resid > 0 && done > 0);
+
+ mutex_exit(&sffs_lock);
+
+ /*
+ * a partial read is never an error
+ */
+ if (total != uio->uio_resid)
+ error = 0;
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+sffs_write(
+ vnode_t *vp,
+ struct uio *uiop,
+ int ioflag,
+ cred_t *cred,
+ caller_context_t *ct)
+{
+ sfnode_t *node = VN2SFN(vp);
+ int error = 0;
+ uint32_t bytes;
+ uint32_t done;
+ ulong_t offset;
+ ssize_t total;
+ rlim64_t limit = uiop->uio_llimit;
+
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ if (vp->v_type != VREG)
+ return (EINVAL);
+
+ /*
+ * We have to hold this lock for a long time to keep
+ * multiple FAPPEND writes from intermixing
+ */
+ mutex_enter(&sffs_lock);
+ if (node->sf_file == NULL) {
+ ASSERT(node->sf_flag != ~0);
+ sfnode_open(node, node->sf_flag);
+ if (node->sf_file == NULL)
+ return (EBADF);
+ }
+
+ sfnode_invalidate_stat_cache(node);
+
+ if (ioflag & FAPPEND) {
+ uint64_t endoffile;
+
+ error = sfprov_get_size(node->sf_sffs->sf_handle,
+ node->sf_path, &endoffile);
+ if (error == ENOENT)
+ sfnode_make_stale(node);
+ if (error != 0) {
+ mutex_exit(&sffs_lock);
+ return (error);
+ }
+ uiop->uio_loffset = endoffile;
+ }
+
+ if (vp->v_type != VREG || uiop->uio_loffset < 0) {
+ mutex_exit(&sffs_lock);
+ return (EINVAL);
+ }
+ if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
+ limit = MAXOFFSET_T;
+
+ if (uiop->uio_loffset >= limit) {
+ mutex_exit(&sffs_lock);
+ return (EFBIG);
+ }
+
+ if (uiop->uio_loffset >= MAXOFFSET_T) {
+ mutex_exit(&sffs_lock);
+ return (EFBIG);
+ }
+
+ total = uiop->uio_resid;
+ if (total == 0) {
+ mutex_exit(&sffs_lock);
+ return (0);
+ }
+
+ do {
+ offset = uiop->uio_offset;
+ bytes = MIN(PAGESIZE, uiop->uio_resid);
+ if (offset + bytes >= limit) {
+ if (offset >= limit) {
+ error = EFBIG;
+ break;
+ }
+ bytes = limit - offset;
+ }
+ error = uiomove(sffs_buffer, bytes, UIO_WRITE, uiop);
+ if (error != 0)
+ break;
+ done = bytes;
+ if (error == 0)
+ error = sfprov_write(node->sf_file, sffs_buffer,
+ offset, &done);
+ total -= done;
+ if (done != bytes) {
+ uiop->uio_resid += bytes - done;
+ break;
+ }
+ } while (error == 0 && uiop->uio_resid > 0 && done > 0);
+
+ mutex_exit(&sffs_lock);
+
+ /*
+ * A short write is never really an error.
+ */
+ if (total != uiop->uio_resid)
+ error = 0;
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+sffs_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+ sfnode_t *node = VN2SFN(vp);
+ int error;
+
+ mutex_enter(&sffs_lock);
+ error = sfnode_access(node, mode, cr);
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+/*
+ * Lookup an entry in a directory and create a new vnode if found.
+ */
+/* ARGSUSED3 */
+static int
+sffs_lookup(
+ vnode_t *dvp, /* the directory vnode */
+ char *name, /* the name of the file or directory */
+ vnode_t **vpp, /* the vnode we found or NULL */
+ struct pathname *pnp,
+ int flags,
+ vnode_t *rdir,
+ cred_t *cred,
+ caller_context_t *ct,
+ int *direntflags,
+ struct pathname *realpnp)
+{
+ int error;
+ sfnode_t *node;
+
+ /*
+ * dvp must be a directory
+ */
+ if (dvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * An empty component name or just "." means the directory itself.
+ * Don't do any further lookup or checking.
+ */
+ if (strcmp(name, "") == 0 || strcmp(name, ".") == 0) {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ /*
+ * Check permission to look at this directory. We always allow "..".
+ */
+ mutex_enter(&sffs_lock);
+ if (strcmp(name, "..") != 0) {
+ error = sfnode_access(VN2SFN(dvp), VEXEC, cred);
+ if (error) {
+ mutex_exit(&sffs_lock);
+ return (error);
+ }
+ }
+
+ /*
+ * Lookup the node.
+ */
+ node = sfnode_lookup(VN2SFN(dvp), name, VNON, 0, NULL, 0, NULL);
+ if (node != NULL)
+ *vpp = sfnode_get_vnode(node);
+ mutex_exit(&sffs_lock);
+ return ((node == NULL) ? ENOENT : 0);
+}
+
+/*ARGSUSED*/
+static int
+sffs_create(
+ vnode_t *dvp,
+ char *name,
+ struct vattr *vap,
+ vcexcl_t exclusive,
+ int mode,
+ vnode_t **vpp,
+ cred_t *cr,
+ int flag,
+ caller_context_t *ct,
+ vsecattr_t *vsecp)
+{
+ vnode_t *vp;
+ sfnode_t *node;
+ int error;
+
+ ASSERT(name != NULL);
+
+ /*
+ * this is used for regular files, not mkdir
+ */
+ if (vap->va_type == VDIR)
+ return (EISDIR);
+ if (vap->va_type != VREG)
+ return (EINVAL);
+
+ /*
+ * is this a pre-existing file?
+ */
+ error = sffs_lookup(dvp, name, &vp,
+ NULL, 0, NULL, cr, ct, NULL, NULL);
+ if (error == ENOENT)
+ vp = NULL;
+ else if (error != 0)
+ return (error);
+
+ /*
+ * Operation on a pre-existing file.
+ */
+ if (vp != NULL) {
+ if (exclusive == EXCL) {
+ VN_RELE(vp);
+ return (EEXIST);
+ }
+ if (vp->v_type == VDIR && (mode & VWRITE) == VWRITE) {
+ VN_RELE(vp);
+ return (EISDIR);
+ }
+
+ mutex_enter(&sffs_lock);
+ node = VN2SFN(vp);
+ error = sfnode_access(node, mode, cr);
+ if (error != 0) {
+ mutex_exit(&sffs_lock);
+ VN_RELE(vp);
+ return (error);
+ }
+
+ sfnode_invalidate_stat_cache(VN2SFN(dvp));
+
+ /*
+ * handle truncating an existing file
+ */
+ if (vp->v_type == VREG && (vap->va_mask & AT_SIZE) &&
+ vap->va_size == 0) {
+ sfnode_open(node, flag | FTRUNC);
+ if (node->sf_path == NULL) {
+ mutex_exit(&sffs_lock);
+ VN_RELE(vp);
+ return (ENOENT);
+ }
+ }
+ mutex_exit(&sffs_lock);
+ *vpp = vp;
+ return (0);
+ }
+
+ /*
+ * Create a new node. First check for a race creating it.
+ */
+ mutex_enter(&sffs_lock);
+ node = sfnode_lookup(VN2SFN(dvp), name, VNON, 0, NULL, 0, NULL);
+ if (node != NULL) {
+ mutex_exit(&sffs_lock);
+ return (EEXIST);
+ }
+
+ /*
+ * Doesn't exist yet and we have the lock, so create it.
+ */
+ sfnode_invalidate_stat_cache(VN2SFN(dvp));
+ int lookuperr;
+ node = sfnode_lookup(VN2SFN(dvp), name, VREG,
+ (vap->va_mask & AT_MODE) ? vap->va_mode : 0, NULL, 0, &lookuperr);
+
+ if (node && node->sf_parent)
+ sfnode_clear_dir_list(node->sf_parent);
+
+ mutex_exit(&sffs_lock);
+ if (node == NULL)
+ return (lookuperr);
+ *vpp = sfnode_get_vnode(node);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+sffs_mkdir(
+ vnode_t *dvp,
+ char *nm,
+ vattr_t *va,
+ vnode_t **vpp,
+ cred_t *cred,
+ caller_context_t *ct,
+ int flags,
+ vsecattr_t *vsecp)
+{
+ sfnode_t *node;
+ vnode_t *vp;
+ int error;
+
+ /*
+ * These should never happen
+ */
+ ASSERT(nm != NULL);
+ ASSERT(strcmp(nm, "") != 0);
+ ASSERT(strcmp(nm, ".") != 0);
+ ASSERT(strcmp(nm, "..") != 0);
+
+ /*
+ * Do an unlocked look up first
+ */
+ error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
+ if (error == 0) {
+ VN_RELE(vp);
+ return (EEXIST);
+ }
+ if (error != ENOENT)
+ return (error);
+
+ /*
+ * Must be able to write in current directory
+ */
+ mutex_enter(&sffs_lock);
+ error = sfnode_access(VN2SFN(dvp), VWRITE, cred);
+ if (error) {
+ mutex_exit(&sffs_lock);
+ return (error);
+ }
+
+ sfnode_invalidate_stat_cache(VN2SFN(dvp));
+ int lookuperr = EACCES;
+ node = sfnode_lookup(VN2SFN(dvp), nm, VDIR,
+ (va->va_mode & AT_MODE) ? va->va_mode : 0, NULL, 0, &lookuperr);
+
+ if (node && node->sf_parent)
+ sfnode_clear_dir_list(node->sf_parent);
+
+ mutex_exit(&sffs_lock);
+ if (node == NULL)
+ return (lookuperr);
+ *vpp = sfnode_get_vnode(node);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+sffs_rmdir(
+ struct vnode *dvp,
+ char *nm,
+ vnode_t *cdir,
+ cred_t *cred,
+ caller_context_t *ct,
+ int flags)
+{
+ sfnode_t *node;
+ vnode_t *vp;
+ int error;
+
+ /*
+ * Return error when removing . and ..
+ */
+ if (strcmp(nm, ".") == 0 || strcmp(nm, "") == 0)
+ return (EINVAL);
+ if (strcmp(nm, "..") == 0)
+ return (EEXIST);
+
+ error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
+ if (error)
+ return (error);
+ if (vp->v_type != VDIR) {
+ VN_RELE(vp);
+ return (ENOTDIR);
+ }
+
+#ifdef VBOXVFS_WITH_MMAP
+ if (vn_vfswlock(vp)) {
+ VN_RELE(vp);
+ return (EBUSY);
+ }
+#endif
+
+ if (vn_mountedvfs(vp)) {
+ VN_RELE(vp);
+ return (EBUSY);
+ }
+
+ node = VN2SFN(vp);
+
+ mutex_enter(&sffs_lock);
+ error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
+ if (error)
+ goto done;
+
+ /*
+ * If anything else is using this vnode, then fail the remove.
+ * Why? Windows hosts can't remove something that is open,
+ * so we have to sfprov_close() it first.
+ * There is no errno for this - since it's not a problem on UNIX,
+ * but EINVAL is the closest.
+ */
+ if (node->sf_file != NULL) {
+ if (vp->v_count > 1) {
+ error = EINVAL;
+ goto done;
+ }
+ (void)sfprov_close(node->sf_file);
+ node->sf_file = NULL;
+ }
+
+ /*
+ * Remove the directory on the host and mark the node as stale.
+ */
+ sfnode_invalidate_stat_cache(VN2SFN(dvp));
+ error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
+ if (error == ENOENT || error == 0)
+ sfnode_make_stale(node);
+
+ if (node->sf_parent)
+ sfnode_clear_dir_list(node->sf_parent);
+done:
+ mutex_exit(&sffs_lock);
+#ifdef VBOXVFS_WITH_MMAP
+ vn_vfsunlock(vp);
+#endif
+ VN_RELE(vp);
+ return (error);
+}
+
+
+#ifdef VBOXVFS_WITH_MMAP
+static caddr_t
+sffs_page_map(
+ page_t *ppage,
+ enum seg_rw segaccess)
+{
+ /* Use seg_kpm driver if possible (64-bit) */
+ if (kpm_enable)
+ return (hat_kpm_mapin(ppage, NULL));
+ ASSERT(segaccess == S_READ || segaccess == S_WRITE);
+ return (ppmapin(ppage, PROT_READ | ((segaccess == S_WRITE) ? PROT_WRITE : 0), (caddr_t)-1));
+}
+
+
+static void
+sffs_page_unmap(
+ page_t *ppage,
+ caddr_t addr)
+{
+ if (kpm_enable)
+ hat_kpm_mapout(ppage, NULL, addr);
+ else
+ ppmapout(addr);
+}
+
+
+/*
+ * Called when there's no page in the cache. This will create new page(s) and read
+ * the file data into it.
+ */
+static int
+sffs_readpages(
+ vnode_t *dvp,
+ offset_t off,
+ page_t *pagelist[],
+ size_t pagelistsize,
+ struct seg *segp,
+ caddr_t addr,
+ enum seg_rw segaccess)
+{
+ ASSERT(MUTEX_HELD(&sffs_lock));
+
+ int error = 0;
+ u_offset_t io_off, total;
+ size_t io_len;
+ page_t *ppages;
+ page_t *pcur;
+
+ sfnode_t *node = VN2SFN(dvp);
+ ASSERT(node);
+ ASSERT(node->sf_file);
+
+ if (pagelistsize == PAGESIZE)
+ {
+ io_off = off;
+ io_len = PAGESIZE;
+ ppages = page_create_va(dvp, io_off, io_len, PG_WAIT | PG_EXCL, segp, addr);
+ }
+ else
+ ppages = pvn_read_kluster(dvp, off, segp, addr, &io_off, &io_len, off, pagelistsize, 0);
+
+ /* If page already exists return success */
+ if (!ppages)
+ {
+ *pagelist = NULL;
+ return (0);
+ }
+
+ /*
+ * Map & read page-by-page.
+ */
+ total = io_off + io_len;
+ pcur = ppages;
+ while (io_off < total)
+ {
+ ASSERT3U(io_off, ==, pcur->p_offset);
+
+ caddr_t virtaddr = sffs_page_map(pcur, segaccess);
+ uint32_t bytes = PAGESIZE;
+ error = sfprov_read(node->sf_file, virtaddr, io_off, &bytes);
+ /*
+ * If we reuse pages without zero'ing them, one process can mmap() and read-past the length
+ * to read previously mmap'd contents (from possibly other processes).
+ */
+ if (error == 0 && bytes < PAGESIZE)
+ memset(virtaddr + bytes, 0, PAGESIZE - bytes);
+ sffs_page_unmap(pcur, virtaddr);
+ if (error != 0)
+ {
+ cmn_err(CE_WARN, "sffs_readpages: sfprov_read() failed. error=%d bytes=%u\n", error, bytes);
+ /* Get rid of all kluster pages read & bail. */
+ pvn_read_done(ppages, B_ERROR);
+ return (error);
+ }
+ pcur = pcur->p_next;
+ io_off += PAGESIZE;
+ }
+
+ /*
+ * Fill in the pagelist from kluster at the requested offset.
+ */
+ pvn_plist_init(ppages, pagelist, pagelistsize, off, io_len, segaccess);
+ ASSERT(pagelist == NULL || (*pagelist)->p_offset == off);
+ return (0);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_getpage(
+ vnode_t *dvp,
+ offset_t off,
+ size_t len,
+ uint_t *protp,
+ page_t *pagelist[],
+ size_t pagelistsize,
+ struct seg *segp,
+ caddr_t addr,
+ enum seg_rw segaccess,
+ cred_t *credp
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+ , caller_context_t *ct
+#endif
+ )
+{
+ int error = 0;
+ int is_recursive = 0;
+ page_t **pageliststart = pagelist;
+ sfnode_t *node = VN2SFN(dvp);
+ ASSERT(node);
+ ASSERT(node->sf_file);
+
+ if (segaccess == S_WRITE)
+ return (ENOSYS); /* Will this ever happen? */
+
+ /* Don't bother about faultahead for now. */
+ if (pagelist == NULL)
+ return (0);
+
+ if (len > pagelistsize)
+ len = pagelistsize;
+ else
+ len = P2ROUNDUP(len, PAGESIZE);
+ ASSERT(pagelistsize >= len);
+
+ if (protp)
+ *protp = PROT_ALL;
+
+ /*
+ * The buffer passed to sffs_write may be mmap'd so we may get a
+ * pagefault there, in which case we'll end up here with this thread
+ * already owning the mutex. Mutexes aren't recursive.
+ */
+ if (mutex_owner(&sffs_lock) == curthread)
+ is_recursive = 1;
+ else
+ mutex_enter(&sffs_lock);
+
+ /* Don't map pages past end of the file. */
+ if (off + len > node->sf_stat.sf_size + PAGEOFFSET)
+ {
+ if (!is_recursive)
+ mutex_exit(&sffs_lock);
+ return (EFAULT);
+ }
+
+ while (len > 0)
+ {
+ /*
+ * Look for pages in the requested offset range, or create them if we can't find any.
+ */
+ if ((*pagelist = page_lookup(dvp, off, SE_SHARED)) != NULL)
+ *(pagelist + 1) = NULL;
+ else if ((error = sffs_readpages(dvp, off, pagelist, pagelistsize, segp, addr, segaccess)) != 0)
+ {
+ while (pagelist > pageliststart)
+ page_unlock(*--pagelist);
+
+ *pagelist = NULL;
+ if (!is_recursive)
+ mutex_exit(&sffs_lock);
+ return (error);
+ }
+
+ while (*pagelist)
+ {
+ ASSERT3U((*pagelist)->p_offset, ==, off);
+ off += PAGESIZE;
+ addr += PAGESIZE;
+ if (len > 0)
+ {
+ ASSERT3U(len, >=, PAGESIZE);
+ len -= PAGESIZE;
+ }
+
+ ASSERT3U(pagelistsize, >=, PAGESIZE);
+ pagelistsize -= PAGESIZE;
+ pagelist++;
+ }
+ }
+
+ /*
+ * Fill the page list array with any pages left in the cache.
+ */
+ while ( pagelistsize > 0
+ && (*pagelist++ = page_lookup_nowait(dvp, off, SE_SHARED)))
+ {
+ off += PAGESIZE;
+ pagelistsize -= PAGESIZE;
+ }
+
+ *pagelist = NULL;
+ if (!is_recursive)
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_putpage(
+ vnode_t *dvp,
+ offset_t off,
+ size_t len,
+ int flags,
+ cred_t *credp
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+ , caller_context_t *ct
+#endif
+ )
+{
+ /*
+ * We don't support PROT_WRITE mmaps.
+ */
+ return (ENOSYS);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_discardpage(
+ vnode_t *dvp,
+ page_t *ppage,
+ u_offset_t *poff,
+ size_t *plen,
+ int flags,
+ cred_t *pcred)
+{
+ /*
+ * This would not get invoked i.e. via pvn_vplist_dirty() since we don't support
+ * PROT_WRITE mmaps and therefore will not have dirty pages.
+ */
+ pvn_write_done(ppage, B_INVAL | B_ERROR | B_FORCE);
+ return (0);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_map(
+ vnode_t *dvp,
+ offset_t off,
+ struct as *asp,
+ caddr_t *addrp,
+ size_t len,
+ uchar_t prot,
+ uchar_t maxprot,
+ uint_t flags,
+ cred_t *credp
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+ , caller_context_t *ct
+#endif
+ )
+{
+ /*
+ * Invocation: mmap()->smmap_common()->VOP_MAP()->sffs_map(). Once the
+ * segment driver creates the new segment via segvn_create(), it'll
+ * invoke down the line VOP_ADDMAP()->sffs_addmap()
+ */
+ int error = 0;
+ sfnode_t *node = VN2SFN(dvp);
+ ASSERT(node);
+ if ((flags & MAP_SHARED) && (prot & PROT_WRITE))
+ return (ENOTSUP);
+
+ if (off < 0 || len > MAXOFFSET_T - off)
+ return (ENXIO);
+
+ if (dvp->v_type != VREG)
+ return (ENODEV);
+
+ if (dvp->v_flag & VNOMAP)
+ return (ENOSYS);
+
+ if (vn_has_mandatory_locks(dvp, node->sf_stat.sf_mode))
+ return (EAGAIN);
+
+ mutex_enter(&sffs_lock);
+ as_rangelock(asp);
+
+#if defined(VBOX_VFS_SOLARIS_10U6)
+ if ((flags & MAP_FIXED) == 0)
+ {
+ if (g_fVBoxVFS_SolOldAddrMap)
+ g_VBoxVFS_SolAddrMap.MapAddr.pfnSol_map_addr_old(addrp, len, off, 1, flags);
+ else
+ g_VBoxVFS_SolAddrMap.MapAddr.pfnSol_map_addr(addrp, len, off, flags);
+ if (*addrp == NULL)
+ error = ENOMEM;
+ }
+ else
+ as_unmap(asp, *addrp, len); /* User specified address, remove any previous mappings */
+#else
+ if (g_fVBoxVFS_SolOldAddrMap)
+ error = g_VBoxVFS_SolAddrMap.ChooseAddr.pfnSol_choose_addr_old(asp, addrp, len, off, 1, flags);
+ else
+ error = g_VBoxVFS_SolAddrMap.ChooseAddr.pfnSol_choose_addr(asp, addrp, len, off, flags);
+#endif
+
+ if (error)
+ {
+ as_rangeunlock(asp);
+ mutex_exit(&sffs_lock);
+ return (error);
+ }
+
+ segvn_crargs_t vnodeargs;
+ memset(&vnodeargs, 0, sizeof(vnodeargs));
+ vnodeargs.vp = dvp;
+ vnodeargs.cred = credp;
+ vnodeargs.offset = off;
+ vnodeargs.type = flags & MAP_TYPE;
+ vnodeargs.prot = prot;
+ vnodeargs.maxprot = maxprot;
+ vnodeargs.flags = flags & ~MAP_TYPE;
+ vnodeargs.amp = NULL; /* anon. mapping */
+ vnodeargs.szc = 0; /* preferred page size code */
+ vnodeargs.lgrp_mem_policy_flags = 0;
+
+ error = as_map(asp, *addrp, len, segvn_create, &vnodeargs);
+
+ as_rangeunlock(asp);
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_addmap(
+ vnode_t *dvp,
+ offset_t off,
+ struct as *asp,
+ caddr_t addr,
+ size_t len,
+ uchar_t prot,
+ uchar_t maxprot,
+ uint_t flags,
+ cred_t *credp
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+ , caller_context_t *ct
+#endif
+ )
+{
+ if (dvp->v_flag & VNOMAP)
+ return (ENOSYS);
+ return (0);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_delmap(
+ vnode_t *dvp,
+ offset_t off,
+ struct as *asp,
+ caddr_t addr,
+ size_t len,
+ uint_t prot,
+ uint_t maxprot,
+ uint_t flags,
+ cred_t *credp
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+ , caller_context_t *ct
+#endif
+ )
+{
+ if (dvp->v_flag & VNOMAP)
+ return (ENOSYS);
+
+ return (0);
+}
+#endif /* VBOXVFS_WITH_MMAP */
+
+
+/*ARGSUSED*/
+static int
+sffs_readlink(
+ vnode_t *vp,
+ uio_t *uiop,
+ cred_t *cred
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+ ,
+ caller_context_t *ct
+#endif
+ )
+{
+ sfnode_t *node;
+ int error = 0;
+ char *target = NULL;
+
+ if (uiop->uio_iovcnt != 1)
+ return (EINVAL);
+
+ if (vp->v_type != VLNK)
+ return (EINVAL);
+
+ mutex_enter(&sffs_lock);
+ node = VN2SFN(vp);
+
+ target = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ error = sfprov_readlink(node->sf_sffs->sf_handle, node->sf_path, target,
+ MAXPATHLEN);
+ if (error)
+ goto done;
+
+ error = uiomove(target, strlen(target), UIO_READ, uiop);
+
+done:
+ mutex_exit(&sffs_lock);
+ if (target)
+ kmem_free(target, MAXPATHLEN);
+ return (error);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_symlink(
+ vnode_t *dvp,
+ char *linkname,
+ vattr_t *vap,
+ char *target,
+ cred_t *cred
+#if !defined(VBOX_VFS_SOLARIS_10U6)
+ ,
+ caller_context_t *ct,
+ int flags
+#endif
+ )
+{
+ sfnode_t *dir;
+ sfnode_t *node;
+ sffs_stat_t stat;
+ int error = 0;
+ char *fullpath;
+
+ /*
+ * These should never happen
+ */
+ ASSERT(linkname != NULL);
+ ASSERT(strcmp(linkname, "") != 0);
+ ASSERT(strcmp(linkname, ".") != 0);
+ ASSERT(strcmp(linkname, "..") != 0);
+
+ /*
+ * Basic checks.
+ */
+ if (vap->va_type != VLNK)
+ return (EINVAL);
+
+ mutex_enter(&sffs_lock);
+
+ if (sfnode_lookup(VN2SFN(dvp), linkname, VNON, 0, NULL, 0, NULL) !=
+ NULL) {
+ error = EEXIST;
+ goto done;
+ }
+
+ dir = VN2SFN(dvp);
+ error = sfnode_access(dir, VWRITE, cred);
+ if (error)
+ goto done;
+
+ /*
+ * Create symlink. Note that we ignore vap->va_mode because generally
+ * we can't change the attributes of the symlink itself.
+ */
+ fullpath = sfnode_construct_path(dir, linkname);
+ error = sfprov_symlink(dir->sf_sffs->sf_handle, fullpath, target,
+ &stat);
+ kmem_free(fullpath, strlen(fullpath) + 1);
+ if (error)
+ goto done;
+
+ node = sfnode_lookup(dir, linkname, VLNK, 0, &stat,
+ sfnode_cur_time_usec(), NULL);
+
+ sfnode_invalidate_stat_cache(dir);
+ sfnode_clear_dir_list(dir);
+
+done:
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_remove(
+ vnode_t *dvp,
+ char *name,
+ cred_t *cred,
+ caller_context_t *ct,
+ int flags)
+{
+ vnode_t *vp;
+ sfnode_t *node;
+ int error;
+
+ /*
+ * These should never happen
+ */
+ ASSERT(name != NULL);
+ ASSERT(strcmp(name, "..") != 0);
+
+ error = sffs_lookup(dvp, name, &vp,
+ NULL, 0, NULL, cred, ct, NULL, NULL);
+ if (error)
+ return (error);
+ node = VN2SFN(vp);
+
+ mutex_enter(&sffs_lock);
+ error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
+ if (error)
+ goto done;
+
+ /*
+ * If anything else is using this vnode, then fail the remove.
+ * Why? Windows hosts can't sfprov_remove() a file that is open,
+ * so we have to sfprov_close() it first.
+ * There is no errno for this - since it's not a problem on UNIX,
+ * but ETXTBSY is the closest.
+ */
+ if (node->sf_file != NULL) {
+ if (vp->v_count > 1) {
+ error = ETXTBSY;
+ goto done;
+ }
+ (void)sfprov_close(node->sf_file);
+ node->sf_file = NULL;
+ }
+
+ /*
+ * Remove the file on the host and mark the node as stale.
+ */
+ sfnode_invalidate_stat_cache(VN2SFN(dvp));
+
+ error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path,
+ node->sf_type == VLNK);
+ if (error == ENOENT || error == 0)
+ sfnode_make_stale(node);
+
+ if (node->sf_parent)
+ sfnode_clear_dir_list(node->sf_parent);
+done:
+ mutex_exit(&sffs_lock);
+ VN_RELE(vp);
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+sffs_rename(
+ vnode_t *old_dir,
+ char *old_nm,
+ vnode_t *new_dir,
+ char *new_nm,
+ cred_t *cred,
+ caller_context_t *ct,
+ int flags)
+{
+ char *newpath;
+ int error;
+ sfnode_t *node;
+
+ if (strcmp(new_nm, "") == 0 ||
+ strcmp(new_nm, ".") == 0 ||
+ strcmp(new_nm, "..") == 0 ||
+ strcmp(old_nm, "") == 0 ||
+ strcmp(old_nm, ".") == 0 ||
+ strcmp(old_nm, "..") == 0)
+ return (EINVAL);
+
+ /*
+ * make sure we have permission to do the rename
+ */
+ mutex_enter(&sffs_lock);
+ error = sfnode_access(VN2SFN(old_dir), VEXEC | VWRITE, cred);
+ if (error == 0 && new_dir != old_dir)
+ error = sfnode_access(VN2SFN(new_dir), VEXEC | VWRITE, cred);
+ if (error)
+ goto done;
+
+ node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON, 0, NULL, 0, NULL);
+ if (node == NULL) {
+ error = ENOENT;
+ goto done;
+ }
+
+ /*
+ * Rename the file on the host and in our caches.
+ */
+ sfnode_invalidate_stat_cache(node);
+ sfnode_invalidate_stat_cache(VN2SFN(old_dir));
+ sfnode_invalidate_stat_cache(VN2SFN(new_dir));
+
+ newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
+ error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
+ node->sf_type == VDIR);
+ if (error == 0)
+ sfnode_rename(node, VN2SFN(new_dir), newpath);
+ else {
+ kmem_free(newpath, strlen(newpath) + 1);
+ if (error == ENOENT)
+ sfnode_make_stale(node);
+ }
+done:
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+
+/*ARGSUSED*/
+static int
+sffs_fsync(vnode_t *vp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ sfnode_t *node;
+ int error;
+
+ /*
+ * Ask the host to sync any data it may have cached for open files.
+ */
+ mutex_enter(&sffs_lock);
+ node = VN2SFN(vp);
+ if (node->sf_file == NULL)
+ error = EBADF;
+ else if (node->sf_sffs->sf_fsync)
+ error = sfprov_fsync(node->sf_file);
+ else
+ error = 0;
+ mutex_exit(&sffs_lock);
+ return (error);
+}
+
+/*
+ * This may be the last reference, possibly time to close the file and
+ * destroy the vnode. If the sfnode is stale, we'll destroy that too.
+ */
+/*ARGSUSED*/
+static void
+#if defined(VBOX_VFS_SOLARIS_10U6)
+sffs_inactive(vnode_t *vp, cred_t *cr)
+#else
+sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+#endif
+{
+ sfnode_t *node;
+
+ /*
+ * nothing to do if this isn't the last use
+ */
+ mutex_enter(&sffs_lock);
+ node = VN2SFN(vp);
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ --vp->v_count;
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&sffs_lock);
+ return;
+ }
+
+ if (vn_has_cached_data(vp)) {
+#ifdef VBOXVFS_WITH_MMAP
+ /* We're fine with releasing the vnode lock here as we should be covered by the sffs_lock */
+ mutex_exit(&vp->v_lock);
+ /* We won't have any dirty pages, this will just invalidate (destroy) the pages and move it to the cachelist. */
+ pvn_vplist_dirty(vp, 0 /* offset */, sffs_discardpage, B_INVAL, cr);
+ mutex_enter(&vp->v_lock);
+#else
+ panic("sffs_inactive() found cached data");
+#endif
+ }
+
+ /*
+ * destroy the vnode
+ */
+ node->sf_vnode = NULL;
+ mutex_exit(&vp->v_lock);
+ vn_invalid(vp);
+ vn_free(vp);
+ LogFlowFunc((" %s vnode cleared\n", node->sf_path));
+
+ /*
+ * Close the sf_file for the node.
+ */
+ if (node->sf_file != NULL) {
+ (void)sfprov_close(node->sf_file);
+ node->sf_file = NULL;
+ }
+
+ /*
+ * Free the directory entries for the node. This should normally
+ * have been taken care of in sffs_close(), but better safe than
+ * sorry.
+ */
+ sfnode_clear_dir_list(node);
+
+ /*
+ * If the node is stale, we can also destroy it.
+ */
+ if (node->sf_is_stale && node->sf_children == 0)
+ sfnode_destroy(node);
+
+ mutex_exit(&sffs_lock);
+ return;
+}
+
+/*
+ * All the work for this is really done in sffs_lookup().
+ */
+/*ARGSUSED*/
+static int
+sffs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ sfnode_t *node;
+ int error = 0;
+
+ mutex_enter(&sffs_lock);
+
+ node = VN2SFN(*vpp);
+ sfnode_open(node, flag);
+ if (node->sf_file == NULL)
+ error = EINVAL;
+ mutex_exit(&sffs_lock);
+
+ return (error);
+}
+
+/*
+ * All the work for this is really done in inactive.
+ */
+/*ARGSUSED*/
+static int
+sffs_close(
+ vnode_t *vp,
+ int flag,
+ int count,
+ offset_t offset,
+ cred_t *cr,
+ caller_context_t *ct)
+{
+ sfnode_t *node;
+
+ mutex_enter(&sffs_lock);
+ node = VN2SFN(vp);
+
+ /*
+ * Free the directory entries for the node. We do this on this call
+ * here because the directory node may not become inactive for a long
+ * time after the readdir is over. Case in point, if somebody cd's into
+ * the directory then it won't become inactive until they cd away again.
+ * In such a case we would end up with the directory listing not getting
+ * updated (i.e. the result of 'ls' always being the same) until they
+ * change the working directory.
+ */
+ sfnode_clear_dir_list(node);
+
+ sfnode_invalidate_stat_cache(node);
+
+ if (node->sf_file != NULL && vp->v_count <= 1)
+ {
+ (void)sfprov_close(node->sf_file);
+ node->sf_file = NULL;
+ }
+
+ mutex_exit(&sffs_lock);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+sffs_seek(vnode_t *v, offset_t o, offset_t *no, caller_context_t *ct)
+{
+ if (*no < 0 || *no > MAXOFFSET_T)
+ return (EINVAL);
+
+ if (v->v_type == VDIR)
+ {
+ sffs_dirents_t *cur_buf = VN2SFN(v)->sf_dir_list;
+ off_t offset = 0;
+
+ if (cur_buf == NULL)
+ return (0);
+
+ while (cur_buf != NULL) {
+ if (*no >= offset && *no <= offset + cur_buf->sf_len)
+ return (0);
+ offset += cur_buf->sf_len;
+ cur_buf = cur_buf->sf_next;
+ }
+ return (EINVAL);
+ }
+ return (0);
+}
+
+
+
+/*
+ * By returning an error for this, we prevent anything in sffs from
+ * being re-exported by NFS
+ */
+/* ARGSUSED */
+static int
+sffs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
+{
+ return (ENOTSUP);
+}
+
+/*
+ * vnode operations for regular files
+ */
+const fs_operation_def_t sffs_ops_template[] = {
+#if defined(VBOX_VFS_SOLARIS_10U6)
+ VOPNAME_ACCESS, sffs_access,
+ VOPNAME_CLOSE, sffs_close,
+ VOPNAME_CREATE, sffs_create,
+ VOPNAME_FID, sffs_fid,
+ VOPNAME_FSYNC, sffs_fsync,
+ VOPNAME_GETATTR, sffs_getattr,
+ VOPNAME_INACTIVE, sffs_inactive,
+ VOPNAME_LOOKUP, sffs_lookup,
+ VOPNAME_MKDIR, sffs_mkdir,
+ VOPNAME_OPEN, sffs_open,
+ VOPNAME_PATHCONF, sffs_pathconf,
+ VOPNAME_READ, sffs_read,
+ VOPNAME_READDIR, sffs_readdir,
+ VOPNAME_READLINK, sffs_readlink,
+ VOPNAME_REMOVE, sffs_remove,
+ VOPNAME_RENAME, sffs_rename,
+ VOPNAME_RMDIR, sffs_rmdir,
+ VOPNAME_SEEK, sffs_seek,
+ VOPNAME_SETATTR, sffs_setattr,
+ VOPNAME_SPACE, sffs_space,
+ VOPNAME_SYMLINK, sffs_symlink,
+ VOPNAME_WRITE, sffs_write,
+
+# ifdef VBOXVFS_WITH_MMAP
+ VOPNAME_MAP, sffs_map,
+ VOPNAME_ADDMAP, sffs_addmap,
+ VOPNAME_DELMAP, sffs_delmap,
+ VOPNAME_GETPAGE, sffs_getpage,
+ VOPNAME_PUTPAGE, sffs_putpage,
+# endif
+
+ NULL, NULL
+#else
+ VOPNAME_ACCESS, { .vop_access = sffs_access },
+ VOPNAME_CLOSE, { .vop_close = sffs_close },
+ VOPNAME_CREATE, { .vop_create = sffs_create },
+ VOPNAME_FID, { .vop_fid = sffs_fid },
+ VOPNAME_FSYNC, { .vop_fsync = sffs_fsync },
+ VOPNAME_GETATTR, { .vop_getattr = sffs_getattr },
+ VOPNAME_INACTIVE, { .vop_inactive = sffs_inactive },
+ VOPNAME_LOOKUP, { .vop_lookup = sffs_lookup },
+ VOPNAME_MKDIR, { .vop_mkdir = sffs_mkdir },
+ VOPNAME_OPEN, { .vop_open = sffs_open },
+ VOPNAME_PATHCONF, { .vop_pathconf = sffs_pathconf },
+ VOPNAME_READ, { .vop_read = sffs_read },
+ VOPNAME_READDIR, { .vop_readdir = sffs_readdir },
+ VOPNAME_READLINK, { .vop_readlink = sffs_readlink },
+ VOPNAME_REMOVE, { .vop_remove = sffs_remove },
+ VOPNAME_RENAME, { .vop_rename = sffs_rename },
+ VOPNAME_RMDIR, { .vop_rmdir = sffs_rmdir },
+ VOPNAME_SEEK, { .vop_seek = sffs_seek },
+ VOPNAME_SETATTR, { .vop_setattr = sffs_setattr },
+ VOPNAME_SPACE, { .vop_space = sffs_space },
+ VOPNAME_SYMLINK, { .vop_symlink = sffs_symlink },
+ VOPNAME_WRITE, { .vop_write = sffs_write },
+
+# ifdef VBOXVFS_WITH_MMAP
+ VOPNAME_MAP, { .vop_map = sffs_map },
+ VOPNAME_ADDMAP, { .vop_addmap = sffs_addmap },
+ VOPNAME_DELMAP, { .vop_delmap = sffs_delmap },
+ VOPNAME_GETPAGE, { .vop_getpage = sffs_getpage },
+ VOPNAME_PUTPAGE, { .vop_putpage = sffs_putpage },
+# endif
+
+ NULL, NULL
+#endif
+};
+
+/*
+ * Also, init and fini functions...
+ */
+int
+sffs_vnode_init(void)
+{
+ int err;
+
+ err = vn_make_ops("sffs", sffs_ops_template, &sffs_ops);
+ if (err)
+ return (err);
+
+ avl_create(&sfnodes, sfnode_compare, sizeof (sfnode_t),
+ offsetof(sfnode_t, sf_linkage));
+ avl_create(&stale_sfnodes, sfnode_compare, sizeof (sfnode_t),
+ offsetof(sfnode_t, sf_linkage));
+
+ sffs_buffer = kmem_alloc(PAGESIZE, KM_SLEEP);
+
+ return (0);
+}
+
+void
+sffs_vnode_fini(void)
+{
+ if (sffs_ops)
+ vn_freevnodeops(sffs_ops);
+ ASSERT(avl_first(&sfnodes) == NULL);
+ avl_destroy(&sfnodes);
+ if (sffs_buffer != NULL) {
+ kmem_free(sffs_buffer, PAGESIZE);
+ sffs_buffer = NULL;
+ }
+}
+
+/*
+ * Utility at unmount to get all nodes in that mounted filesystem removed.
+ */
+int
+sffs_purge(struct sffs_data *sffs)
+{
+ sfnode_t *node;
+ sfnode_t *prev;
+
+ /*
+ * Check that no vnodes are active.
+ */
+ if (sffs->sf_rootnode->v_count > 1)
+ return (-1);
+ for (node = avl_first(&sfnodes); node;
+ node = AVL_NEXT(&sfnodes, node)) {
+ if (node->sf_sffs == sffs && node->sf_vnode &&
+ node->sf_vnode != sffs->sf_rootnode)
+ return (-1);
+ }
+ for (node = avl_first(&stale_sfnodes); node;
+ node = AVL_NEXT(&stale_sfnodes, node)) {
+ if (node->sf_sffs == sffs && node->sf_vnode &&
+ node->sf_vnode != sffs->sf_rootnode)
+ return (-1);
+ }
+
+ /*
+ * All clear to destroy all node information. Since there are no
+ * vnodes, the make stale will cause deletion.
+ */
+ VN_RELE(sffs->sf_rootnode);
+ mutex_enter(&sffs_lock);
+ for (prev = NULL;;) {
+ if (prev == NULL)
+ node = avl_first(&sfnodes);
+ else
+ node = AVL_NEXT(&sfnodes, prev);
+
+ if (node == NULL)
+ break;
+
+ if (node->sf_sffs == sffs) {
+ if (node->sf_vnode != NULL)
+ panic("vboxfs: purge hit active vnode");
+ sfnode_make_stale(node);
+ } else {
+ prev = node;
+ }
+ }
+ mutex_exit(&sffs_lock);
+ return (0);
+}
+
+#if 0
+/* Debug helper functions */
+static void
+sfnode_print(sfnode_t *node)
+{
+ Log(("0x%p", node));
+ Log((" type=%s (%d)",
+ node->sf_type == VDIR ? "VDIR" :
+ node->sf_type == VNON ? "VNON" :
+ node->sf_type == VLNK ? "VLNK" :
+ node->sf_type == VREG ? "VREG" : "other", node->sf_type));
+ Log((" ino=%d", (uint_t)node->sf_ino));
+ Log((" path=%s", node->sf_path));
+ Log((" parent=0x%p", node->sf_parent));
+ if (node->sf_children)
+ Log((" children=%d", node->sf_children));
+ if (node->sf_vnode)
+ Log((" vnode=0x%p", node->sf_vnode));
+ Log(("%s\n", node->sf_is_stale ? " STALE" : ""));
+}
+
+static void
+sfnode_list(void)
+{
+ sfnode_t *n;
+ for (n = avl_first(&sfnodes); n != NULL; n = AVL_NEXT(&sfnodes, n))
+ sfnode_print(n);
+ for (n = avl_first(&stale_sfnodes); n != NULL;
+ n = AVL_NEXT(&stale_sfnodes, n))
+ sfnode_print(n);
+}
+#endif
+
diff --git a/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
new file mode 100644
index 00000000..04e06225
--- /dev/null
+++ b/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.h
@@ -0,0 +1,99 @@
+/* $Id: vboxfs_vnode.h $ */
+/** @file
+ * VirtualBox File System for Solaris Guests, VNode header.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_vnode_h
+#define GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_vnode_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <sys/t_lock.h>
+#include <sys/avl.h>
+#include <vm/seg.h>
+#include <vm/seg_vn.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * sfnode is the file system dependent vnode data for vboxsf.
+ * sfnode's also track all files ever accessed, both open and closed.
+ * It duplicates some of the information in vnode, since it holds
+ * information for files that may have been completely closed.
+ *
+ * The sfnode_t's are stored in an AVL tree sorted by:
+ * sf_sffs, sf_path
+ */
+typedef struct sfnode {
+ avl_node_t sf_linkage; /* AVL tree linkage */
+ struct sffs_data *sf_sffs; /* containing mounted file system */
+ char *sf_path; /* full pathname to file or dir */
+ uint64_t sf_ino; /* assigned unique ID number */
+ vnode_t *sf_vnode; /* vnode if active */
+ sfp_file_t *sf_file; /* non NULL if open */
+ int sf_flag; /* last opened file-mode. */
+ struct sfnode *sf_parent; /* parent sfnode of this one */
+ uint16_t sf_children; /* number of children sfnodes */
+ uint8_t sf_type; /* VDIR or VREG */
+ uint8_t sf_is_stale; /* this is stale and should be purged */
+ sffs_stat_t sf_stat; /* cached file attrs for this node */
+ uint64_t sf_stat_time; /* last-modified time of sf_stat */
+ sffs_dirents_t *sf_dir_list; /* list of entries for this directory */
+} sfnode_t;
+
+#define VN2SFN(vp) ((sfnode_t *)(vp)->v_data)
+
+#ifdef _KERNEL
+extern int sffs_vnode_init(void);
+extern void sffs_vnode_fini(void);
+extern sfnode_t *sfnode_make(struct sffs_data *, char *, vtype_t, sfp_file_t *,
+ sfnode_t *parent, sffs_stat_t *, uint64_t stat_time);
+extern vnode_t *sfnode_get_vnode(sfnode_t *);
+
+/*
+ * Purge all cached information about a shared file system at unmount
+ */
+extern int sffs_purge(struct sffs_data *);
+
+extern kmutex_t sffs_lock;
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GA_INCLUDED_SRC_solaris_SharedFolders_vboxfs_vnode_h */
diff --git a/src/VBox/Additions/solaris/Virtio/Makefile.kmk b/src/VBox/Additions/solaris/Virtio/Makefile.kmk
new file mode 100644
index 00000000..cd56717e
--- /dev/null
+++ b/src/VBox/Additions/solaris/Virtio/Makefile.kmk
@@ -0,0 +1,68 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for Solaris Virtio drivers.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#ifneq ($(KBUILD_HOST),solaris)
+#$(error "The Solaris guest additions can only be built on Solaris!")
+#endif
+
+#
+# virtionet ("virtnet" on Solaris) - The Virtio Network Drivers
+#
+SYSMODS.solaris += virtionet
+virtionet_NAME.solaris = virtnet
+virtionet_TEMPLATE = VBoxGuestR0Drv
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ virtionet_DEFS = VBOX_WITH_HGCM VBOX_SVN_REV=$(VBOX_SVN_REV) VBOX_VERSION_STRING="$(VBOX_VERSION_STRING)"
+else
+ virtionet_DEFS = VBOX_WITH_HGCM VBOX_SVN_REV=$(VBOX_SVN_REV) VBOX_VERSION_STRING=\"$(VBOX_VERSION_STRING)\"
+endif
+virtionet_DEPS = $(VBOX_SVN_REV_KMK)
+virtionet_INCS := .
+virtionet_SOURCES = \
+ Virtio-solaris.c \
+ VirtioPci-solaris.c \
+ VirtioRing-solaris.c \
+ VirtioNet-solaris.c
+# virtionet should resolve all IPRT bits from vboxguest.
+#virtionet_LIBS = \
+# $(VBOX_LIB_IPRT_GUEST_R0)
+virtionet_LDFLAGS += -N drv/vboxguest -N misc/mac
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/solaris/Virtio/Virtio-solaris.c b/src/VBox/Additions/solaris/Virtio/Virtio-solaris.c
new file mode 100644
index 00000000..7406f68d
--- /dev/null
+++ b/src/VBox/Additions/solaris/Virtio/Virtio-solaris.c
@@ -0,0 +1,224 @@
+/* $Id: Virtio-solaris.c $ */
+/** @file
+ * VirtualBox Guest Additions - Virtio Driver for Solaris.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "Virtio-solaris.h"
+
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <VBox/log.h>
+
+
+/**
+ * Virtio Attach routine that should be called from all Virtio drivers' attach
+ * routines.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (attach/resume).
+ * @param pDeviceOps Pointer to device ops structure.
+ * @param pHyperOps Pointer to hypervisor ops structure.
+ *
+ * @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
+ */
+int VirtioAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd, PVIRTIODEVICEOPS pDeviceOps, PVIRTIOHYPEROPS pHyperOps)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioAttach: pDip=%p enmCmd=%d pDeviceOps=%p pHyperOps=%p\n", pDip, enmCmd, pDeviceOps, pHyperOps));
+
+ AssertReturn(pDip, DDI_EINVAL);
+ AssertReturn(pDeviceOps, DDI_EINVAL);
+ AssertReturn(pHyperOps, DDI_EINVAL);
+
+ if (enmCmd != DDI_ATTACH)
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioAttach: Invalid enmCmd=%#x expected DDI_ATTACH\n", enmCmd));
+ return DDI_FAILURE;
+ }
+
+ int rc = DDI_FAILURE;
+ PVIRTIODEVICE pDevice = RTMemAllocZ(sizeof(VIRTIODEVICE));
+ if (RT_LIKELY(pDevice))
+ {
+ pDevice->pDip = pDip;
+ pDevice->pDeviceOps = pDeviceOps;
+ pDevice->pHyperOps = pHyperOps;
+
+ pDevice->pvDevice = pDevice->pDeviceOps->pfnAlloc(pDevice);
+ if (RT_LIKELY(pDevice->pvDevice))
+ {
+ pDevice->pvHyper = pDevice->pHyperOps->pfnAlloc(pDevice);
+ if (RT_LIKELY(pDevice->pvHyper))
+ {
+ /*
+ * Attach hypervisor interface and obtain features supported by host.
+ */
+ rc = pDevice->pHyperOps->pfnAttach(pDevice);
+ if (rc == DDI_SUCCESS)
+ {
+ pDevice->fHostFeatures = pDevice->pHyperOps->pfnGetFeatures(pDevice);
+ LogFlow((VIRTIOLOGNAME ":VirtioAttach: Host features=%#x\n", pDevice->fHostFeatures));
+
+ /*
+ * Attach the device type interface.
+ */
+ rc = pDevice->pDeviceOps->pfnAttach(pDevice);
+ if (rc == DDI_SUCCESS)
+ {
+ ddi_set_driver_private(pDip, pDevice);
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioAttach: DeviceOps pfnAttach failed. rc=%d\n", rc));
+
+ pDevice->pHyperOps->pfnDetach(pDevice);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioAttach: HyperOps pfnAttach failed. rc=%d\n", rc));
+
+ pDevice->pHyperOps->pfnFree(pDevice);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioAttach: HyperOps->pfnAlloc failed!\n"));
+
+ pDevice->pDeviceOps->pfnFree(pDevice);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioAttach: DeviceOps->pfnAlloc failed!\n"));
+
+ RTMemFree(pDevice);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioAttach: failed to alloc %u bytes for device structure.\n", sizeof(VIRTIODEVICE)));
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Virtio Detach routine that should be called from all Virtio drivers' detach
+ * routines.
+ *
+ * @param pDip The module structure instance.
+ * @param enmCmd Operation type (detach/suspend).
+ *
+ * @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
+ */
+int VirtioDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ PVIRTIODEVICE pDevice = ddi_get_driver_private(pDip);
+ if (RT_UNLIKELY(!pDevice))
+ return DDI_FAILURE;
+
+ if (enmCmd != DDI_DETACH)
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioDetach: Invalid enmCmd=%#x expected DDI_DETACH.\n", enmCmd));
+ return DDI_FAILURE;
+ }
+
+ int rc = pDevice->pDeviceOps->pfnDetach(pDevice);
+ if (rc == DDI_SUCCESS)
+ {
+ pDevice->pHyperOps->pfnDetach(pDevice);
+ pDevice->pDeviceOps->pfnFree(pDevice);
+ pDevice->pvDevice = NULL;
+ pDevice->pHyperOps->pfnFree(pDevice);
+ pDevice->pvHyper = NULL;
+
+ ddi_set_driver_private(pDevice->pDip, NULL);
+ RTMemFree(pDevice);
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioDetach: DeviceOps pfnDetach failed. rc=%d\n", rc));
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Allocates a Virtio Queue object and assigns it an index.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param Index Queue index.
+ *
+ * @return A pointer to a Virtio Queue instance.
+ */
+PVIRTIOQUEUE VirtioGetQueue(PVIRTIODEVICE pDevice, uint16_t Index)
+{
+ PVIRTIOQUEUE pQueue = RTMemAllocZ(sizeof(VIRTIOQUEUE));
+ if (RT_UNLIKELY(!pQueue))
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioGetQueue: failed to alloc memory for %u bytes.\n", sizeof(VIRTIOQUEUE)));
+ return NULL;
+ }
+
+ pQueue->QueueIndex = Index;
+ pQueue->pvData = pDevice->pHyperOps->pfnGetQueue(pDevice, pQueue);
+ if (RT_UNLIKELY(!pQueue->pvData))
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioGetQueue: HyperOps GetQueue failed!\n"));
+ RTMemFree(pQueue);
+ return NULL;
+ }
+
+ AssertReturn(pQueue->pQueue, NULL);
+ AssertReturn(pQueue->Ring.cDesc > 0, NULL);
+
+ /** @todo enable interrupt. */
+
+ return pQueue;
+}
+
+
+/**
+ * Puts a queue and destroys the instance.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param pQueue Pointer to the Virtio queue.
+ */
+void VirtioPutQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue)
+{
+ AssertReturnVoid(pDevice);
+ AssertReturnVoid(pQueue);
+
+ pDevice->pHyperOps->pfnPutQueue(pDevice, pQueue);
+ RTMemFree(pQueue);
+}
+
diff --git a/src/VBox/Additions/solaris/Virtio/Virtio-solaris.h b/src/VBox/Additions/solaris/Virtio/Virtio-solaris.h
new file mode 100644
index 00000000..9badca92
--- /dev/null
+++ b/src/VBox/Additions/solaris/Virtio/Virtio-solaris.h
@@ -0,0 +1,205 @@
+/* $Id: Virtio-solaris.h $ */
+/** @file
+ * VirtualBox Guest Additions: Virtio Driver for Solaris, header.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_solaris_Virtio_Virtio_solaris_h
+#define GA_INCLUDED_SRC_solaris_Virtio_Virtio_solaris_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <sys/sunddi.h>
+
+/** Release log descriptive prefix. */
+#define VIRTIOLOGNAME "Virtio"
+/** Buffer continues via the Next field */
+#define VIRTIO_FLAGS_RING_DESC_NEXT RT_BIT(0)
+/** Buffer is write-only, else is read-only. */
+#define VIRTIO_FLAGS_RING_DESC_WRITE RT_BIT(1)
+/** Indirect buffer (buffer contains list of buffer descriptors) */
+#define VIRTIO_FLAGS_RING_DESC_INDIRECT RT_BIT(2)
+
+/* Values from our Virtio.h */
+#define VIRTIO_PCI_STATUS_ACK 0x01
+#define VIRTIO_PCI_STATUS_DRV 0x02
+#define VIRTIO_PCI_STATUS_DRV_OK 0x04
+#define VIRTIO_PCI_STATUS_FAILED 0x80
+
+/**
+ * The ring descriptor table refers to the buffers the guest is using for the
+ * device.
+ */
+struct VirtioRingDesc
+{
+ uint64_t AddrBuf; /* Physical address of buffer. */
+ uint32_t cbBuf; /* Length of the buffer in bytes. */
+ uint16_t fFlags; /* Flags of the next buffer. */
+ uint16_t Next; /* Index of the next buffer. */
+};
+typedef struct VirtioRingDesc VIRTIORINGDESC;
+typedef VIRTIORINGDESC *PVIRTIORINGDESC;
+
+/**
+ * The available ring refers to what descriptors are being offered to the
+ * device.
+ */
+struct VirtioRingAvail
+{
+ uint16_t fFlags; /* Interrupt supression flag. */
+ uint16_t Index; /* Index of available ring. */
+ uint16_t aRings[1]; /* Array of indices into descriptor table. */
+};
+typedef struct VirtioRingAvail VIRTIORINGAVAIL;
+typedef VIRTIORINGAVAIL *PVIRTIORINGAVAIL;
+
+/**
+ * The used ring refers to the buffers the device is done using them. The
+ * element is a pair-descriptor refers to the buffer once the device is done
+ * with the buffer.
+ */
+struct VirtioRingUsedElem
+{
+ uint32_t Index; /* Index of start of used descriptor chain. */
+ uint32_t cbElem; /* Number of bytes written into the buffer. */
+};
+typedef struct VirtioRingUsedElem VIRTIORINGUSEDELEM;
+typedef VIRTIORINGUSEDELEM *PVIRTIORINGUSEDELEM;
+
+/**
+ * The Virtio Ring which contains the descriptors.
+ */
+struct VirtioRing
+{
+ uint_t cDesc; /* Number of descriptors. */
+ PVIRTIORINGDESC pRingDesc; /* Pointer to ring descriptor. */
+ PVIRTIORINGAVAIL pRingAvail; /* Pointer to available ring. */
+ PVIRTIORINGUSEDELEM pRingUsedElem; /* Pointer to used ring element. */
+};
+typedef struct VirtioRing VIRTIORING;
+typedef VIRTIORING *PVIRTIORING;
+
+struct VirtioDevice;
+struct VirtioQueue;
+
+typedef void *(*PFNVIRTIOALLOC)(struct VirtioDevice *pDevice);
+typedef void (*PFNVIRTIOFREE)(struct VirtioDevice *pDevice);
+typedef int (*PFNVIRTIOATTACH)(struct VirtioDevice *pDevice);
+typedef int (*PFNVIRTIODETACH)(struct VirtioDevice *pDevice);
+typedef uint32_t (*PFNVIRTIOGETFEATURES)(struct VirtioDevice *pDevice);
+typedef void (*PFNVIRTIOSETFEATURES)(struct VirtioDevice *pDevice, uint32_t fFeatures);
+typedef void (*PFNVIRTIOGET)(struct VirtioDevice *pDevice, off_t off, void *pv, size_t cb);
+typedef void (*PFNVIRTIOSET)(struct VirtioDevice *pDevice, off_t off, void *pv, size_t cb);
+typedef void *(*PFNVIRTIOGETQUEUE)(struct VirtioDevice *pDevice, struct VirtioQueue *pQueue);
+typedef void (*PFNVIRTIOPUTQUEUE)(struct VirtioDevice *pDevice, struct VirtioQueue *pQueue);
+typedef int (*PFNVIRTIONOTIFYQUEUE)(struct VirtioDevice *pDevice, struct VirtioQueue *pQueue);
+typedef void (*PFNVIRTIOSETSTATUS)(struct VirtioDevice *pDevice, uint8_t Status);
+
+/**
+ * Virtio device operations.
+ */
+struct VirtioDeviceOps
+{
+ PFNVIRTIOALLOC pfnAlloc; /* Device alloc. */
+ PFNVIRTIOFREE pfnFree; /* Device free. */
+ PFNVIRTIOATTACH pfnAttach; /* Device attach. */
+ PFNVIRTIODETACH pfnDetach; /* Device detach. */
+};
+typedef struct VirtioDeviceOps VIRTIODEVICEOPS;
+typedef VIRTIODEVICEOPS *PVIRTIODEVICEOPS;
+
+/**
+ * Hypervisor access operations.
+ */
+struct VirtioHyperOps
+{
+ PFNVIRTIOALLOC pfnAlloc; /* Hypervisor alloc. */
+ PFNVIRTIOFREE pfnFree; /* Hypervisor free */
+ PFNVIRTIOATTACH pfnAttach; /* Hypervisor attach. */
+ PFNVIRTIODETACH pfnDetach; /* Hypervisor detach. */
+ PFNVIRTIOGETFEATURES pfnGetFeatures; /* Hypervisor get features. */
+ PFNVIRTIOSETFEATURES pfnSetFeatures; /* Hypervisor set features. */
+ PFNVIRTIONOTIFYQUEUE pfnNotifyQueue; /* Hypervisor notify queue. */
+ PFNVIRTIOGET pfnGet; /* Hypervisor get. */
+ PFNVIRTIOSET pfnSet; /* Hypervisor set. */
+ PFNVIRTIOGETQUEUE pfnGetQueue; /* Hypervisor get queue. */
+ PFNVIRTIOPUTQUEUE pfnPutQueue; /* Hypervisor put queue. */
+ PFNVIRTIOSETSTATUS pfnSetStatus; /* Hypervisor set status. */
+};
+typedef struct VirtioHyperOps VIRTIOHYPEROPS;
+typedef VIRTIOHYPEROPS *PVIRTIOHYPEROPS;
+
+/**
+ * Virtio Queue into which buffers are posted.
+ */
+struct VirtioQueue
+{
+ VIRTIORING Ring; /* Ring buffer of this queue. */
+ uint16_t cBufs; /* Number of pushed, unnotified buffers. */
+ uint16_t FreeHeadIndex; /* Index of head of free list. */
+ uint16_t QueueIndex; /* Index of this queue. */
+ caddr_t pQueue; /* Allocated DMA region for queue. */
+ void *pvData; /* Queue private data. */
+};
+typedef struct VirtioQueue VIRTIOQUEUE;
+typedef VIRTIOQUEUE *PVIRTIOQUEUE;
+
+/**
+ * Virtio device descriptor, common to all Virtio devices.
+ */
+struct VirtioDevice
+{
+ dev_info_t *pDip; /* OS device info. */
+ PVIRTIODEVICEOPS pDeviceOps; /* Device hooks. */
+ void *pvDevice; /* Device opaque data. */
+ PVIRTIOHYPEROPS pHyperOps; /* Hypervisor hooks. */
+ void *pvHyper; /* Hypervisor opaque data. */
+ uint32_t fHostFeatures; /* Features provided by the host. */
+};
+typedef struct VirtioDevice VIRTIODEVICE;
+typedef VIRTIODEVICE *PVIRTIODEVICE;
+
+
+int VirtioAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd, PVIRTIODEVICEOPS pDeviceOps, PVIRTIOHYPEROPS pHyperOps);
+int VirtioDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+
+PVIRTIOQUEUE VirtioGetQueue(PVIRTIODEVICE pDevice, uint16_t Index);
+void VirtioPutQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue);
+
+void VirtioRingInit(PVIRTIOQUEUE pQueue, uint_t cDescs, caddr_t virtBuf, ulong_t Align);
+int VirtioRingPush(PVIRTIOQUEUE pQueue, paddr_t physBuf, uint32_t cbBuf, uint16_t fFlags);
+size_t VirtioRingSize(uint64_t cElements, ulong_t Align);
+
+#endif /* !GA_INCLUDED_SRC_solaris_Virtio_Virtio_solaris_h */
+
diff --git a/src/VBox/Additions/solaris/Virtio/VirtioNet-solaris.c b/src/VBox/Additions/solaris/Virtio/VirtioNet-solaris.c
new file mode 100644
index 00000000..dee46ad7
--- /dev/null
+++ b/src/VBox/Additions/solaris/Virtio/VirtioNet-solaris.c
@@ -0,0 +1,852 @@
+/* $Id: VirtioNet-solaris.c $ */
+/** @file
+ * VirtualBox Guest Additions - Virtio Network Driver for Solaris.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "Virtio-solaris.h"
+#include "VirtioPci-solaris.h"
+
+#include <sys/conf.h>
+#include <sys/sunddi.h>
+#include <sys/mac_provider.h>
+#include <sys/strsun.h>
+#include <sys/cmn_err.h>
+
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+#include <iprt/mem.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define DEVICE_NAME "virtnet"
+/** The module descriptions as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox VirtioNet"
+
+/** Copied from "mac_ether.h" - why the heck is this not public?? All Solaris
+ * mac clients use it... */
+#define MAC_PLUGIN_IDENT_ETHER "mac_ether"
+
+/* Copied from our Virtio Device emulation. */
+#define VIRTIO_NET_CSUM 0x00000001 /* Host handles pkts w/ partial csum */
+#define VIRTIO_NET_GUEST_CSUM 0x00000002 /* Guest handles pkts w/ partial csum */
+#define VIRTIO_NET_MAC 0x00000020 /* Host has given MAC address. */
+#define VIRTIO_NET_GSO 0x00000040 /* Host handles pkts w/ any GSO type */
+#define VIRTIO_NET_GUEST_TSO4 0x00000080 /* Guest can handle TSOv4 in. */
+#define VIRTIO_NET_GUEST_TSO6 0x00000100 /* Guest can handle TSOv6 in. */
+#define VIRTIO_NET_GUEST_ECN 0x00000200 /* Guest can handle TSO[6] w/ ECN in. */
+#define VIRTIO_NET_GUEST_UFO 0x00000400 /* Guest can handle UFO in. */
+#define VIRTIO_NET_HOST_TSO4 0x00000800 /* Host can handle TSOv4 in. */
+#define VIRTIO_NET_HOST_TSO6 0x00001000 /* Host can handle TSOv6 in. */
+#define VIRTIO_NET_HOST_ECN 0x00002000 /* Host can handle TSO[6] w/ ECN in. */
+#define VIRTIO_NET_HOST_UFO 0x00004000 /* Host can handle UFO in. */
+#define VIRTIO_NET_MRG_RXBUF 0x00008000 /* Host can merge receive buffers. */
+#define VIRTIO_NET_STATUS 0x00010000 /* virtio_net_config.status available */
+#define VIRTIO_NET_CTRL_VQ 0x00020000 /* Control channel available */
+#define VIRTIO_NET_CTRL_RX 0x00040000 /* Control channel RX mode support */
+#define VIRTIO_NET_CTRL_VLAN 0x00080000 /* Control channel VLAN filtering */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void *VirtioNetDevAlloc(PVIRTIODEVICE pDevice);
+static void VirtioNetDevFree(PVIRTIODEVICE pDevice);
+static int VirtioNetDevAttach(PVIRTIODEVICE pDevice);
+static int VirtioNetDevDetach(PVIRTIODEVICE pDevice);
+
+static int VirtioNetAttach(dev_info_t *pDip, ddi_attach_cmd_t Cmd);
+static int VirtioNetDetach(dev_info_t *pDip, ddi_detach_cmd_t Cmd);
+
+static int VirtioNetStat(void *pvArg, uint_t cmdStat, uint64_t *pu64Val);
+static int VirtioNetStart(void *pvArg);
+static void VirtioNetStop(void *pvArg);
+static int VirtioNetSetPromisc(void *pvArg, boolean_t fPromiscOn);
+static int VirtioNetSetMulticast(void *pvArg, boolean_t fAdd, const uint8_t *pbMac);
+static int VirtioNetSetUnicast(void *pvArg, const uint8_t *pbMac);
+static boolean_t VirtioNetGetCapab(void *pvArg, mac_capab_t Capab, void *pvCapabData);
+static mblk_t *VirtioNetXmit(void *pvArg, mblk_t *pMsg);
+static uint_t VirtioNetISR(caddr_t addrArg);
+
+static int VirtioNetAttachQueues(PVIRTIODEVICE pDevice);
+static void VirtioNetDetachQueues(PVIRTIODEVICE pDevice);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Device operations for Virtio Net.
+ */
+VIRTIODEVICEOPS g_VirtioDeviceOpsNet =
+{
+ VirtioNetDevAlloc,
+ VirtioNetDevFree,
+ VirtioNetDevAttach,
+ VirtioNetDevDetach
+};
+
+/**
+ * virtio_net_t: Private data per Virtio Device.
+ */
+typedef struct virtio_net_t
+{
+ mac_handle_t hMac; /* Handle to the MAC layer. */
+ RTMAC MacAddr; /* MAC address. */
+ PVIRTIOQUEUE pRxQueue; /* Receive Queue. */
+ PVIRTIOQUEUE pTxQueue; /* Xmit Queue. */
+ PVIRTIOQUEUE pCtrlQueue; /* Control Queue. */
+ kmem_cache_t *pTxCache; /* TX buffer cache. */
+} virtio_net_t;
+
+/**
+ * virtio_txbuf_t: Virtio Net TX buffer.
+ */
+typedef struct virtio_net_txbuf_t
+{
+ ddi_dma_handle_t hDMA; /* DMA TX handle. */
+} virtio_net_txbuf_t;
+
+/*
+ * virtio_net_header_t: Virtio Net TX/RX buffer header.
+ */
+typedef struct virtio_net_header_t
+{
+ uint8_t u8Flags; /* Flags. */
+ uint8_t u8GSOType; /* GSO type. */
+ uint16_t u16HdrLen; /* Length of this header. */
+ uint16_t u16GSOSize; /* GSO length if applicable. */
+ uint16_t u16CSumStart; /* Checksum start.*/
+ uint16_t u16CSumOffset; /* Checksum offset.*/
+} virtio_net_header_t;
+
+/**
+ * MAC layer hooks for VirtioNet.
+ */
+static mac_callbacks_t g_VirtioNetCallbacks =
+{
+ MC_GETCAPAB, /* Mask of available extra hooks. */
+ VirtioNetStat,
+ VirtioNetStart,
+ VirtioNetStop,
+ VirtioNetSetPromisc,
+ VirtioNetSetMulticast,
+ VirtioNetSetUnicast,
+ VirtioNetXmit,
+ NULL, /* Reserved. */
+ NULL, /* IOCtl. */
+ VirtioNetGetCapab,
+};
+
+/**
+ * DMA transfer attributes for Xmit/Recv. buffers.
+ */
+static ddi_dma_attr_t g_VirtioNetBufDmaAttr =
+{
+ DMA_ATTR_V0, /* Version. */
+ 0, /* Lowest usable address. */
+ 0xffffffffffffffffULL, /* Highest usable address. */
+ 0x7fffffff, /* Maximum DMAable byte count. */
+ MMU_PAGESIZE, /* Alignment in bytes. */
+ 0x7ff, /* Bitmap of burst sizes */
+ 1, /* Minimum transfer. */
+ 0xffffffffU, /* Maximum transfer. */
+ 0xffffffffffffffffULL, /* Maximum segment length. */
+ 1, /* Maximum number of segments. */
+ 1, /* Granularity. */
+ 0 /* Flags (reserved). */
+};
+
+/**
+ * cb_ops: driver char/block entry points
+ */
+static struct cb_ops g_VirtioNetCbOps =
+{
+ nulldev, /* cb open */
+ nulldev, /* cb close */
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ nodev, /* cb read */
+ nodev, /* cb write */
+ nodev, /* cb ioctl */
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ NULL, /* streamtab */
+ D_MP, /* compat. flag */
+ CB_REV /* revision */
+};
+
+/**
+ * dev_ops: driver entry/exit and other ops.
+ */
+static struct dev_ops g_VirtioNetDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ NULL, /* get info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VirtioNetAttach,
+ VirtioNetDetach,
+ nodev, /* reset */
+ &g_VirtioNetCbOps,
+ (struct bus_ops *)0,
+ nodev /* power */
+};
+
+/**
+ * modldrv: export driver specifics to kernel
+ */
+static struct modldrv g_VirtioNetDriver =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VirtioNetDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VirtioNetModLinkage =
+{
+ MODREV_1, /* loadable module system revision */
+ {
+ &g_VirtioNetDriver, /* driver framework */
+ NULL /* terminate array of linkage structures */
+ }
+};
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":_init\n"));
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VirtioNetModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ LogRel((VIRTIOLOGNAME ":failed to disable autounloading!\n"));
+
+ /*
+ * Initialize IPRT.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize Solaris specific globals here.
+ */
+ mac_init_ops(&g_VirtioNetDevOps, DEVICE_NAME);
+ rc = mod_install(&g_VirtioNetModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((VIRTIOLOGNAME ":mod_install failed. rc=%d\n", rc));
+ mac_fini_ops(&g_VirtioNetDevOps);
+ RTR0Term();
+ return rc;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":failed to initialize IPRT (rc=%d)\n", rc));
+
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ int rc;
+ LogFlowFunc((VIRTIOLOGNAME ":_fini\n"));
+
+ rc = mod_remove(&g_VirtioNetModLinkage);
+ if (!rc)
+ {
+ mac_fini_ops(&g_VirtioNetDevOps);
+ RTR0Term();
+ }
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":_info\n"));
+
+ int rc = mod_info(&g_VirtioNetModLinkage, pModInfo);
+
+ LogFlow((VIRTIOLOGNAME ":_info returns %d\n", rc));
+ return rc;
+}
+
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param pDip The module structure instance.
+ * @param Cmd Operation type (attach/resume).
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetAttach(dev_info_t *pDip, ddi_attach_cmd_t Cmd)
+{
+ return VirtioAttach(pDip, Cmd, &g_VirtioDeviceOpsNet, &g_VirtioHyperOpsPci);
+}
+
+
+/**
+ * Detach entry point, to detach a device to the system or suspend it.
+ *
+ * @param pDip The module structure instance.
+ * @param Cmd Operation type (detach/suspend).
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetDetach(dev_info_t *pDip, ddi_detach_cmd_t Cmd)
+{
+ return VirtioDetach(pDip, Cmd);
+}
+
+
+/**
+ * Virtio Net TX buffer constructor for kmem_cache_create().
+ *
+ * @param pvBuf Pointer to the allocated buffer.
+ * @param pvArg Opaque private data.
+ * @param fFlags Propagated KM flag values.
+ *
+ * @return 0 on success, or -1 on failure.
+ */
+static int VirtioNetTxBufCreate(void *pvBuf, void *pvArg, int fFlags)
+{
+ virtio_net_txbuf_t *pTxBuf = pvBuf;
+ PVIRTIODEVICE pDevice = pvArg;
+
+ /** @todo ncookies handles? */
+ int rc = ddi_dma_alloc_handle(pDevice->pDip, &g_VirtioNetBufDmaAttr,
+ fFlags & KM_NOSLEEP ? DDI_DMA_DONTWAIT : DDI_DMA_SLEEP,
+ 0 /* Arg */, &pTxBuf->hDMA);
+ if (rc == DDI_SUCCESS)
+ return 0;
+ return -1;
+}
+
+
+/**
+ * Virtio Net TX buffer destructor for kmem_cache_create().
+ *
+ * @param pvBuf Pointer to the allocated buffer.
+ * @param pvArg
+ */
+static void VirtioNetTxBufDestroy(void *pvBuf, void *pvArg)
+{
+ NOREF(pvArg);
+ virtio_net_txbuf_t *pTxBuf = pvBuf;
+
+ ddi_dma_free_handle(&pTxBuf->hDMA);
+}
+
+
+/**
+ * Virtio Net private data allocation routine.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ *
+ * @return Allocated private data that must only be freed by calling
+ * VirtioNetDevFree().
+ */
+static void *VirtioNetDevAlloc(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioNetDevAlloc pDevice=%p\n", pDevice));
+
+ AssertReturn(pDevice, NULL);
+ virtio_net_t *pNet = RTMemAllocZ(sizeof(virtio_net_t));
+ if (RT_LIKELY(pNet))
+ {
+ /*
+ * Create a kernel memory cache for frequently allocated/deallocated
+ * buffers.
+ */
+ char szCachename[KSTAT_STRLEN];
+ RTStrPrintf(szCachename, sizeof(szCachename), "VirtioNet_Cache_%d", ddi_get_instance(pDevice->pDip));
+ pNet->pTxCache = kmem_cache_create(szCachename, /* Cache name */
+ sizeof(virtio_net_txbuf_t), /* Size of buffers in cache */
+ 0, /* Align */
+ VirtioNetTxBufCreate, /* Buffer constructor */
+ VirtioNetTxBufDestroy, /* Buffer destructor */
+ NULL, /* pfnReclaim */
+ pDevice, /* Private data */
+ NULL, /* "vmp", MBZ (man page) */
+ 0 /* "cflags", MBZ (man page) */
+ );
+ if (RT_LIKELY(pNet->pTxCache))
+ return pNet;
+ else
+ LogRel((VIRTIOLOGNAME ":kmem_cache_create failed.\n"));
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":failed to alloc %u bytes for Net instance.\n", sizeof(virtio_net_t)));
+
+ return NULL;
+}
+
+
+/**
+ * Virtio Net private data free routine.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ */
+static void VirtioNetDevFree(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioNetDevFree pDevice=%p\n", pDevice));
+ AssertReturnVoid(pDevice);
+
+ virtio_net_t *pNet = pDevice->pvDevice;
+ kmem_cache_destroy(pNet->pTxCache);
+ RTMemFree(pNet);
+ pDevice->pvDevice = NULL;
+}
+
+
+/**
+ * Virtio Net device attach rountine.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetDevAttach(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioNetDevAttach pDevice=%p\n", pDevice));
+
+ virtio_net_t *pNet = pDevice->pvDevice;
+ mac_register_t *pMacRegHandle = mac_alloc(MAC_VERSION);
+ if (pMacRegHandle)
+ {
+ pMacRegHandle->m_driver = pDevice;
+ pMacRegHandle->m_dip = pDevice->pDip;
+ pMacRegHandle->m_callbacks = &g_VirtioNetCallbacks;
+ pMacRegHandle->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+ pMacRegHandle->m_min_sdu = 0;
+ pMacRegHandle->m_max_sdu = 1500; /** @todo verify */
+ /** @todo should we set the margin size? */
+ pMacRegHandle->m_src_addr = pNet->MacAddr.au8;
+
+ /*
+ * Get MAC from the host or generate a random MAC address.
+ */
+ if (pDevice->fHostFeatures & VIRTIO_NET_MAC)
+ {
+ pDevice->pHyperOps->pfnGet(pDevice, 0 /* offset */, &pNet->MacAddr.au8, sizeof(pNet->MacAddr));
+ LogFlow((VIRTIOLOGNAME ":VirtioNetDevAttach: Obtained MAC address from host: %.6Rhxs\n", pNet->MacAddr.au8));
+ }
+ else
+ {
+ pNet->MacAddr.au8[0] = 0x08;
+ pNet->MacAddr.au8[1] = 0x00;
+ pNet->MacAddr.au8[2] = 0x27;
+ RTRandBytes(&pNet->MacAddr.au8[3], 3);
+ LogFlow((VIRTIOLOGNAME ":VirtioNetDevAttach: Generated MAC address %.6Rhxs\n", pNet->MacAddr.au8));
+ }
+
+ int rc = VirtioNetAttachQueues(pDevice);
+ if (rc == DDI_SUCCESS)
+ {
+ rc = mac_register(pMacRegHandle, &pNet->hMac);
+ if (rc == 0)
+ {
+ mac_link_update(pNet->hMac, LINK_STATE_DOWN);
+ mac_free(pMacRegHandle);
+ LogFlow((VIRTIOLOGNAME ":VirtioNetDevAttach: successfully registered mac.\n"));
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioNetDevAttach: mac_register failed. rc=%d\n", rc));
+
+ VirtioNetDetachQueues(pDevice);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioNetDevAttach: VirtioNetAttachQueues failed. rc=%d\n", rc));
+
+ mac_free(pMacRegHandle);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioNetDevAttach: mac_alloc failed. Invalid version!?!\n"));
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Virtio Net device detach routine.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetDevDetach(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioNetDevDetach pDevice=%p\n", pDevice));
+ virtio_net_t *pNet = pDevice->pvDevice;
+
+ int rc = mac_unregister(pNet->hMac);
+ if (rc == 0)
+ {
+ VirtioNetDetachQueues(pDevice);
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioNetDevDetach: mac_unregister failed. rc=%d\n", rc));
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Attach the Virtio Net TX, RX and control queues.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetAttachQueues(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioNetAttachQueues pDevice=%p\n", pDevice));
+
+ virtio_net_t *pNet = pDevice->pvDevice;
+
+ pNet->pRxQueue = VirtioGetQueue(pDevice, 0 /* index */ );
+ if (pNet->pRxQueue)
+ {
+ pNet->pTxQueue = VirtioGetQueue(pDevice, 1 /* index */);
+ if (pNet->pTxQueue)
+ {
+ if (pDevice->fHostFeatures & VIRTIO_NET_CTRL_VQ)
+ {
+ pNet->pCtrlQueue = VirtioGetQueue(pDevice, 2 /* index */);
+ if (pNet->pCtrlQueue)
+ {
+ LogFlow((VIRTIOLOGNAME ":VirtioNetAttachQueues successfully obtained 3 queues.\n"));
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioNetAttachQueues: failed to get Control queue.\n"));
+ }
+ else
+ {
+ LogFlow((VIRTIOLOGNAME ":VirtioNetAttachQueues successfully obtained 2 queues.\n"));
+ return DDI_SUCCESS;
+ }
+
+ VirtioPutQueue(pDevice, pNet->pTxQueue);
+ pNet->pTxQueue = NULL;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioNetAttachQueues: failed to get TX queue.\n"));
+
+ VirtioPutQueue(pDevice, pNet->pRxQueue);
+ pNet->pRxQueue = NULL;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioNetAttachQueues: failed to get RX queue.\n"));
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Detach the Virtio Net TX, RX and control queues.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ */
+static void VirtioNetDetachQueues(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioNetDetachQueues pDevice=%p\n", pDevice));
+ virtio_net_t *pNet = pDevice->pvDevice;
+
+ if ( pDevice->fHostFeatures & VIRTIO_NET_CTRL_VQ
+ && pNet->pCtrlQueue)
+ VirtioPutQueue(pDevice, pNet->pCtrlQueue);
+
+ if (pNet->pTxCache)
+ VirtioPutQueue(pDevice, pNet->pTxQueue);
+
+ if (pNet->pRxQueue)
+ VirtioPutQueue(pDevice, pNet->pRxQueue);
+}
+
+
+
+/* -=-=-=-=- Virtio Net MAC layer callbacks -=-=-=-=- */
+
+/**
+ * Virtio Net statistics.
+ *
+ * @param pvArg Pointer to private data.
+ * @param cmdStat Which statistics to provide.
+ * @param pu64Val Where to write statistics data.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetStat(void *pvArg, uint_t cmdStat, uint64_t *pu64Val)
+{
+ NOREF(pvArg);
+ NOREF(cmdStat);
+ NOREF(pu64Val);
+ return ENOTSUP;
+}
+
+
+/**
+ * Virtio Net Start.
+ *
+ * @param pvArg Pointer to private data.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetStart(void *pvArg)
+{
+ PVIRTIODEVICE pDevice = pvArg;
+ virtio_net_t *pNet = pDevice->pvDevice;
+ mac_link_update(pNet->hMac, LINK_STATE_UP);
+
+ pDevice->pHyperOps->pfnSetStatus(pDevice, VIRTIO_PCI_STATUS_DRV_OK);
+ return 0;
+}
+
+
+/**
+ * Virtio Net Stop.
+ *
+ * @param pvArg Pointer to private data.
+ */
+static void VirtioNetStop(void *pvArg)
+{
+ PVIRTIODEVICE pDevice = pvArg;
+ virtio_net_t *pNet = pDevice->pvDevice;
+ mac_link_update(pNet->hMac, LINK_STATE_DOWN);
+
+ /*
+ * I don't think we should set status here as the host checks the status on every Xmit. This means pending Xmits
+ * would also be dropped.
+ * @todo: Not sure what's the best way to signal connect/disconnect of the link to the host. Figure it out.
+ */
+}
+
+
+/**
+ * Virtio Net toggle Promiscuous mode.
+ *
+ * @param pvArg Pointer to private data.
+ * @param fPromiscOn Promiscuous On/Off.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetSetPromisc(void *pvArg, boolean_t fPromiscOn)
+{
+ NOREF(pvArg);
+ NOREF(fPromiscOn);
+ return 0;
+}
+
+
+/**
+ * Virtio Net set/add multicast address.
+ *
+ * @param pvArg Pointer to private data.
+ * @param fAdd Whether to add multicast address or not.
+ * @param pbMac Pointer to the multicast MAC address to set/add.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetSetMulticast(void *pvArg, boolean_t fAdd, const uint8_t *pbMac)
+{
+ NOREF(pvArg);
+ NOREF(fAdd);
+ NOREF(pbMac);
+ return 1;
+}
+
+
+/**
+ * Virtio Net set unicast address.
+ *
+ * @param pvArg Pointer to private data.
+ * @param pbMac Pointer to the unicast MAC address to set.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VirtioNetSetUnicast(void *pvArg, const uint8_t *pbMac)
+{
+ NOREF(pvArg);
+ NOREF(pbMac);
+ return ENOTSUP;
+}
+
+
+/**
+ * Virtio Net get capabilities hook.
+ *
+ * @param pvArg Pointer to private data.
+ * @param Capab MAC layer capabilities.
+ * @param pvCapabData Pointer to store capability info.
+ *
+ * @return B_TRUE upon success, otherwise B_FALSE.
+ */
+static boolean_t VirtioNetGetCapab(void *pvArg, mac_capab_t Capab, void *pvCapabData)
+{
+ NOREF(pvArg);
+ NOREF(Capab);
+ NOREF(pvCapabData);
+ return B_FALSE;
+}
+
+
+/**
+ * Virtio Net Xmit hook.
+ *
+ * @param pvArg Pointer to private data.
+ * @param pMsg Pointer to the message.
+ *
+ * @return Pointer to message not Xmited.
+ */
+static mblk_t *VirtioNetXmit(void *pvArg, mblk_t *pMsg)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioNetXmit pMsg=%p\n", pMsg));
+ cmn_err(CE_NOTE, "Xmit pMsg=%p\n", pMsg);
+
+ PVIRTIODEVICE pDevice = pvArg;
+ virtio_net_t *pNet = pDevice->pvDevice;
+ bool fNotify = false;
+
+ while (pMsg)
+ {
+ mblk_t *pNextMsg = pMsg->b_next;
+
+#if 0
+ mblk_t *pHdr = allocb(sizeof(virtio_net_header_t), BPRI_HI);
+ if (RT_UNLIKELY(!pHdr))
+ break;
+
+ virtio_net_header_t *pNetHdr = pHdr->b_rptr;
+ memset(pNetHdr, 0, sizeof(virtio_net_header_t));
+ pNetHdr->u8Flags = VIRTIO_NET_GUEST_CSUM;
+ pNetHdr->u16HdrLen = sizeof(virtio_net_header_t);
+
+ pHdr->b_wptr += sizeof(virtio_net_header_t);
+ pHdr->b_cont = pMsg;
+#endif
+
+ virtio_net_txbuf_t *pTxBuf = kmem_cache_alloc(pNet->pTxCache, KM_SLEEP);
+ if (!pTxBuf)
+ break;
+
+ ddi_dma_cookie_t DmaCookie;
+ uint_t cCookies;
+ int rc = ddi_dma_addr_bind_handle(pTxBuf->hDMA, NULL /* addrspace */, (char *)pMsg->b_rptr, MBLKL(pMsg),
+ DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0 /* addr */,
+ &DmaCookie, &cCookies);
+ cmn_err(CE_NOTE, "VirtioNetXmit: MBLKL pMsg=%u\n", MBLKL(pMsg));
+ if (rc != DDI_DMA_MAPPED)
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioNetXmit failed to map address to DMA handle. rc=%d\n", rc));
+ kmem_cache_free(pNet->pTxCache, pTxBuf);
+ break;
+ }
+
+ /** @todo get 'cCookies' slots from the ring. */
+
+ for (uint_t i = 0; i < cCookies; i++)
+ {
+ uint16_t fFlags = 0;
+ if (i < cCookies - 1)
+ fFlags |= VIRTIO_FLAGS_RING_DESC_NEXT;
+
+ rc = VirtioRingPush(pNet->pTxQueue, DmaCookie.dmac_laddress, DmaCookie.dmac_size, fFlags);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioNetXmit failed. rc=%Rrc\n", rc));
+ break;
+ }
+
+ ddi_dma_nextcookie(pTxBuf->hDMA, &DmaCookie);
+ }
+
+ pMsg = pNextMsg;
+ fNotify = true;
+ if (RT_FAILURE(rc))
+ {
+ ddi_dma_unbind_handle(pTxBuf->hDMA);
+ break;
+ }
+ }
+
+ if (fNotify)
+ pDevice->pHyperOps->pfnNotifyQueue(pDevice, pNet->pTxQueue);
+
+ return pMsg;
+}
+
+
+/**
+ * Interrupt Service Routine for Virtio Net.
+ *
+ * @param Arg Private data (unused, will be NULL).
+ * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
+ */
+static uint_t VirtioNetISR(caddr_t Arg)
+{
+ cmn_err(CE_NOTE, "VirtioNetISR Arg=%p\n", Arg);
+ NOREF(Arg);
+ return DDI_INTR_UNCLAIMED;
+}
+
diff --git a/src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.c b/src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.c
new file mode 100644
index 00000000..36f69e1b
--- /dev/null
+++ b/src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.c
@@ -0,0 +1,665 @@
+/* $Id: VirtioPci-solaris.c $ */
+/** @file
+ * VirtualBox Guest Additions - Virtio Driver for Solaris, PCI Hypervisor Interface.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VirtioPci-solaris.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <VBox/log.h>
+
+#include <sys/pci.h>
+#include <sys/param.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*
+ * Pci Register offsets.
+ */
+#define VIRTIO_PCI_HOST_FEATURES 0x00
+#define VIRTIO_PCI_GUEST_FEATURES 0x04
+#define VIRTIO_PCI_QUEUE_PFN 0x08
+#define VIRTIO_PCI_QUEUE_NUM 0x0C
+#define VIRTIO_PCI_QUEUE_SEL 0x0E
+#define VIRTIO_PCI_QUEUE_NOTIFY 0x10
+#define VIRTIO_PCI_STATUS 0x12
+#define VIRTIO_PCI_ISR 0x13
+#define VIRTIO_PCI_CONFIG 0x14
+
+#define VIRTIO_PCI_RING_ALIGN PAGE_SIZE
+#define VIRTIO_PCI_QUEUE_ADDR_SHIFT PAGE_SHIFT
+
+/**
+ * virtio_pci_t: Private data per device instance.
+ */
+typedef struct virtio_pci_t
+{
+ ddi_acc_handle_t hIO; /* IO handle */
+ caddr_t addrIOBase; /* IO base address */
+} virtio_pci_t;
+
+/**
+ * virtio_pci_queue_t: Private data per queue instance.
+ */
+typedef struct virtio_pci_queue_t
+{
+ ddi_dma_handle_t hDMA; /* DMA handle. */
+ ddi_acc_handle_t hIO; /* IO handle. */
+ size_t cbBuf; /* Physical address of buffer. */
+ paddr_t physBuf; /* Size of buffer. */
+ pfn_t pageBuf; /* Page frame number of buffer. */
+} virtio_pci_queue_t;
+
+static ddi_device_acc_attr_t g_VirtioPciAccAttrRegs =
+{
+ DDI_DEVICE_ATTR_V0, /* Version */
+ DDI_STRUCTURE_LE_ACC, /* Structural data access in little endian. */
+ DDI_STRICTORDER_ACC, /* Strict ordering. */
+ DDI_DEFAULT_ACC /* Default access, possible panic on errors*/
+};
+
+static ddi_device_acc_attr_t g_VirtioPciAccAttrRing =
+{
+ DDI_DEVICE_ATTR_V0, /* Version. */
+ DDI_NEVERSWAP_ACC, /* Data access with no byte swapping*/
+ DDI_STRICTORDER_ACC, /* Strict ordering. */
+ DDI_DEFAULT_ACC /* Default access, possible panic on errors*/
+};
+
+static ddi_dma_attr_t g_VirtioPciDmaAttrRing =
+{
+ DMA_ATTR_V0, /* Version. */
+ 0, /* Lowest usable address. */
+ 0xffffffffffffffffULL, /* Highest usable address. */
+ 0x7fffffff, /* Maximum DMAable byte count. */
+ VIRTIO_PCI_RING_ALIGN, /* Alignment in bytes. */
+ 0x7ff, /* Bitmap of burst sizes */
+ 1, /* Minimum transfer. */
+ 0xffffffffU, /* Maximum transfer. */
+ 0xffffffffffffffffULL, /* Maximum segment length. */
+ 1, /* Maximum number of segments. */
+ 1, /* Granularity. */
+ 0 /* Flags (reserved). */
+};
+
+/** Pointer to the interrupt handle vector */
+static ddi_intr_handle_t *g_pIntr;
+/** Number of actually allocated interrupt handles */
+static size_t g_cIntrAllocated;
+/** The IRQ Mutex */
+static kmutex_t g_IrqMtx;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void *VirtioPciAlloc(PVIRTIODEVICE pDevice);
+static void VirtioPciFree(PVIRTIODEVICE pDevice);
+static int VirtioPciAttach(PVIRTIODEVICE pDevice);
+static int VirtioPciDetach(PVIRTIODEVICE pDevice);
+static uint32_t VirtioPciGetFeatures(PVIRTIODEVICE pDevice);
+static void VirtioPciSetFeatures(PVIRTIODEVICE pDevice, uint32_t fFeatures);
+static int VirtioPciNotifyQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue);
+static void VirtioPciGet(PVIRTIODEVICE pDevice, off_t off, void *pv, size_t cb);
+static void VirtioPciSet(PVIRTIODEVICE pDevice, off_t off, void *pv, size_t cb);
+static void *VirtioPciGetQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue);
+static void VirtioPciPutQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue);
+static void VirtioPciSetStatus(PVIRTIODEVICE pDevice, uint8_t Status);
+
+static uint_t VirtioPciISR(caddr_t Arg);
+static int VirtioPciSetupIRQ(dev_info_t *pDip);
+static void VirtioPciRemoveIRQ(dev_info_t *pDip);
+
+/**
+ * Hypervisor operations for Virtio Pci.
+ */
+VIRTIOHYPEROPS g_VirtioHyperOpsPci =
+{
+ VirtioPciAlloc,
+ VirtioPciFree,
+ VirtioPciAttach,
+ VirtioPciDetach,
+ VirtioPciGetFeatures,
+ VirtioPciSetFeatures,
+ VirtioPciNotifyQueue,
+ VirtioPciGet,
+ VirtioPciSet,
+ VirtioPciGetQueue,
+ VirtioPciPutQueue,
+ VirtioPciSetStatus
+};
+
+
+/**
+ * Virtio Pci private data allocation routine.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @return Allocated private data structure which must only be freed by calling
+ * VirtioPciFree().
+ */
+static void *VirtioPciAlloc(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciAlloc pDevice=%p\n", pDevice));
+ virtio_pci_t *pPciData = RTMemAllocZ(sizeof(virtio_pci_t));
+ return pPciData;
+}
+
+
+/**
+ * Virtio Pci private data deallocation routine.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ */
+static void VirtioPciFree(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciFree pDevice=%p\n", pDevice));
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ if (pPciData)
+ {
+ RTMemFree(pDevice->pvHyper);
+ pDevice->pvHyper = NULL;
+ }
+}
+
+
+/**
+ * Virtio Pci attach routine, called from driver attach.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ *
+ * @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
+ */
+static int VirtioPciAttach(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciAttach pDevice=%p\n", pDevice));
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ AssertReturn(pPciData, DDI_FAILURE);
+
+ int rc = ddi_regs_map_setup(pDevice->pDip,
+ 1, /* reg. num */
+ &pPciData->addrIOBase,
+ 0, /* offset */
+ 0, /* length */
+ &g_VirtioPciAccAttrRegs,
+ &pPciData->hIO);
+ if (rc == DDI_SUCCESS)
+ {
+ /*
+ * Reset the device.
+ */
+ VirtioPciSetStatus(pDevice, 0);
+
+ /*
+ * Add interrupt handler.
+ */
+ VirtioPciSetupIRQ(pDevice->pDip);
+
+ LogFlow((VIRTIOLOGNAME ":VirtioPciAttach: successfully mapped registers.\n"));
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciAttach: ddi_regs_map_setup failed. rc=%d\n", rc));
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Virtio Pci detach routine, called from driver detach.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ *
+ * @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
+ */
+static int VirtioPciDetach(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciDetach pDevice=%p\n", pDevice));
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ AssertReturn(pPciData, DDI_FAILURE);
+
+ VirtioPciRemoveIRQ(pDevice->pDip);
+ ddi_regs_map_free(&pPciData->hIO);
+ return DDI_SUCCESS;
+}
+
+
+/**
+ * Get host supported features.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ *
+ * @return Mask of host features.
+ */
+static uint32_t VirtioPciGetFeatures(PVIRTIODEVICE pDevice)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciGetFeatures pDevice=%p\n", pDevice));
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ AssertReturn(pPciData, 0);
+
+ return ddi_get32(pPciData->hIO, (uint32_t *)(pPciData->addrIOBase + VIRTIO_PCI_HOST_FEATURES));
+}
+
+
+/**
+ * Set guest supported features.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param u32Features Mask of guest supported features.
+ */
+static void VirtioPciSetFeatures(PVIRTIODEVICE pDevice, uint32_t u32Features)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciSetFeatures pDevice=%p\n", pDevice));
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ AssertReturnVoid(pPciData);
+
+ ddi_put32(pPciData->hIO, (uint32_t *)(pPciData->addrIOBase + VIRTIO_PCI_GUEST_FEATURES), u32Features);
+}
+
+
+/**
+ * Update the queue, notify the host.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param pQueue Pointer to the Queue that is doing the notification.
+ *
+ * @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
+ */
+static int VirtioPciNotifyQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciNotifyQueue pDevice=%p pQueue=%p\n", pDevice, pQueue));
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ AssertReturn(pPciData, DDI_FAILURE);
+
+ pQueue->Ring.pRingAvail->Index += pQueue->cBufs;
+ pQueue->cBufs = 0;
+
+ ASMCompilerBarrier();
+
+ ddi_put16(pPciData->hIO, (uint16_t *)(pPciData->addrIOBase + VIRTIO_PCI_QUEUE_NOTIFY), pQueue->QueueIndex);
+ cmn_err(CE_NOTE, "VirtioPciNotifyQueue\n");
+}
+
+
+
+/**
+ * Virtio Pci set (write) routine.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param off Offset into the PCI config space.
+ * @param pv Pointer to the buffer to write from.
+ * @param cb Size of the buffer in bytes.
+ */
+static void VirtioPciSet(PVIRTIODEVICE pDevice, off_t off, void *pv, size_t cb)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciSet pDevice=%p\n", pDevice));
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ AssertReturnVoid(pPciData);
+
+ uint8_t *pb = pv;
+ for (size_t i = 0; i < cb; i++, pb++)
+ ddi_put8(pPciData->hIO, (uint8_t *)(pPciData->addrIOBase + VIRTIO_PCI_CONFIG + off + i), *pb);
+}
+
+
+/**
+ * Virtio Pci get (read) routine.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param off Offset into the PCI config space.
+ * @param pv Where to store the read data.
+ * @param cb Size of the buffer in bytes.
+ */
+static void VirtioPciGet(PVIRTIODEVICE pDevice, off_t off, void *pv, size_t cb)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciGet pDevice=%p off=%u pv=%p cb=%u\n", pDevice, off, pv, cb));
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ AssertReturnVoid(pPciData);
+
+ uint8_t *pb = pv;
+ for (size_t i = 0; i < cb; i++, pb++)
+ *pb = ddi_get8(pPciData->hIO, (uint8_t *)(pPciData->addrIOBase + VIRTIO_PCI_CONFIG + off + i));
+}
+
+
+/**
+ * Virtio Pci put queue routine. Places the queue and frees associated queue.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param pQueue Pointer to the queue.
+ */
+static void VirtioPciPutQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciPutQueue pDevice=%p pQueue=%p\n", pDevice, pQueue));
+ AssertReturnVoid(pDevice);
+ AssertReturnVoid(pQueue);
+
+ virtio_pci_t *pPci = pDevice->pvHyper;
+ AssertReturnVoid(pPci);
+ virtio_pci_queue_t *pPciQueue = pQueue->pvData;
+ if (RT_UNLIKELY(!pPciQueue))
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioPciPutQueue missing Pci queue.\n"));
+ return;
+ }
+
+ ddi_put16(pPci->hIO, (uint16_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_SEL), pQueue->QueueIndex);
+ ddi_put32(pPci->hIO, (uint32_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_PFN), 0);
+
+ ddi_dma_unbind_handle(pPciQueue->hDMA);
+ ddi_dma_mem_free(&pPciQueue->hIO);
+ ddi_dma_free_handle(&pPciQueue->hDMA);
+ RTMemFree(pPciQueue);
+}
+
+
+/**
+ * Virtio Pci get queue routine. Allocates a PCI queue and DMA resources.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param pQueue Where to store the queue.
+ *
+ * @return An allocated Virtio Pci queue, or NULL in case of errors.
+ */
+static void *VirtioPciGetQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue)
+{
+ LogFlowFunc((VIRTIOLOGNAME ":VirtioPciGetQueue pDevice=%p pQueue=%p\n", pDevice, pQueue));
+ AssertReturn(pDevice, NULL);
+
+ virtio_pci_t *pPci = pDevice->pvHyper;
+ AssertReturn(pPci, NULL);
+
+ /*
+ * Select a Queue.
+ */
+ ddi_put16(pPci->hIO, (uint16_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_SEL), pQueue->QueueIndex);
+
+ /*
+ * Get the currently selected Queue's size.
+ */
+ pQueue->Ring.cDesc = ddi_get16(pPci->hIO, (uint16_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_NUM));
+ if (RT_UNLIKELY(!pQueue->Ring.cDesc))
+ {
+ LogRel((VIRTIOLOGNAME ": VirtioPciGetQueue: Queue[%d] has no descriptors.\n", pQueue->QueueIndex));
+ return NULL;
+ }
+
+ /*
+ * Check if it's already active.
+ */
+ uint32_t QueuePFN = ddi_get32(pPci->hIO, (uint32_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_PFN));
+ if (QueuePFN != 0)
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: Queue[%d] is already used.\n", pQueue->QueueIndex));
+ return NULL;
+ }
+
+ LogFlow(("Queue[%d] has %d slots.\n", pQueue->QueueIndex, pQueue->Ring.cDesc));
+
+ /*
+ * Allocate and initialize Pci queue data.
+ */
+ virtio_pci_queue_t *pPciQueue = RTMemAllocZ(sizeof(virtio_pci_queue_t));
+ if (pPciQueue)
+ {
+ /*
+ * Setup DMA.
+ */
+ size_t cbQueue = VirtioRingSize(pQueue->Ring.cDesc, VIRTIO_PCI_RING_ALIGN);
+ int rc = ddi_dma_alloc_handle(pDevice->pDip, &g_VirtioPciDmaAttrRing, DDI_DMA_SLEEP, 0 /* addr */, &pPciQueue->hDMA);
+ if (rc == DDI_SUCCESS)
+ {
+ rc = ddi_dma_mem_alloc(pPciQueue->hDMA, cbQueue, &g_VirtioPciAccAttrRing, DDI_DMA_CONSISTENT,
+ DDI_DMA_SLEEP, 0 /* addr */, &pQueue->pQueue, &pPciQueue->cbBuf,
+ &pPciQueue->hIO);
+ if (rc == DDI_SUCCESS)
+ {
+ AssertRelease(pPciQueue->cbBuf >= cbQueue);
+ ddi_dma_cookie_t DmaCookie;
+ uint_t cCookies;
+ rc = ddi_dma_addr_bind_handle(pPciQueue->hDMA, NULL /* addrspace */, pQueue->pQueue, pPciQueue->cbBuf,
+ DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
+ 0 /* addr */, &DmaCookie, &cCookies);
+ if (rc == DDI_SUCCESS)
+ {
+ pPciQueue->physBuf = DmaCookie.dmac_laddress;
+ pPciQueue->pageBuf = pPciQueue->physBuf >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
+
+ LogFlow((VIRTIOLOGNAME ":VirtioPciGetQueue: Queue[%d]%p physBuf=%x pfn of Buf %#x\n", pQueue->QueueIndex,
+ pQueue->pQueue, pPciQueue->physBuf, pPciQueue->pageBuf));
+ cmn_err(CE_NOTE, ":VirtioPciGetQueue: Queue[%d]%p physBuf=%x pfn of Buf %x\n", pQueue->QueueIndex,
+ pQueue->pQueue, pPciQueue->physBuf, pPciQueue->pageBuf);
+
+ /*
+ * Activate the queue and initialize a ring for the queue.
+ */
+ memset(pQueue->pQueue, 0, pPciQueue->cbBuf);
+ ddi_put32(pPci->hIO, (uint32_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_PFN), pPciQueue->pageBuf);
+ VirtioRingInit(pQueue, pQueue->Ring.cDesc, pQueue->pQueue, VIRTIO_PCI_RING_ALIGN);
+ return pPciQueue;
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: ddi_dma_addr_bind_handle failed. rc=%d\n", rc));
+
+ ddi_dma_mem_free(&pPciQueue->hIO);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: ddi_dma_mem_alloc failed for %u bytes rc=%d\n", cbQueue, rc));
+
+ ddi_dma_free_handle(&pPciQueue->hDMA);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: ddi_dma_alloc_handle failed. rc=%d\n", rc));
+
+ RTMemFree(pPciQueue);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: failed to alloc %u bytes for Pci Queue data.\n", sizeof(virtio_pci_queue_t)));
+
+ return NULL;
+}
+
+
+/**
+ * Set the Virtio PCI status bit.
+ *
+ * @param pDevice Pointer to the Virtio device instance.
+ * @param Status The status to set.
+ */
+static void VirtioPciSetStatus(PVIRTIODEVICE pDevice, uint8_t Status)
+{
+ virtio_pci_t *pPciData = pDevice->pvHyper;
+ ddi_put8(pPciData->hIO, (uint8_t *)(pPciData->addrIOBase + VIRTIO_PCI_STATUS), Status);
+}
+
+
+/**
+ * Sets up IRQ for Virtio PCI.
+ *
+ * @param pDip Pointer to the device info structure.
+ *
+ * @return Solaris error code.
+ */
+static int VirtioPciSetupIRQ(dev_info_t *pDip)
+{
+ LogFlow((VIRTIOLOGNAME ":VirtioPciSetupIRQ: pDip=%p\n", pDip));
+ cmn_err(CE_NOTE, "VirtioPciSetupIRQ\n");
+
+ int IntrType = 0;
+ int rc = ddi_intr_get_supported_types(pDip, &IntrType);
+ if (rc == DDI_SUCCESS)
+ {
+ /* We won't need to bother about MSIs. */
+ if (IntrType & DDI_INTR_TYPE_FIXED)
+ {
+ int IntrCount = 0;
+ rc = ddi_intr_get_nintrs(pDip, IntrType, &IntrCount);
+ if ( rc == DDI_SUCCESS
+ && IntrCount > 0)
+ {
+ int IntrAvail = 0;
+ rc = ddi_intr_get_navail(pDip, IntrType, &IntrAvail);
+ if ( rc == DDI_SUCCESS
+ && IntrAvail > 0)
+ {
+ /* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
+ g_pIntr = RTMemAllocZ(IntrCount * sizeof(ddi_intr_handle_t));
+ if (g_pIntr)
+ {
+ int IntrAllocated;
+ rc = ddi_intr_alloc(pDip, g_pIntr, IntrType, 0, IntrCount, &IntrAllocated, DDI_INTR_ALLOC_NORMAL);
+ if ( rc == DDI_SUCCESS
+ && IntrAllocated > 0)
+ {
+ g_cIntrAllocated = IntrAllocated;
+ uint_t uIntrPriority;
+ rc = ddi_intr_get_pri(g_pIntr[0], &uIntrPriority);
+ if (rc == DDI_SUCCESS)
+ {
+ /* Initialize the mutex. */
+ mutex_init(&g_IrqMtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
+
+ /* Assign interrupt handler functions and enable interrupts. */
+ for (int i = 0; i < IntrAllocated; i++)
+ {
+ rc = ddi_intr_add_handler(g_pIntr[i], (ddi_intr_handler_t *)VirtioPciISR,
+ NULL /* No Private Data */, NULL);
+ if (rc == DDI_SUCCESS)
+ rc = ddi_intr_enable(g_pIntr[i]);
+ if (rc != DDI_SUCCESS)
+ {
+ /* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
+ IntrAllocated = i;
+ break;
+ }
+ }
+ if (rc == DDI_SUCCESS)
+ {
+ cmn_err(CE_NOTE, "VirtioPciSetupIRQ success\n");
+ return rc;
+ }
+
+ /* Remove any assigned handlers */
+ LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ failed to assign IRQs allocated=%d\n", IntrAllocated));
+ for (int x = 0; x < IntrAllocated; x++)
+ ddi_intr_remove_handler(g_pIntr[x]);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ failed to get priority of interrupt. rc=%d\n", rc));
+
+ /* Remove allocated IRQs, too bad we can free only one handle at a time. */
+ for (int k = 0; k < g_cIntrAllocated; k++)
+ ddi_intr_free(g_pIntr[k]);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
+ RTMemFree(g_pIntr);
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
+ }
+ else
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n",
+ rc, IntrAvail));
+ }
+ }
+ else
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc,
+ IntrCount));
+ }
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: invalid irq type. IntrType=%#x\n", IntrType));
+ }
+ else
+ LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to get supported interrupt types\n"));
+ return rc;
+}
+
+
+/**
+ * Removes IRQ for Virtio PCI device.
+ *
+ * @param pDip Pointer to the device info structure.
+ */
+static void VirtioPciRemoveIRQ(dev_info_t *pDip)
+{
+ LogFlow((VIRTIOLOGNAME ":VirtioPciRemoveIRQ pDip=%p:\n", pDip));
+
+ for (int i = 0; i < g_cIntrAllocated; i++)
+ {
+ int rc = ddi_intr_disable(g_pIntr[i]);
+ if (rc == DDI_SUCCESS)
+ {
+ rc = ddi_intr_remove_handler(g_pIntr[i]);
+ if (rc == DDI_SUCCESS)
+ ddi_intr_free(g_pIntr[i]);
+ }
+ }
+ RTMemFree(g_pIntr);
+ mutex_destroy(&g_IrqMtx);
+}
+
+
+/**
+ * Interrupt Service Routine for Virtio PCI device.
+ *
+ * @param Arg Private data (unused, will be NULL).
+ * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
+ */
+static uint_t VirtioPciISR(caddr_t Arg)
+{
+ LogFlow((VIRTIOLOGNAME ":VBoxGuestSolarisISR\n"));
+ cmn_err(CE_NOTE, "VBoxGuestSolarisISRd Arg=%p\n", Arg);
+
+ mutex_enter(&g_IrqMtx);
+ bool fOurIRQ = false;
+ /*
+ * Call the DeviceOps ISR routine somehow which should notify all Virtio queues
+ * on the interrupt.
+ */
+ mutex_exit(&g_IrqMtx);
+
+ return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
+}
+
diff --git a/src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.h b/src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.h
new file mode 100644
index 00000000..5d0aaf0c
--- /dev/null
+++ b/src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.h
@@ -0,0 +1,48 @@
+/* $Id: VirtioPci-solaris.h $ */
+/** @file
+ * VirtualBox Guest Additions: Virtio Driver for Solaris, PCI Hypervisor Interface.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_solaris_Virtio_VirtioPci_solaris_h
+#define GA_INCLUDED_SRC_solaris_Virtio_VirtioPci_solaris_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "Virtio-solaris.h"
+
+extern VIRTIOHYPEROPS g_VirtioHyperOpsPci;
+
+#endif /* !GA_INCLUDED_SRC_solaris_Virtio_VirtioPci_solaris_h */
+
diff --git a/src/VBox/Additions/solaris/Virtio/VirtioRing-solaris.c b/src/VBox/Additions/solaris/Virtio/VirtioRing-solaris.c
new file mode 100644
index 00000000..004075c1
--- /dev/null
+++ b/src/VBox/Additions/solaris/Virtio/VirtioRing-solaris.c
@@ -0,0 +1,148 @@
+/* $Id: VirtioRing-solaris.c $ */
+/** @file
+ * VirtualBox Guest Additions: Virtio Driver for Solaris, Ring implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "Virtio-solaris.h"
+
+#include <iprt/asm.h>
+#include <iprt/cdefs.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+
+#include <sys/cmn_err.h>
+
+/**
+ * Returns the size of the ring in bytes given the number of elements and
+ * alignment requirements.
+ *
+ * @param cElements Number of elements.
+ * @param Align Alignment (must be a power of two).
+ *
+ * @return Size of the Virtio ring.
+ */
+size_t VirtioRingSize(uint64_t cElements, ulong_t Align)
+{
+ size_t cb = 0;
+ cb = cElements * sizeof(VIRTIORINGDESC); /* Ring descriptors. */
+ cb += 2 * sizeof(uint16_t); /* Available flags and index. */
+ cb += cElements * sizeof(uint16_t); /* Available descriptors. */
+
+ size_t cbAlign = RT_ALIGN_Z(cb, Align);
+ cbAlign += 2 * sizeof(uint16_t); /* Used flags and index. */
+ cbAlign += cElements * sizeof(VIRTIORINGUSEDELEM); /* Used descriptors. */
+
+ return cbAlign;
+}
+
+
+/**
+ * Initializes a ring of a queue. This associates the DMA virtual address
+ * with the Ring structure's "pRingDesc".
+ *
+ * @param pQueue Pointer to the Virtio Queue.
+ * @param cDescs Number of descriptors.
+ * @param virtBuf Buffer associated with the ring.
+ * @param Align Alignment (must be power of two).
+ */
+void VirtioRingInit(PVIRTIOQUEUE pQueue, uint_t cDescs, caddr_t virtBuf, ulong_t Align)
+{
+ PVIRTIORING pRing = &pQueue->Ring;
+ pRing->cDesc = cDescs;
+ pRing->pRingDesc = (void *)virtBuf;
+ pRing->pRingAvail = (PVIRTIORINGAVAIL)(virtBuf + (cDescs * sizeof(pRing->pRingDesc[0])));
+ pRing->pRingUsedElem = RT_ALIGN_PT(pRing->pRingAvail + RT_UOFFSETOF_DYN(VIRTIORINGAVAIL, aRings[pQueue->Ring.cDesc]), Align,
+ PVIRTIORINGUSEDELEM);
+
+ for (uint_t i = 0; i < pRing->cDesc - 1; i++)
+ pRing->pRingDesc[i].Next = i + 1;
+
+ pQueue->FreeHeadIndex = 0;
+
+ cmn_err(CE_NOTE, "cDesc=%u pRingDesc=%p pRingAvail=%p\n", pRing->cDesc, pRing->pRingDesc, pRing->pRingAvail);
+}
+
+
+/**
+ * Push a buffer into the ring.
+ *
+ * @param pQueue Pointer to the Virtio queue.
+ * @param physBuf Physical address of the buffer.
+ * @param cbBuf Size of the buffer in bytes.
+ * @param fFlags Buffer flags, see VIRTIO_FLAGS_RING_DESC_*.
+ *
+ * @return IPRT error code.
+ */
+int VirtioRingPush(PVIRTIOQUEUE pQueue, paddr_t physBuf, uint32_t cbBuf, uint16_t fFlags)
+{
+ /*
+ * Claim a slot, fill the buffer and move head pointer.
+ */
+ uint_t FreeIndex = pQueue->FreeHeadIndex;
+ PVIRTIORING pRing = &pQueue->Ring;
+
+ if (FreeIndex >= pRing->cDesc - 1)
+ {
+ LogRel((VIRTIOLOGNAME ":VirtioRingPush: failed. No free descriptors. cDesc=%u\n", pRing->cDesc));
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ PVIRTIORINGDESC pRingDesc = &pRing->pRingDesc[FreeIndex];
+
+ AssertCompile(sizeof(physBuf) == sizeof(pRingDesc->AddrBuf));
+
+ pQueue->cBufs++;
+ uint_t AvailIndex = (pRing->pRingAvail->Index + pQueue->cBufs) % pQueue->Ring.cDesc;
+ pRing->pRingAvail->aRings[AvailIndex - 1] = FreeIndex;
+
+ pRingDesc->AddrBuf = physBuf;
+ pRingDesc->cbBuf = cbBuf;
+ pRingDesc->fFlags = fFlags;
+
+ pQueue->FreeHeadIndex = pRingDesc->Next;
+
+ ASMCompilerBarrier();
+
+ cmn_err(CE_NOTE, "VirtioRingPush: cbBuf=%u FreeIndex=%u AvailIndex=%u cDesc=%u Queue->cBufs=%u\n",
+ cbBuf, FreeIndex, AvailIndex, pQueue->Ring.cDesc,
+ pQueue->cBufs);
+
+ return VINF_SUCCESS;
+}
+