xpvd.c revision 843e19887f64dde75055cf8842fc4db2171eff45
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Host to hypervisor virtual devices nexus driver
*
* TODO:
* - Add DR IOCTLs
*/
#include <sys/hypervisor.h>
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/mach_intr.h>
#include <sys/evtchn_impl.h>
#include <sys/bootconf.h>
#include <sys/bootsvcs.h>
#include <sys/bootinfo.h>
/*
* DDI dev_ops entrypoints
*/
/*
* NDI bus_ops entrypoints
*/
void *);
ddi_intr_handle_impl_t *, void *);
int, char *, caddr_t, int *);
void *, dev_info_t **);
void *);
char *, ddi_eventcookie_t *);
ddi_eventcookie_t, void (*)(dev_info_t *,
ddi_eventcookie_t, void *, void *),
void *, ddi_callback_id_t *);
ddi_eventcookie_t, void *);
/*
* misc functions
*/
static int xpvd_removechild(dev_info_t *);
static int xpvd_initchild(dev_info_t *);
static int xpvd_name_child(dev_info_t *, char *, int);
domid_t *, int *);
/* Extern declarations */
psm_intr_op_t, int *);
struct bus_ops xpvd_bus_ops = {
NULL,
NULL,
NULL,
0, /* (*bus_intr_ctl)(); */
NULL, /* (*bus_fm_init)(); */
NULL, /* (*bus_fm_fini)(); */
NULL, /* (*bus_fm_access_enter)(); */
NULL, /* (*bus_fm_access_exit)(); */
NULL, /* (*bus_power)(); */
xpvd_intr_ops /* (*bus_intr_op)(); */
};
DEVO_REV, /* devo_rev */
0, /* refcnt */
xpvd_info, /* info */
nulldev, /* identify */
nulldev, /* probe */
xpvd_attach, /* attach */
xpvd_detach, /* detach */
nulldev, /* reset */
(struct cb_ops *)0, /* driver operations */
&xpvd_bus_ops /* bus operations */
};
#define CF_DBG 0x1
#define ALL_DBG 0xff
static ndi_event_definition_t xpvd_ndi_event_defs[] = {
};
#define XENDEV_N_NDI_EVENTS \
(sizeof (xpvd_ndi_event_defs) / sizeof (xpvd_ndi_event_defs[0]))
static ndi_event_set_t xpvd_ndi_events = {
};
static ndi_event_hdl_t xpvd_ndi_event_handle;
/*
* Hypervisor interrupt capabilities
*/
#define XENDEV_INTR_CAPABILITIES \
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module */
"virtual device nexus driver %I%",
&xpvd_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
/* ARGSUSED */
static int
{
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2DEVINFO:
return (DDI_SUCCESS);
}
}
/*ARGSUSED*/
static int
{
extern void xvdi_watch_devices(int);
NDI_SLEEP) != NDI_SUCCESS) {
return (DDI_FAILURE);
}
NDI_SLEEP) != NDI_SUCCESS) {
(void) ndi_event_free_hdl(xpvd_ndi_event_handle);
return (DDI_FAILURE);
}
/* watch both frontend and backend for new devices */
if (DOMAIN_IS_INITDOMAIN(xen_info))
else
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
return (DDI_FAILURE);
}
/*
* xpvd_prop_op()
*
* Query xenstore for the value of properties if DDI_PROP_NOTPROM
* is not set. Xenstore property values are represented as ascii strings.
*/
static int
int *lengthp)
{
struct xendev_ppd *pdp;
void *prop_str;
unsigned int len;
int rv;
goto toss_off;
/*
* First try reading the property off the the frontend. if that
* fails, try and read it from the backend node. If that
* also fails, pass the request on the DDI framework
*/
goto got_xs_prop;
goto got_xs_prop;
switch (prop_op) {
case PROP_LEN:
break;
case PROP_LEN_AND_VAL_ALLOC:
break;
case PROP_LEN_AND_VAL_BUF:
break;
default:
break;
}
}
return (rv);
}
/*
* return address of the device's interrupt spec structure.
*/
/*ARGSUSED*/
struct intrspec *
{
struct xendev_ppd *pdp;
return (NULL);
}
/*
* return (and determine) the interrupt priority of the device.
*/
/*ARGSUSED*/
static int
{
struct xendev_ppd *pdp;
int *intpriorities;
(void *)dip));
return (DDI_FAILURE);
/*
* Set the default priority based on the device class. The
* "interrupt-priorities" property can be used to override
* the default.
*/
if (ispec->intrspec_pri == 0) {
"interrupt-priorities", &intpriorities,
&num_intpriorities) == DDI_PROP_SUCCESS) {
}
}
return (DDI_SUCCESS);
}
/*
* xpvd_intr_ops: bus_intr_op() function for interrupt support
*/
/* ARGSUSED */
static int
{
int priority = 0;
struct xendev_ppd *pdp;
"xpvd_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
/* Process the request */
switch (intr_op) {
/* Fixed supported by default */
*(int *)result = DDI_INTR_TYPE_FIXED;
break;
case DDI_INTROP_NINTRS:
*(int *)result = 1;
break;
case DDI_INTROP_ALLOC:
/*
* FIXED interrupts: just return available interrupts
*/
/*
* event channels are edge-triggered, maskable,
* and support int pending.
*/
} else {
return (DDI_FAILURE);
}
break;
case DDI_INTROP_FREE:
return (DDI_FAILURE);
break;
case DDI_INTROP_GETPRI:
return (DDI_FAILURE);
priority));
break;
case DDI_INTROP_SETPRI:
/* Validate the interrupt priority passed */
if (*(int *)result > LOCK_LEVEL)
return (DDI_FAILURE);
/* Ensure that PSM is all initialized */
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
/* Change the priority */
return (DDI_FAILURE);
return (DDI_FAILURE);
break;
case DDI_INTROP_ADDISR:
/* update ispec */
return (DDI_FAILURE);
break;
case DDI_INTROP_REMISR:
if (ispec) {
ispec->intrspec_vec = 0;
}
break;
case DDI_INTROP_GETCAP:
/*
* event channels are edge-triggered, maskable,
* and support int pending.
*/
*(int *)result = XENDEV_INTR_CAPABILITIES;
} else {
*(int *)result = 0;
return (DDI_FAILURE);
}
*(int *)result));
break;
case DDI_INTROP_SETCAP:
*(int *)result));
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
" returned failure\n"));
return (DDI_FAILURE);
}
break;
case DDI_INTROP_ENABLE:
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
return (DDI_FAILURE);
break;
case DDI_INTROP_DISABLE:
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
break;
case DDI_INTROP_BLOCKENABLE:
case DDI_INTROP_BLOCKDISABLE:
return (DDI_FAILURE);
case DDI_INTROP_SETMASK:
case DDI_INTROP_CLRMASK:
/*
* Handle this here
*/
return (DDI_FAILURE);
if (intr_op == DDI_INTROP_SETMASK) {
} else {
}
break;
case DDI_INTROP_GETPENDING:
return (DDI_FAILURE);
*(int *)result));
break;
case DDI_INTROP_NAVAIL:
*(int *)result = 1;
*(int *)result));
break;
default:
}
return (DDI_SUCCESS);
}
static int
{
int vector;
return (DDI_FAILURE);
/* translate the interrupt if needed */
/* Add the interrupt handler */
return (DDI_FAILURE);
/* Note this really is an irq. */
return (DDI_SUCCESS);
}
static void
{
int vector;
return;
/* translate the interrupt if needed */
/* Disable the interrupt handler */
}
/*ARGSUSED*/
static int
{
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
if (rdip == (dev_info_t *)0)
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_CTLOPS_INITCHILD:
case DDI_CTLOPS_UNINITCHILD:
case DDI_CTLOPS_SIDDEV:
return (DDI_SUCCESS);
case DDI_CTLOPS_REGSIZE:
case DDI_CTLOPS_NREGS:
return (DDI_FAILURE);
case DDI_CTLOPS_POWER: {
}
default:
}
/* NOTREACHED */
}
/*
* Assign the address portion of the node name
*/
static int
{
char *unit_address;
/*
* i_xpvd_parse_devname() knows the formats used by this
* routine. If this code changes, so must that.
*/
return (DDI_FAILURE);
/*
* Use "domain" and "vdev" properties (backend drivers).
*/
if (*domain != DOMID_SELF) {
!= DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
*/
== DDI_PROP_SUCCESS) {
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
char name[80];
/*
* Pseudo nodes indicate a prototype node with per-instance
* properties to be merged into the real h/w device node.
*/
if (ndi_dev_is_persistent_node(child) == 0) {
/*
* Try to merge the properties from this prototype
* node into real h/w nodes.
*/
/*
* Merged ok - return failure to remove the node.
*/
return (DDI_FAILURE);
}
/*
* The child was not merged into a h/w node,
* but there's not much we can do with it other
* than return failure to cause the node to be removed.
*/
return (DDI_NOT_WELL_FORMED);
}
return (DDI_FAILURE);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
/*
* Strip the node to properly convert it back to prototype
* form.
*/
return (DDI_SUCCESS);
}
static int
void *device_name)
{
}
/*
* Given the name of a child of xpvd, determine the device class,
* domain and vdevnum to which it refers.
*/
static boolean_t
{
goto done;
}
if (*devclassp < 0) {
goto done;
}
/*
* Parsing the address component requires knowledge of how
* xpvd_name_child() works. If that code changes, so must
* this.
*/
/* Backend format is "<domain>,<vdev>". */
goto done;
}
/* Frontend format is "<vdev>". */
*domp = DOMID_SELF;
goto done;
}
done:
return (ret);
}
/*
* xpvd_bus_config()
*
* BUS_CONFIG_ONE:
* Enumerate the exact instance of a driver.
*
* BUS_CONFIG_ALL:
* Enumerate all the instances of all the possible children (seen before
* and never seen before).
*
* BUS_CONFIG_DRIVER:
* Enumerate all the instances of a particular driver.
*/
static int
{
int circ;
switch (op) {
case BUS_CONFIG_ONE: {
int vdev;
return (NDI_FAILURE);
}
return (NDI_FAILURE);
else
}
case BUS_CONFIG_DRIVER: {
return (NDI_FAILURE);
} else {
}
/* NOTREACHED */
}
case BUS_CONFIG_ALL:
default:
return (NDI_FAILURE);
}
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
cb_id));
}
/*ARGSUSED*/
static int
{
cookie, bus_impldata));
}