bmc_fe.c revision 147982cb800a90a2ac06d00d9a79ac50b0ca4ddb
/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* IPMI: front-end to BMC access
*/
#include <sys/dditypes.h>
#include <sys/sysmacros.h>
#include <sys/bmc_intf.h>
#include "bmc_fe.h"
int bmc_debug = 0;
#define BMC_NUM_CMDS 256
typedef struct bmc_clone {
} bmc_clone_t;
#define BMC_CLONE(x) ((bmc_clone_t *)(x))
static void *ipmi_state;
static bmc_clone_t *bmc_clones;
static int bmc_nclones;
/*ARGSUSED*/
static void
{
/* mptr points to the the data the user passed down */
/* Consolidate multiple mblk_t's used to build this message */
if (mptr) {
goto mioctl_exit;
}
}
/* Make sure that the user passed in something */
goto mioctl_exit;
}
/* Don't allow transparent ioctls */
goto mioctl_exit;
}
goto mioctl_exit;
}
case IOCTL_IPMI_KCS_ACTION: /* DEPRECATED */
/*
* If the user has provided at least enough space to hold
* the interface type, then return it. Otherwise, bail
* out with an error.
*/
} else {
"IOCTL_IPMI_INTERFACE_METHOD: Not enough data"
" supplied to ioctl");
}
break;
default:
break;
}
}
static int
{
/* We're expecting a message with data here */
case M_DATA:
/* Queue for later processing */
}
break;
case M_IOCTL:
/* Process the I_STR ioctl() from user land */
bmc_mioctl(q, mp);
break;
case M_FLUSH:
/*
* Flush processing is a requirement of streams drivers and
* modules.
*
* The bmc driver does not use the read queue, so M_FLUSH
* handling consists of passing a read flush message back
* up the read side of the queue to any modules that may
* be residing above it as well as clearing the write queue,
* if requested.
*
*/
}
} else
/* No read processing required. Throw away message */
break;
default:
"Message not understood. Ignoring. db_type = %d",
break;
}
return (0);
}
/*
* Write-size queue processing
*
* Process data messages and perform BMC operations as directed.
*/
static int
{
/* We only queued M_DATA messages */
/*
* If we wouldn't be able to put a message upstream, hold
* off on processing this message and but it back on our
* write queue. We'll get scheduled later and check the
* state of our upstream partner at that time.
*/
if (!canputnext(rq)) {
/* If putbq fails, free the message */
break;
}
/*
* Process this message. Any replies will not reuse
* mp, so discard it after the message is processed.
*/
if (intr) {
break;
}
}
return (0);
}
/*ARGSUSED*/
static int
{
int c;
if (sflag) {
/* Clone open NOT supported here */
return (ENXIO);
}
return (ENXIO);
}
/*
* Locate and reserve a clone structure. We skip clone 0 as that is
* the real minor number, and we assign a new minor to each clone.
*/
for (c = 0; c < bmc_nclones; c++) {
break;
}
}
if (c >= bmc_nclones)
return (EAGAIN);
/* Init q data pointers */
qprocson(q); /* Turn on the q */
return (0);
}
/*ARGSUSED*/
static int
{
qprocsoff(q); /* Turn the q off */
return (0);
}
static int
{
== BMC_SUCCESS) {
/* check for the version */
goto getbmcversions_error;
}
}
return (BMC_SUCCESS);
}
if (*intr)
else
return (BMC_FAILURE);
}
static int
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
return (DDI_FAILURE);
return (DDI_FAILURE);
}
/* LDI initialization */
goto cleanup_on_fail;
}
/* Try twice to get the BMC version */
goto cleanup_on_fail;
}
}
goto cleanup_on_fail;
}
return (DDI_SUCCESS);
(void) vc_uninit();
return (DDI_FAILURE);
}
static int
{
int instance;
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
(void) vc_uninit();
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
return (DDI_FAILURE);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_FAILURE;
} else {
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*PRINTFLIKE2*/
void
{
#ifdef DEBUG
if (d <= bmc_debug) {
}
#endif
}
static boolean_t
{
int i;
/*
* BMC commands are grouped by function (netFn).
* The commands implemented within each function
* group are tabulated, together with their associated
* privilege level in the bmc_netfn* arrays.
*
* Currently two privilege levels are defined:
* BMC_REQ_NORM permits global access to this command
* BMC_REQ_PRIV permits privileged (sys_admin) access
* to this command.
*
* bmc_command_requires_privilege() returns B_FALSE in the case
* that global access is permitted and B_TRUE in the case
* that sys_admin privileges are required.
*
* Future IPMI implementations may add further function
* groups and further commands to existing function groups.
* In the case that an unknown function group is specified,
* and in the case that an unknown command within an existing
* function group is specified, B_TRUE is returned.
*/
switch (netFn) {
case BMC_NETFN_CHASSIS:
break;
case BMC_NETFN_BRIDGE:
break;
case BMC_NETFN_SE:
break;
case BMC_NETFN_APP:
break;
case BMC_NETFN_STORAGE:
break;
case BMC_NETFN_TRANSPORT:
break;
default:
return (B_TRUE); /* Unknown function group */
}
}
return (B_TRUE); /* Unknown command */
}
/*
* Send an error code upstream
* Used to signal system-related errors to the stream head
* Use sparingly, as sending an M_ERROR wakes up all processes
* sleeping on system calls to this device and is semi-permanent.
*
* q: an upward-facing queue (read-side)
*/
static void
{
}
}
/*
* Process a message sent from the user.
*
* q passed in is the WRITE side.
*/
static mblk_t *
{
int response_allocsz;
int msgsize;
/* Construct contiguous message so we can access its fields below */
return (origmp);
}
/* Done with the original message; the pulled-up message is in mp */
/* The message must be at LEAST as large as a bmc_msg_t */
" size (size was %d, must be at least %lu)", msgsize,
EINVAL);
/* Invalid message -- send an error upstream and return */
return (mp);
}
case BMC_MSG_REQUEST:
/*
* Calculate the payload size (the size of the request
* structure embedded in the bmc_msg_t request) by subtracting
* the size of all members of the bmc_msg_t except for the
* msg field (which is overlayed with the bmc_req_t).
*/
/* Perform some sanity checks on the size of the message */
" size=%lu, payload size=%d, expected size=%lu",
sizeof (bmc_req_t) :
request->datalength)));
/* Send a message to signal an error */
EINVAL);
break;
}
/* Does the command number look OK? */
0, NULL);
break;
}
/*
* Command number's good. Does the messages have a NULL
* cred attached to its first data block, or does this
* command require privileges the user doesn't have?
*
* (This implies that should any STREAMS modules be pushed
* between the stream head and this driver, it must preserve
* the cred added to the original message so that this driver
* can do the appropriate permissions checks).
*/
B_FALSE) != 0)) {
EACCES);
break;
}
"MSG type 0x%x subcmd 0x%x req_len 0x%x",
/* Allocate enough space for the largest response possible */
/*
* If an error occurs during the processing of the command,
* the cause of the error is recorded in the response, so
* ignore the return value and send the response upstream.
*/
if (!*intr) {
"MSG DONE subcmd 0x%x req_len 0x%x rsp_len 0x%x "
"code 0x%x",
}
break;
default:
break;
}
if (!*intr) {
/* Send the reply upstream */
}
return (mp);
}
static mblk_t *
{
switch (mtype) {
case BMC_MSG_ERROR:
/*
* Build an error message. The second parameter is the
* message ID, and the third is the error code.
*/
break;
/* First byte of msg is the error code */
break;
case BMC_MSG_RESPONSE:
/*
* Total message size is (# of bytes before the msg field
* in the bmc_msg_t field) + the full size of the bmc_rsp_t
* structure, including all non-data members + size of the
* data array (variable).
*/
break;
break;
default:
"bmc_build_msg: unknown message type 0x%x!",
mtype);
break;
}
}
return (mp);
}
static struct module_info bmc_minfo = {
0xabcd, /* module id number */
"IPMI bmc driver %I%", /* module name */
0, /* min packet size */
INFPSZ, /* max packet size */
1024, /* hi water mark */
512 /* low water mark */
};
NULL, /* put procedure */
NULL, /* service procedure */
bmc_open, /* open() procedure */
bmc_close, /* close() procedure */
NULL, /* reserved */
&bmc_minfo, /* module information pointer */
NULL /* module stats pointer */
};
bmc_wput, /* put procedure */
bmc_wsrv, /* service procedure */
NULL, /* open() not used on write side */
NULL, /* close() not used on write side */
NULL, /* reserved */
&bmc_minfo, /* module information pointer */
NULL /* module state pointer */
};
struct streamtab bmc_str_info = {
NULL,
};
bmc_ops, \
nulldev, \
nulldev, \
bmc_attach, \
bmc_detach, \
nodev, \
bmc_getinfo, \
&bmc_str_info \
);
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int error;
sizeof (ipmi_state_t), 0)) != 0)
return (error);
if (bmc_nclones <= 0)
}
return (error);
}
int
_fini(void)
{
int error;
}
return (error);
}
int
{
}