SUPDrv-solaris.c revision 344115a50dfcaf97123d38b056161195a7e4e48c
/* $Id$ */
/** @file
* VBoxDrv - The VirtualBox Support Driver - Solaris specifics.
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP_DRV
#include <sys/priv_names.h>
#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
#include "../SUPDrvInternal.h"
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
#include <iprt/initterm.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The module name. */
#define DEVICE_NAME "vboxdrv"
/** The module description as seen in 'modinfo'. */
#define DEVICE_DESC "VirtualBox HostDrv"
/** Maximum number of driver instances. */
#define DEVICE_MAXINSTANCES 16
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int VBoxDrvSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArgs, int mode, cred_t *pCred, int *pVal);
static int VBoxSupDrvErr2SolarisErr(int rc);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
*/
static struct cb_ops g_VBoxDrvSolarisCbOps =
{
nodev, /* b strategy */
nodev, /* b dump */
nodev, /* b print */
nodev, /* c devmap */
nodev, /* c mmap */
nodev, /* c segmap */
nochpoll, /* c poll */
ddi_prop_op, /* property ops */
NULL, /* streamtab */
CB_REV /* revision */
};
/**
* dev_ops: for driver device operations
*/
static struct dev_ops g_VBoxDrvSolarisDevOps =
{
DEVO_REV, /* driver build revision */
0, /* ref count */
nulldev, /* get info */
nulldev, /* identify */
nulldev, /* probe */
nodev, /* reset */
(struct bus_ops *)0,
nodev /* power */
};
/**
* modldrv: export driver specifics to the kernel
*/
static struct modldrv g_VBoxDrvSolarisModule =
{
&mod_driverops, /* extern from kernel */
};
/**
*/
static struct modlinkage g_VBoxDrvSolarisModLinkage =
{
MODREV_1, /* loadable module system revision */
{
NULL /* terminate array of linkage structures */
}
};
#ifndef USE_SESSION_HASH
/**
* State info for each open file handle.
*/
typedef struct
{
/**< Pointer to the session data. */
#else
/** State info. for each driver instance. */
typedef struct
{
#endif
/** Opaque pointer to list of state */
static void *g_pVBoxDrvSolarisState;
/** Device extention & session data association structure */
static SUPDRVDEVEXT g_DevExt;
/** Hash table */
/** Spinlock protecting g_apSessionHashTab. */
/** Calculates bucket index into g_apSessionHashTab.*/
/**
* Kernel entry points
*/
int _init(void)
{
/*
* Prevent module autounloading.
*/
if (pModCtl)
else
/*
* Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
*/
if (RT_SUCCESS(rc))
{
/*
* Initialize the device extension
*/
if (RT_SUCCESS(rc))
{
/*
* Initialize the session hash table.
*/
if (RT_SUCCESS(rc))
{
if (!rc)
{
if (!rc)
return rc; /* success */
}
else
}
else
}
else
}
else
return RTErrConvertToErrno(rc);
}
int _fini(void)
{
/*
* Undo the work we did at start (in the reverse order).
*/
if (rc != 0)
return rc;
return 0;
}
{
return e;
}
/**
* Attach entry point, to attach a device to the system or resume it.
*
* @param pDip The module structure instance.
*
* @return corresponding solaris error code.
*/
{
switch (enmCmd)
{
case DDI_ATTACH:
{
int rc;
#ifdef USE_SESSION_HASH
{
return DDI_FAILURE;
}
#endif
/*
*/
"pm-hardware-state", "needs-suspend-resume", sizeof("needs-suspend-resume"));
if (rc != DDI_PROP_SUCCESS)
/*
* Register ourselves as a character device, pseudo-driver
*/
#ifdef VBOX_WITH_HARDENING
#else
0, "none", "none", 0666);
#endif
if (rc == DDI_SUCCESS)
{
#ifdef USE_SESSION_HASH
#endif
return DDI_SUCCESS;
}
return DDI_FAILURE;
}
case DDI_RESUME:
{
#if 0
#endif
return DDI_SUCCESS;
}
default:
return DDI_FAILURE;
}
return DDI_FAILURE;
}
/**
* Detach entry point, to detach a device to the system or suspend it.
*
* @param pDip The module structure instance.
*
* @return corresponding solaris error code.
*/
{
switch (enmCmd)
{
case DDI_DETACH:
{
#ifndef USE_SESSION_HASH
#else
#endif
return DDI_SUCCESS;
}
case DDI_SUSPEND:
{
#if 0
#endif
return DDI_SUCCESS;
}
default:
return DDI_FAILURE;
}
}
/**
* User context entry points
*/
{
int rc;
#ifndef USE_SESSION_HASH
/*
* Locate a new device open instance.
*
* For each open call we'll allocate an item in the soft state of the device.
* The item index is stored in the dev_t. I hope this is ok...
*/
unsigned iOpenInstance;
{
{
break;
}
}
if (!pState)
{
return ENXIO;
}
/*
* Create a new session.
*/
if (RT_SUCCESS(rc))
{
return 0;
}
/* failed - clean up */
#else
/*
* Create a new session.
* Sessions in Solaris driver are mostly useless. It's however needed
* in VBoxDrvSolarisIOCtlSlow() while calling supdrvIOCtl()
*/
if (RT_SUCCESS(rc))
{
unsigned iHash;
/*
* Insert it into the hash table.
*/
}
int instance;
{
if (pState)
break;
}
if (instance >= DEVICE_MAXINSTANCES)
{
return ENXIO;
}
#endif
return VBoxSupDrvErr2SolarisErr(rc);
}
{
#ifndef USE_SESSION_HASH
/*
* Get the session and free the soft state item.
*/
if (!pState)
{
return EFAULT;
}
if (!pSession)
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisClose: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
return EFAULT;
}
#else
/*
* Remove from the hash table.
*/
if (pSession)
{
{
}
else
{
while (pSession)
{
{
break;
}
/* next */
}
}
}
if (!pSession)
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisClose: WHAT?!? pSession == NULL! This must be a mistake... pid=%d (close)\n",
(int)Process));
return EFAULT;
}
#endif
/*
* Close the session.
*/
return 0;
}
{
return 0;
}
{
return 0;
}
/**
* Driver ioctl, an alternate entry point for this character driver.
*
* @param Dev Device number
* @param Cmd Operation identifier
* @param pArg Arguments from user to driver
* @param pCred User credentials
* @param pVal Return value for calling process.
*
* @return corresponding solaris error code.
*/
static int VBoxDrvSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArgs, int Mode, cred_t *pCred, int *pVal)
{
#ifndef USE_SESSION_HASH
/*
* Get the session from the soft state item.
*/
if (!pState)
{
return EINVAL;
}
if (!pSession)
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtl: no session in state data for %#x (%d)\n", Dev, getminor(Dev)));
return DDI_SUCCESS;
}
#else
/*
* Find the session.
*/
{
}
if (!pSession)
{
LogRel((DEVICE_NAME ":VBoxSupDrvIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#x\n",
return EINVAL;
}
#endif
/*
* Deal with the two high-speed IOCtl that takes it's arguments from
* the session and iCmd, and only returns a VBox status code.
*/
if ( Cmd == SUP_IOCTL_FAST_DO_RAW_RUN
|| Cmd == SUP_IOCTL_FAST_DO_NOP)
{
return 0;
}
}
/** @def IOCPARM_LEN
* Gets the length from the ioctl number.
*/
#ifndef IOCPARM_LEN
#endif
/**
* Worker for VBoxSupDrvIOCtl that takes the slow IOCtl functions.
*
* @returns Solaris errno.
*
* @param pSession The session.
* @param Cmd The IOCtl command.
* @param Mode Information bitfield (for specifying ownership of data)
* @param iArg User space address of the request buffer.
*/
{
int rc;
union
{
} StackBuf;
/*
* Read the header.
*/
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: iCmd=%#x len %d expected %d\n", iCmd, IOCPARM_LEN(iCmd), sizeof(StackBuf.Hdr)));
return EINVAL;
}
if (RT_UNLIKELY(rc))
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: ddi_copyin(,%#lx,) failed; iCmd=%#x. rc=%d\n", iArg, iCmd, rc));
return EFAULT;
}
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: bad header magic %#x; iCmd=%#x\n", StackBuf.Hdr.fFlags & SUPREQHDR_FLAGS_MAGIC_MASK, iCmd));
return EINVAL;
}
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: max(%#x,%#x); iCmd=%#x\n", StackBuf.Hdr.cbIn, StackBuf.Hdr.cbOut, iCmd));
return EINVAL;
}
/*
* Buffer the request.
*/
else
{
if (RT_UNLIKELY(!pHdr))
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: failed to allocate buffer of %d bytes for iCmd=%#x.\n", cbBuf, iCmd));
return ENOMEM;
}
}
if (RT_UNLIKELY(rc))
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: copy_from_user(,%#lx, %#x) failed; iCmd=%#x. rc=%d\n", iArg, cbBuf, iCmd, rc));
return EFAULT;
}
/*
* Process the IOCtl.
*/
/*
* Copy ioctl data and output buffer back to user space.
*/
{
{
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: too much output! %#x > %#x; iCmd=%#x!\n", cbOut, cbBuf, iCmd));
}
if (RT_UNLIKELY(rc != 0))
{
/* this is really bad */
LogRel((DEVICE_NAME ":VBoxDrvSolarisIOCtlSlow: ddi_copyout(,%p,%d) failed. rc=%d\n", (void *)iArg, cbBuf, rc));
}
}
else
return rc;
}
/**
* The SUPDRV IDC entry point.
*
* @returns VBox status code, see supdrvIDC.
* @param iReq The request code.
* @param pReq The request.
*/
{
/*
* Some quick validations.
*/
return VERR_INVALID_POINTER;
if (pSession)
{
return VERR_INVALID_PARAMETER;
return VERR_INVALID_PARAMETER;
}
return VERR_INVALID_PARAMETER;
/*
* Do the job.
*/
}
/**
* Converts an supdrv error code to a solaris error code.
*
* @returns corresponding solaris error code.
* @param rc IPRT status code.
*/
static int VBoxSupDrvErr2SolarisErr(int rc)
{
switch (rc)
{
case VINF_SUCCESS: return 0;
case VERR_GENERAL_FAILURE: return EACCES;
case VERR_INVALID_PARAMETER: return EINVAL;
case VERR_INVALID_MAGIC: return EILSEQ;
case VERR_INVALID_HANDLE: return ENXIO;
case VERR_INVALID_POINTER: return EFAULT;
case VERR_LOCK_FAILED: return ENOLCK;
case VERR_ALREADY_LOADED: return EEXIST;
case VERR_PERMISSION_DENIED: return EPERM;
case VERR_VERSION_MISMATCH: return ENOSYS;
}
return EPERM;
}
/**
* Initializes any OS specific object creator fields.
*/
{
}
/**
* Checks if the session can access the object.
*
* @returns true if a decision has been made.
* @returns false if the default access policy should be applied.
*
* @param pObj The object in question.
* @param pSession The session wanting to access the object.
* @param pszObjName The object name, can be NULL.
* @param prc Where to store the result when returning true.
*/
bool VBOXCALL supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
{
return false;
}
{
return false;
}
#if defined(VBOX_WITH_NATIVE_SOLARIS_LOADING) \
&& !defined(VBOX_WITHOUT_NATIVE_R0_LOADER)
int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
{
# if 1 /* This approach requires _init/_fini/_info stubs. */
/*
* Construct a filename that escapes the module search path and let us
* specify a root path.
*/
char *pszSubDir = RTStrAPrintf2("../../../../../../../../../../../%.*s", pszName - pszFilename, pszFilename);
if (!pszSubDir)
return VERR_NO_STR_MEMORY;
if (idMod == -1)
{
return VERR_LDR_GENERAL_FAILURE;
}
if (!pModCtl)
{
/* No point in calling modunload. */
return VERR_LDR_GENERAL_FAILURE;
}
# else
const int idMod = -1;
if (!pModCtl)
{
return VERR_LDR_GENERAL_FAILURE;
}
if (rc != 0)
{
return RTErrConvertFromErrno(rc);
}
# endif
/*
* Get the module info.
*/
LogRel(("supdrvOSLdrOpen: succeeded for '%s' (mi_base=%p mi_base=%#x), id=%d ctl=%p\n",
return VINF_SUCCESS;
}
int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
{
return VERR_INVALID_PARAMETER;
return VINF_SUCCESS;
}
int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits)
{
/*
* Comparing is very very difficult since text and data may be allocated
* separately.
*/
{
return VERR_LDR_MISMATCH_NATIVE;
}
/*
* Get the symbol addresses.
*/
int rc;
{
while (iSym-- > 0)
{
if (!uValue)
{
LogRel(("supdrvOSLdrLoad on %s failed to resolve the exported symbol: '%s'\n", pImage->szName, pszSymbol));
break;
}
{
LogRel(("supdrvOSLdrLoad on %s symbol out of range: %p (%s) \n", pImage->szName, offSymbol, pszSymbol));
break;
}
}
}
else
{
}
return rc;
}
{
# if 1
if (rc)
# else
# endif
}
#else /* !VBOX_WITH_NATIVE_SOLARIS_LOADING */
int VBOXCALL supdrvOSLdrOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const char *pszFilename)
{
return VERR_NOT_SUPPORTED;
}
int VBOXCALL supdrvOSLdrValidatePointer(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, void *pv, const uint8_t *pbImageBits)
{
return VERR_NOT_SUPPORTED;
}
int VBOXCALL supdrvOSLdrLoad(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage, const uint8_t *pbImageBits)
{
return VERR_NOT_SUPPORTED;
}
{
}
#endif /* !VBOX_WITH_NATIVE_SOLARIS_LOADING */
{
char szMsg[512];
return 0;
}