/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* sun4v VIO DR Module
*/
#include <sys/hypervisor_api.h>
#include <sys/mach_descrip.h>
#include <sys/mdesc_impl.h>
#include <sys/machsystm.h>
#include <sys/ethernet.h>
"sun4v VIO DR"
};
(void *)&modlmisc,
};
/*
* VIO DS Interface
*/
/*
* Global DS Handle
*/
/*
* Supported DS Capability Versions
*/
/*
* DS Capability Description
*/
DR_VIO_DS_ID, /* svc_id */
dr_vio_vers, /* vers */
DR_VIO_NVERS /* nvers */
};
/*
* DS Callbacks
*/
/*
* DS Client Ops Vector
*/
dr_vio_reg_handler, /* ds_reg_cb */
dr_vio_unreg_handler, /* ds_unreg_cb */
dr_vio_data_handler, /* ds_data_cb */
NULL /* cb_arg */
};
typedef struct {
char *name;
static int
{
char *name;
return (DDI_WALK_CONTINUE);
"reg", -1);
DR_DBG_IO("%s: found devid=%ld, looking for %ld\n",
/* matching node must be returned held */
if (!e_ddi_branch_held(dip))
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
/*
* Walk the device tree to find the dip corresponding to the devid
* passed in. If present, the dip is returned held. The caller must
* release the hold on the dip once it is no longer required. If no
* matching node if found, NULL is returned.
*/
static dev_info_t *
{
DR_DBG_IO("dr_io_find_node...\n");
}
/*
* Look up a particular IO node in the MD. Returns the mde_cookie_t
* representing that IO node if present, and MDE_INVAL_ELEM_COOKIE otherwise.
* It is assumed the scratch array has already been allocated so that
* it can accommodate the worst case scenario, every node in the MD.
*/
static mde_cookie_t
{
int i;
int nnodes;
char *devnm;
/*
* Scan the DAG for all candidate nodes.
*/
if (nnodes < 0) {
DR_DBG_IO("%s: scan for "
"'virtual-device' nodes failed\n", __func__);
return (result);
}
/*
* Find the node of interest
*/
for (i = 0; i < nnodes; i++) {
DR_DBG_IO("%s: missing 'name' property for"
" IO node %d\n", __func__, i);
return (DDI_WALK_ERROR);
}
continue;
DR_DBG_IO("%s: missing 'cfg-handle' property for"
" IO node %d\n", __func__, i);
break;
}
/* found a match */
DR_DBG_IO("%s: found IO node %s@%ld "
break;
}
}
if (result == MDE_INVAL_ELEM_COOKIE)
return (result);
}
typedef struct {
} cb_arg_t;
static int
{
char *compat;
int len = 0;
char *curr;
int i = 0;
/*
* Add 'name' property
*/
return (DDI_WALK_ERROR);
}
return (DDI_WALK_ERROR);
}
/*
* Add 'compatible' property
*/
DR_DBG_IO("%s: failed to read "
"'compatible' prop from MD\n", __func__);
return (DDI_WALK_ERROR);
}
/* parse the MD string array */
DR_DBG_IO("%s: adding '%s' to "
if (i == STR_ARR_LEN) {
break;
}
}
return (DDI_WALK_ERROR);
}
/*
* Add 'device_type' property
*/
DR_DBG_IO("%s: failed to read "
"'device-type' prop from MD\n", __func__);
return (DDI_WALK_ERROR);
}
DR_DBG_IO("%s: failed to create "
"'device-type' prop\n", __func__);
return (DDI_WALK_ERROR);
}
/*
* Add 'reg' (cfg-handle) property
*/
DR_DBG_IO("%s: failed to read "
"'cfg-handle' prop from MD\n", __func__);
return (DDI_WALK_ERROR);
}
!= DDI_SUCCESS) {
return (DDI_WALK_ERROR);
}
int i, j;
&macaddr)) {
DR_DBG_IO("%s: failed to read "
"'local-mac-address' prop from MD\n", __func__);
return (DDI_WALK_ERROR);
}
!= DDI_SUCCESS) {
DR_DBG_IO("%s: failed to create "
"'local-mac-address' prop\n", __func__);
return (DDI_WALK_ERROR);
}
DR_DBG_IO("%s: failed to read "
"'mtu' prop from MD\n", __func__);
return (DDI_WALK_ERROR);
}
mtu) != DDI_SUCCESS) {
DR_DBG_IO("%s: failed to "
"create 'mtu' prop\n", __func__);
return (DDI_WALK_ERROR);
}
DR_DBG_IO("%s: Added properties for %s@%ld, "
}
return (DDI_WALK_TERMINATE);
}
/*
* Find the parent node of the argument virtual device node in
* the MD. For virtual devices, the parent is always
* "channel-devices", so scan the MD using the "back" arcs
* looking for a node with that name.
*/
static mde_cookie_t
{
int max_nodes;
int num_nodes;
int listsz;
DR_DBG_KMEM("%s: alloc addr %p size %d\n",
if (num_nodes == 1)
DR_DBG_KMEM("%s: free addr %p size %d\n",
return (pnode);
}
static int
{
int listsz;
int nnodes;
char *pname;
int drctl_cmd;
int drctl_flags = 0;
char *p;
DR_DBG_IO("%s: %s@%ld already configured\n",
/* Return success if resources is already there. */
return (0);
}
/* Assume we fail to find the node to be added. */
return (ENXIO);
}
DR_DBG_KMEM("%s: alloc addr %p size %d\n",
/*
* Get the MD device node.
*/
if (node == MDE_INVAL_ELEM_COOKIE) {
goto done;
}
/*
* Get the MD parent node.
*/
if (pnode == MDE_INVAL_ELEM_COOKIE) {
DR_DBG_IO("%s: failed to find MD parent of %lx\n",
goto done;
}
DR_DBG_IO("%s: failed to read "
goto done;
}
DR_DBG_IO("%s: failed to read 'cfg-handle' "
goto done;
}
/*
* Get the devinfo parent node.
*/
DR_DBG_IO("%s: parent device %s@%ld not found\n",
goto done;
}
DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
/*
* Construct the path of the device as it will be if it
* is successfully added.
*/
p = drctl_req->res_dev_path;
(void) sprintf(p, "/devices");
if (rv != 0) {
goto done;
}
} else {
}
done:
if (listp) {
DR_DBG_KMEM("%s: free addr %p size %d\n",
}
if (mdp)
(void) md_fini_handle(mdp);
if (pdip)
DR_DBG_KMEM("%s: free addr %p size %ld\n",
if (drctl_resp) {
DR_DBG_KMEM("%s: free addr %p size %ld\n",
}
if (rv == 0) {
/* notify interested parties about the operation */
} else {
}
return (rv);
}
static int
{
int rv;
char *p;
int drctl_cmd;
int drctl_flags = 0;
DR_DBG_IO("%s: %s@%ld already unconfigured\n",
return (0);
}
/* Assume we fail to unconfigure the resource. */
DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
p = drctl_req->res_dev_path;
(void) sprintf(p, "/devices");
if (rv != 0) {
goto done;
}
DR_DBG_KMEM("%s: alloc addr %p size %d\n",
/*
* If non-NULL, fdip is held and must be released.
*/
} else {
}
DR_DBG_IO("%s: node removal failed: %s (%p)",
DR_DBG_KMEM("%s: free addr %p size %d\n",
} else {
}
if (rv == 0) {
/* Notify interested parties about the operation. */
}
done:
DR_DBG_KMEM("%s: free addr %p size %ld\n",
if (drctl_resp) {
DR_DBG_KMEM("%s: free addr %p size %ld\n",
}
return (rv);
}
static void
{
/*
* Allocate a response buffer, because we always want to
* send back a response message.
*/
DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
/*
* Sanity check the message
*/
DR_DBG_IO("empty message: expected at least %ld bytes\n",
sizeof (dr_vio_req_t));
goto done;
}
if (buflen < sizeof (dr_vio_req_t)) {
DR_DBG_IO("incoming message short: expected at least %ld "
goto done;
}
DR_DBG_TRANS("incoming request:\n");
case DR_VIO_CONFIGURE:
break;
case DR_VIO_FORCE_UNCONFIG:
case DR_VIO_UNCONFIGURE:
break;
default:
break;
}
done:
DR_DBG_TRANS("outgoing response:\n");
/* send back the response */
DR_DBG_IO("ds_send failed\n");
if (res) {
DR_DBG_KMEM("%s: free addr %p size %ld\n",
}
}
static void
{
DR_DBG_IO("vio_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
ds_vio_handle = hdl;
}
static void
{
}
static int
dr_io_init(void)
{
int rv;
return (-1);
}
return (0);
}
static int
dr_io_fini(void)
{
int rv;
return (-1);
}
return (0);
}
int
_init(void)
{
int status;
/* check that IO DR is enabled */
if (dr_is_disabled(DR_TYPE_VIO)) {
return (-1);
}
if ((status = dr_io_init()) != 0) {
return (status);
}
(void) dr_io_fini();
}
return (status);
}
int
{
}
int dr_io_allow_unload = 0;
int
_fini(void)
{
int status;
if (dr_io_allow_unload == 0)
return (EBUSY);
(void) dr_io_fini();
}
return (status);
}