diff options
Diffstat (limited to 'src/VBox/Additions/solaris')
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; +} + |