summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/devfsadm/sparc/misc_link_sparc.c16
-rw-r--r--usr/src/pkgdefs/SUNWcakr.v/prototype_com4
-rw-r--r--usr/src/pkgdefs/common_files/i.minorperm_sparc1
-rw-r--r--usr/src/pkgdefs/etc/exception_list_i3861
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc1
-rw-r--r--usr/src/uts/common/sys/Makefile1
-rw-r--r--usr/src/uts/common/sys/bmc_cmd.h251
-rw-r--r--usr/src/uts/sparc/os/minor_perm1
-rw-r--r--usr/src/uts/sparc/os/name_to_major1
-rw-r--r--usr/src/uts/sun4v/Makefile.files5
-rw-r--r--usr/src/uts/sun4v/Makefile.rules9
-rw-r--r--usr/src/uts/sun4v/Makefile.sun4v.shared1
-rw-r--r--usr/src/uts/sun4v/bmc/Makefile96
-rw-r--r--usr/src/uts/sun4v/io/bmc/bmc.conf28
-rw-r--r--usr/src/uts/sun4v/io/bmc/bmc_fe.c911
-rw-r--r--usr/src/uts/sun4v/io/bmc/bmc_fe.h73
-rw-r--r--usr/src/uts/sun4v/io/bmc/bmc_vc.c718
17 files changed, 2115 insertions, 3 deletions
diff --git a/usr/src/cmd/devfsadm/sparc/misc_link_sparc.c b/usr/src/cmd/devfsadm/sparc/misc_link_sparc.c
index f93a5212fe..158259d165 100644
--- a/usr/src/cmd/devfsadm/sparc/misc_link_sparc.c
+++ b/usr/src/cmd/devfsadm/sparc/misc_link_sparc.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -45,6 +45,7 @@ static int ddi_cardreader(di_minor_t minor, di_node_t node);
static int starcat_sbbc_node(di_minor_t minor, di_node_t node);
static int lom(di_minor_t minor, di_node_t node);
static int ntwdt_create(di_minor_t minor, di_node_t node);
+static int bmc(di_minor_t minor, di_node_t node);
static devfsadm_create_t misc_cbt[] = {
{ "other", "ddi_other", NULL,
@@ -71,6 +72,9 @@ static devfsadm_create_t misc_cbt[] = {
{ "pseudo", "ddi_pseudo", "ntwdt",
TYPE_EXACT | DRV_EXACT, ILEVEL_0, ntwdt_create
},
+ { "pseudo", "ddi_pseudo", "bmc",
+ TYPE_EXACT | DRV_EXACT, ILEVEL_0, bmc
+ }
};
DEVFSADM_CREATE_INIT_V0(misc_cbt);
@@ -296,3 +300,13 @@ ntwdt_create(di_minor_t minor, di_node_t node)
(void) devfsadm_mklink("ntwdt", node, minor, 0);
return (DEVFSADM_CONTINUE);
}
+
+/*
+ * Creates /dev/bmc node.
+ */
+static int
+bmc(di_minor_t minor, di_node_t node)
+{
+ (void) devfsadm_mklink("bmc", node, minor, 0);
+ return (DEVFSADM_CONTINUE);
+}
diff --git a/usr/src/pkgdefs/SUNWcakr.v/prototype_com b/usr/src/pkgdefs/SUNWcakr.v/prototype_com
index d5236b47f4..1a2bc28956 100644
--- a/usr/src/pkgdefs/SUNWcakr.v/prototype_com
+++ b/usr/src/pkgdefs/SUNWcakr.v/prototype_com
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -56,11 +56,13 @@ f none platform/sun4v/kernel/crypto/sparcv9/md5 755 root sys
f none platform/sun4v/kernel/crypto/sparcv9/arcfour 755 root sys
d none platform/sun4v/kernel/drv 755 root sys
f none platform/sun4v/kernel/drv/bge.conf 644 root sys
+f none platform/sun4v/kernel/drv/bmc.conf 644 root sys
f none platform/sun4v/kernel/drv/trapstat.conf 644 root sys
f none platform/sun4v/kernel/drv/ncp.conf 644 root sys
f none platform/sun4v/kernel/drv/n2rng.conf 644 root sys
d none platform/sun4v/kernel/drv/sparcv9 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/bge 755 root sys
+f none platform/sun4v/kernel/drv/sparcv9/bmc 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/ebus 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/dma 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/px 755 root sys
diff --git a/usr/src/pkgdefs/common_files/i.minorperm_sparc b/usr/src/pkgdefs/common_files/i.minorperm_sparc
index 493fd08629..5eec85cdc5 100644
--- a/usr/src/pkgdefs/common_files/i.minorperm_sparc
+++ b/usr/src/pkgdefs/common_files/i.minorperm_sparc
@@ -336,6 +336,7 @@ physmem:*
smbsrv:*
vscan:*
nsmb:*
+bmc:bmc
EOF
}
diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386
index 02ef16aa77..8aae8515d1 100644
--- a/usr/src/pkgdefs/etc/exception_list_i386
+++ b/usr/src/pkgdefs/etc/exception_list_i386
@@ -847,6 +847,7 @@ usr/lib/amd64/llib-lshell.ln i386
#
# bmc (IPMI) interfaces shared within ON.
#
+usr/include/sys/bmc_cmd.h i386
usr/include/sys/bmc_intf.h i386
#
# These files are used by the iSCSI Target which is in this consolidation
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc
index 4f281fada4..0c266199ef 100644
--- a/usr/src/pkgdefs/etc/exception_list_sparc
+++ b/usr/src/pkgdefs/etc/exception_list_sparc
@@ -921,6 +921,7 @@ usr/lib/sparcv9/llib-lshell.ln sparc
#
# bmc (IPMI) interfaces shared within ON.
#
+usr/include/sys/bmc_cmd.h sparc
usr/include/sys/bmc_intf.h sparc
#
# This file is used in ON to build DSCP clients. It is not for customers.
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index d0d531088f..3b4dcb3ef7 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -101,6 +101,7 @@ CHKHDRS= \
bitmap.h \
bitset.h \
bl.h \
+ bmc_cmd.h \
bmc_intf.h \
bofi.h \
bofi_impl.h \
diff --git a/usr/src/uts/common/sys/bmc_cmd.h b/usr/src/uts/common/sys/bmc_cmd.h
new file mode 100644
index 0000000000..75e6f2b9db
--- /dev/null
+++ b/usr/src/uts/common/sys/bmc_cmd.h
@@ -0,0 +1,251 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+#ifndef _BMC_CMD_H
+#define _BMC_CMD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct command_priv_level {
+ uint8_t command;
+ uint8_t req_level;
+} bmc_command_priv_level_t;
+
+enum {
+ BMC_REQ_NORM, /* Normal request */
+ BMC_REQ_PRIV, /* Privileged request */
+ BMC_END_OF_LIST /* End of command list marker */
+};
+
+/*
+ * IPMI Command Privilege Level Tables
+ *
+ * These tables associate a privilege level with each of the supported
+ * IPMI commands.
+ *
+ * IPMI commands are grouped by net function (netFn), and a Command
+ * Privilege Level Table is provided for each of the supported net
+ * functions.
+ *
+ * Two levels of privilege are supported: global access, BMC_REQ_NORM;
+ * and sys_admin privilege, BMC_REQ_PRIV.
+ * The assignment of privilege level to command follows the recommendations
+ * in the IPMI Specification V2.0, Appendix G.
+ */
+bmc_command_priv_level_t bmc_netfn_chassis[] = {
+ { 0x00, BMC_REQ_NORM }, /* Get Chassis Capabilities */
+ { 0x01, BMC_REQ_NORM }, /* Get Chassis Status */
+ { 0x02, BMC_REQ_PRIV }, /* Chassis Control */
+ { 0x03, BMC_REQ_PRIV }, /* Chassis Reset */
+ { 0x04, BMC_REQ_PRIV }, /* Chassis Identify */
+ { 0x05, BMC_REQ_PRIV }, /* Set Chassis Capabilities */
+ { 0x06, BMC_REQ_PRIV }, /* Set Power Restore Policy */
+ { 0x07, BMC_REQ_NORM }, /* Get System Restart Cause */
+ { 0x08, BMC_REQ_PRIV }, /* Set System Boot Options */
+ { 0x09, BMC_REQ_PRIV }, /* Get System Boot Options */
+ { 0x0a, BMC_REQ_PRIV }, /* Set Front Panel Enables */
+ { 0x0b, BMC_REQ_PRIV }, /* Set Power Cycle Interval */
+ { 0x0f, BMC_REQ_NORM }, /* Get POH Counter */
+ { 0xff, BMC_END_OF_LIST }
+};
+
+bmc_command_priv_level_t bmc_netfn_bridge[] = {
+ { 0x00, BMC_REQ_NORM }, /* Get Bridge State */
+ { 0x01, BMC_REQ_PRIV }, /* Set Bridge State */
+ { 0x02, BMC_REQ_NORM }, /* Get ICMB Address */
+ { 0x03, BMC_REQ_PRIV }, /* Set ICMB Address */
+ { 0x04, BMC_REQ_PRIV }, /* Set Bridge Proxy Address */
+ { 0x05, BMC_REQ_NORM }, /* Get Bridge Statistics */
+ { 0x06, BMC_REQ_NORM }, /* Get ICMB Capabilities */
+ { 0x08, BMC_REQ_PRIV }, /* Clear Bridge Statistics */
+ { 0x09, BMC_REQ_NORM }, /* Get Bridge Proxy Address */
+ { 0x0a, BMC_REQ_NORM }, /* Get ICMB Connector Info */
+ { 0x0b, BMC_REQ_NORM }, /* Get ICMB Connection ID */
+ { 0x0c, BMC_REQ_NORM }, /* Send ICMB Connection ID */
+ { 0x10, BMC_REQ_PRIV }, /* Prepare for Discovery */
+ { 0x11, BMC_REQ_NORM }, /* Get Addresses */
+ { 0x12, BMC_REQ_PRIV }, /* Set Discovered */
+ { 0x13, BMC_REQ_NORM }, /* Get Chassis Device ID */
+ { 0x14, BMC_REQ_PRIV }, /* Set Chassis Device ID */
+ { 0x20, BMC_REQ_PRIV }, /* Bridge Request */
+ { 0x21, BMC_REQ_PRIV }, /* Bridge Message */
+ { 0x30, BMC_REQ_NORM }, /* Get Event Count */
+ { 0x31, BMC_REQ_PRIV }, /* Set Event Destination */
+ { 0x32, BMC_REQ_PRIV }, /* Set Event Reception State */
+ { 0x33, BMC_REQ_PRIV }, /* Send ICMB Event Message */
+ { 0x34, BMC_REQ_NORM }, /* Get Event Destination */
+ { 0x35, BMC_REQ_NORM }, /* Get Event Reception State */
+ { 0xff, BMC_END_OF_LIST }
+};
+
+bmc_command_priv_level_t bmc_netfn_se[] = {
+ { 0x00, BMC_REQ_PRIV }, /* Set Event Receiver */
+ { 0x01, BMC_REQ_NORM }, /* Get Event Receiver */
+ { 0x02, BMC_REQ_PRIV }, /* Platform Event */
+ { 0x10, BMC_REQ_NORM }, /* Get PEF Capabilities */
+ { 0x11, BMC_REQ_PRIV }, /* Arm PEF Postpone Timer */
+ { 0x12, BMC_REQ_PRIV }, /* Set PEF Conf Parameters */
+ { 0x13, BMC_REQ_PRIV }, /* Get PEF Conf Parameters */
+ { 0x14, BMC_REQ_PRIV }, /* Set Last Processed Event ID */
+ { 0x15, BMC_REQ_PRIV }, /* Get Last Processed Event ID */
+ { 0x16, BMC_REQ_PRIV }, /* Alert Immediate */
+ { 0x17, BMC_REQ_PRIV }, /* PET Acknowledge */
+ { 0x20, BMC_REQ_NORM }, /* Get Device SDR Info */
+ { 0x21, BMC_REQ_NORM }, /* Get Device SDR */
+ { 0x22, BMC_REQ_NORM }, /* Reserve Device SDR Repository */
+ { 0x23, BMC_REQ_NORM }, /* Get Sensor Reading Factors */
+ { 0x24, BMC_REQ_PRIV }, /* Set Sensor Hysteresis */
+ { 0x25, BMC_REQ_NORM }, /* Get Sensor Hysterisis */
+ { 0x26, BMC_REQ_PRIV }, /* Set Sensor Threshold */
+ { 0x27, BMC_REQ_NORM }, /* Get Sensor Threshold */
+ { 0x28, BMC_REQ_PRIV }, /* Set Sensor Event Enable */
+ { 0x29, BMC_REQ_NORM }, /* Get Sensor Event Enable */
+ { 0x2a, BMC_REQ_PRIV }, /* Re-arm Sensor Events */
+ { 0x2b, BMC_REQ_NORM }, /* Get Sensor Event Status */
+ { 0x2d, BMC_REQ_NORM }, /* Get Sensor Reading */
+ { 0x2e, BMC_REQ_PRIV }, /* Set Sensor Type */
+ { 0x2f, BMC_REQ_NORM }, /* Get Event Reception State */
+ { 0xff, BMC_END_OF_LIST }
+};
+
+bmc_command_priv_level_t bmc_netfn_app[] = {
+ { 0x01, BMC_REQ_NORM }, /* Get Device ID */
+ { 0x02, BMC_REQ_PRIV }, /* Cold Reset */
+ { 0x03, BMC_REQ_PRIV }, /* Warm Reset */
+ { 0x04, BMC_REQ_NORM }, /* Get Self Test Results */
+ { 0x05, BMC_REQ_PRIV }, /* Manufacturing Test On */
+ { 0x06, BMC_REQ_PRIV }, /* Set ACPI Power State */
+ { 0x07, BMC_REQ_NORM }, /* Get ACPI Power State */
+ { 0x08, BMC_REQ_NORM }, /* Get Device GUID */
+ { 0x22, BMC_REQ_PRIV }, /* Reset Watchdog Timer */
+ { 0x24, BMC_REQ_PRIV }, /* Set Watchdog Timer */
+ { 0x25, BMC_REQ_NORM }, /* Get Watchdog Timer */
+ { 0x2e, BMC_REQ_PRIV }, /* Set BMC Global Enables */
+ { 0x2f, BMC_REQ_NORM }, /* Get BMC Global Enables */
+ { 0x30, BMC_REQ_PRIV }, /* Clear Message Flags */
+ { 0x31, BMC_REQ_PRIV }, /* Get Message Flags */
+ { 0x32, BMC_REQ_PRIV }, /* Enable Message Channel Receive */
+ { 0x33, BMC_REQ_PRIV }, /* Get Message */
+ { 0x34, BMC_REQ_NORM }, /* Send Message */
+ { 0x35, BMC_REQ_PRIV }, /* Read Event Message Buffer */
+ { 0x36, BMC_REQ_NORM }, /* Get BT Interface Capabilities */
+ { 0x37, BMC_REQ_NORM }, /* Get System GUID */
+ { 0x38, BMC_REQ_NORM }, /* Get Channel Auth Capabilities */
+ { 0x39, BMC_REQ_NORM }, /* Get Session Challenge */
+ { 0x3a, BMC_REQ_NORM }, /* Activate Session */
+ { 0x3b, BMC_REQ_NORM }, /* Set Session Privilege Level */
+ { 0x3c, BMC_REQ_PRIV }, /* Close Session */
+ { 0x3d, BMC_REQ_NORM }, /* Get Session Info */
+ { 0x3f, BMC_REQ_PRIV }, /* Get Auth Code */
+ { 0x40, BMC_REQ_PRIV }, /* Set Channel Access */
+ { 0x41, BMC_REQ_NORM }, /* Get Channel Access */
+ { 0x42, BMC_REQ_NORM }, /* Get Channel Info Command */
+ { 0x43, BMC_REQ_PRIV }, /* Set User Access Command */
+ { 0x44, BMC_REQ_PRIV }, /* Get User Access Command */
+ { 0x45, BMC_REQ_PRIV }, /* Set User Name */
+ { 0x46, BMC_REQ_PRIV }, /* Get User Name Command */
+ { 0x47, BMC_REQ_PRIV }, /* Set User Password Command */
+ { 0x48, BMC_REQ_PRIV }, /* Activate Payload */
+ { 0x49, BMC_REQ_PRIV }, /* Deactivate Payload */
+ { 0x4a, BMC_REQ_NORM }, /* Get Payload Activation Status */
+ { 0x4b, BMC_REQ_NORM }, /* Get Payload Instance Info */
+ { 0x4c, BMC_REQ_PRIV }, /* Set User Payload Access */
+ { 0x4d, BMC_REQ_PRIV }, /* Get User Payload Access */
+ { 0x4e, BMC_REQ_NORM }, /* Get Channel Payload Support */
+ { 0x4f, BMC_REQ_NORM }, /* Get Channel Payload Version */
+ { 0x50, BMC_REQ_NORM }, /* Get Channel Payload OEM Info */
+ { 0x52, BMC_REQ_PRIV }, /* Master Write-Read */
+ { 0x54, BMC_REQ_NORM }, /* Get Channel Cipher Suites */
+ { 0x55, BMC_REQ_NORM }, /* Suspend/Resume Payload Encryption */
+ { 0x56, BMC_REQ_PRIV }, /* Set Channel Security Keys */
+ { 0x57, BMC_REQ_NORM }, /* Get System Interface Capabilities */
+ { 0xff, BMC_END_OF_LIST }
+};
+
+bmc_command_priv_level_t bmc_netfn_storage[] = {
+ { 0x10, BMC_REQ_NORM }, /* Get FRU Inventory Area Info */
+ { 0x11, BMC_REQ_NORM }, /* Read FRU Data */
+ { 0x12, BMC_REQ_PRIV }, /* Write FRU Data */
+ { 0x20, BMC_REQ_NORM }, /* Get SDR Repository Info */
+ { 0x21, BMC_REQ_NORM }, /* Get SDR Repository Alloc Info */
+ { 0x22, BMC_REQ_NORM }, /* Reserve SDR Repository */
+ { 0x23, BMC_REQ_NORM }, /* Get SDR */
+ { 0x24, BMC_REQ_PRIV }, /* Add SDR */
+ { 0x25, BMC_REQ_PRIV }, /* Partial Add SDR */
+ { 0x26, BMC_REQ_PRIV }, /* Delete SDR */
+ { 0x27, BMC_REQ_PRIV }, /* Clear SDR Repository */
+ { 0x28, BMC_REQ_NORM }, /* Get SDR Repository Time */
+ { 0x29, BMC_REQ_PRIV }, /* Set SDR Repository Time */
+ { 0x2a, BMC_REQ_PRIV }, /* Enter SDR Repository Update Mode */
+ { 0x2b, BMC_REQ_PRIV }, /* Exit SDR Repository Update Mode */
+ { 0x2c, BMC_REQ_PRIV }, /* Run Initialization Agent */
+ { 0x40, BMC_REQ_NORM }, /* Get SEL Info */
+ { 0x41, BMC_REQ_NORM }, /* Get SEL Allocation Info */
+ { 0x42, BMC_REQ_NORM }, /* Reserve SEL */
+ { 0x43, BMC_REQ_NORM }, /* Get SEL Entry */
+ { 0x44, BMC_REQ_PRIV }, /* Add SEL Entry */
+ { 0x45, BMC_REQ_PRIV }, /* Partial Add SEL Entry */
+ { 0x46, BMC_REQ_PRIV }, /* Delete SEL Entry */
+ { 0x47, BMC_REQ_PRIV }, /* Clear SEL */
+ { 0x48, BMC_REQ_NORM }, /* Get SEL Time */
+ { 0x49, BMC_REQ_PRIV }, /* Set SEL Time */
+ { 0x5a, BMC_REQ_NORM }, /* Get Auxiliary Log Status */
+ { 0x5b, BMC_REQ_PRIV }, /* Set Auxiliary Log Status */
+ { 0xff, BMC_END_OF_LIST }
+};
+
+bmc_command_priv_level_t bmc_netfn_transport[] = {
+ { 0x01, BMC_REQ_PRIV }, /* Set LAN Configuration Parameters */
+ { 0x02, BMC_REQ_PRIV }, /* Get LAN Configuration Parameters */
+ { 0x03, BMC_REQ_PRIV }, /* Suspend BMC ARPs */
+ { 0x04, BMC_REQ_NORM }, /* Get IP/UDP/RMCP Statistics */
+ { 0x10, BMC_REQ_PRIV }, /* Set Serial/Modem Configuration */
+ { 0x11, BMC_REQ_PRIV }, /* Get Serial/Modem Configuration */
+ { 0x12, BMC_REQ_PRIV }, /* Set Serial/Modem Mux */
+ { 0x13, BMC_REQ_NORM }, /* Get TAP Response Codes */
+ { 0x14, BMC_REQ_PRIV }, /* Set PPP UDP Proxy Transmit Data */
+ { 0x15, BMC_REQ_PRIV }, /* Get PPP UDP Proxy Transmit Data */
+ { 0x16, BMC_REQ_PRIV }, /* Send PPP UDP Proxy Packet */
+ { 0x17, BMC_REQ_PRIV }, /* Get PPP UDP Proxy Receive Data */
+ { 0x18, BMC_REQ_PRIV }, /* Serial/Modem Connection Active */
+ { 0x19, BMC_REQ_PRIV }, /* Callback */
+ { 0x1a, BMC_REQ_PRIV }, /* Set User Callback Options */
+ { 0x1b, BMC_REQ_NORM }, /* Get User Callback Options */
+ { 0x20, BMC_REQ_PRIV }, /* SOL Activating */
+ { 0x21, BMC_REQ_PRIV }, /* Set SOL Configuration Parameters */
+ { 0x22, BMC_REQ_NORM }, /* Get SOL Configuration Parameters */
+ { 0xff, BMC_END_OF_LIST }
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BMC_CMD_H */
diff --git a/usr/src/uts/sparc/os/minor_perm b/usr/src/uts/sparc/os/minor_perm
index d56b37858a..6697b448f4 100644
--- a/usr/src/uts/sparc/os/minor_perm
+++ b/usr/src/uts/sparc/os/minor_perm
@@ -181,3 +181,4 @@ vnic:* 0666 root sys
physmem:* 0600 root sys
sdp:sdp 0666 root sys
nsmb:* 0666 root sys
+bmc:bmc 0666 root sys
diff --git a/usr/src/uts/sparc/os/name_to_major b/usr/src/uts/sparc/os/name_to_major
index 922035ceb8..a906d70f5b 100644
--- a/usr/src/uts/sparc/os/name_to_major
+++ b/usr/src/uts/sparc/os/name_to_major
@@ -223,3 +223,4 @@ vscan 274
softmac 275
nsmb 276
mem_cache 277
+bmc 278
diff --git a/usr/src/uts/sun4v/Makefile.files b/usr/src/uts/sun4v/Makefile.files
index c9ca629f68..2d6bbad4a3 100644
--- a/usr/src/uts/sun4v/Makefile.files
+++ b/usr/src/uts/sun4v/Makefile.files
@@ -135,6 +135,11 @@ MEMTEST_OBJS += memtest.o memtest_asm.o \
memtest_vf.o
#
+# sun4v /dev/bmc
+#
+BMC_OBJS = bmc_fe.o bmc_vc.o
+
+#
# sun4v virtual devices
#
QCN_OBJS = qcn.o
diff --git a/usr/src/uts/sun4v/Makefile.rules b/usr/src/uts/sun4v/Makefile.rules
index 0328c614fd..29804afe9d 100644
--- a/usr/src/uts/sun4v/Makefile.rules
+++ b/usr/src/uts/sun4v/Makefile.rules
@@ -20,7 +20,7 @@
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -69,6 +69,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/sun4v/promif/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/sun4v/io/bmc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/sun4v/io/px/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -161,6 +165,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/sun4v/cpu/%.s
$(LINTS_DIR)/%.ln: $(UTSBASE)/sun4v/io/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/sun4v/io/bmc/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/sun4v/io/px/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/sun4v/Makefile.sun4v.shared b/usr/src/uts/sun4v/Makefile.sun4v.shared
index 60aa0eedc1..1df7f3ca83 100644
--- a/usr/src/uts/sun4v/Makefile.sun4v.shared
+++ b/usr/src/uts/sun4v/Makefile.sun4v.shared
@@ -327,6 +327,7 @@ MACH_NOT_YET_KMODS = $(AUTOCONF_OBJS)
# Machine Specific Driver Modules (/kernel/drv):
#
DRV_KMODS += bge
+DRV_KMODS += bmc
DRV_KMODS += cnex
DRV_KMODS += cpc
DRV_KMODS += dma
diff --git a/usr/src/uts/sun4v/bmc/Makefile b/usr/src/uts/sun4v/bmc/Makefile
new file mode 100644
index 0000000000..89f7f25332
--- /dev/null
+++ b/usr/src/uts/sun4v/bmc/Makefile
@@ -0,0 +1,96 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# uts/sun4v/bmc/Makefile
+#
+# This makefile drives the production of the bmc driver module.
+#
+# sun4v implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = bmc
+OBJECTS = $(BMC_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(BMC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/sun4v/io/bmc
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4v/Makefile.sun4v
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(CONFMOD)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+CFLAGS += $(CCVERBOSE)
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW
+LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/$(PLATFORM)/Makefile.targ
diff --git a/usr/src/uts/sun4v/io/bmc/bmc.conf b/usr/src/uts/sun4v/io/bmc/bmc.conf
new file mode 100644
index 0000000000..1d6c7bf8b7
--- /dev/null
+++ b/usr/src/uts/sun4v/io/bmc/bmc.conf
@@ -0,0 +1,28 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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"
+
+name="bmc" parent="pseudo" instance=0;
diff --git a/usr/src/uts/sun4v/io/bmc/bmc_fe.c b/usr/src/uts/sun4v/io/bmc/bmc_fe.c
new file mode 100644
index 0000000000..bf21c35064
--- /dev/null
+++ b/usr/src/uts/sun4v/io/bmc/bmc_fe.c
@@ -0,0 +1,911 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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/types.h>
+#include <sys/stropts.h>
+#include <sys/note.h>
+#include <sys/stat.h>
+#include <sys/kstat.h>
+#include <sys/devops.h>
+#include <sys/dditypes.h>
+#include <sys/stream.h>
+#include <sys/modctl.h>
+#include <sys/varargs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/policy.h>
+#include <sys/sysmacros.h>
+#include <sys/atomic.h>
+#include <sys/bmc_intf.h>
+#include <sys/bmc_cmd.h>
+
+#include "bmc_fe.h"
+
+static boolean_t bmc_command_requires_privilege(uint8_t, uint8_t);
+static void bmc_send_syserror(queue_t *q, uint8_t err);
+static mblk_t *bmc_process_msg(queue_t *q, mblk_t *mp, boolean_t *intr);
+static mblk_t *bmc_build_msg(uint8_t type, uint32_t mid, ...);
+
+int bmc_debug = 0;
+
+#define BMC_NUM_CMDS 256
+
+typedef struct bmc_clone {
+ ipmi_state_t *ipmip; /* IPMI state */
+ dev_t dev; /* maj/min for this 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
+bmc_mioctl(queue_t *q, mblk_t *mp)
+{
+ struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
+ mblk_t *mptr;
+ ipmi_state_t *ipmip = BMC_CLONE(q->q_ptr)->ipmip;
+ ipmi_dev_t *devp = &ipmip->ipmi_dev_ext;
+ bmc_reqrsp_t *intfp;
+ uint8_t *methodp;
+ unsigned char ack_type = M_IOCNAK;
+
+ dprintf(BMC_DEBUG_LEVEL_4, "IOCTL enter");
+
+ /* mptr points to the the data the user passed down */
+ mptr = mp->b_cont;
+
+ /* Consolidate multiple mblk_t's used to build this message */
+ if (mptr) {
+ dprintf(BMC_DEBUG_LEVEL_4, "mptr: %p", (void *)mptr);
+ if (pullupmsg(mptr, -1) == 0) {
+ dprintf(BMC_DEBUG_LEVEL_4, "pullupmsg failure");
+ iocp->ioc_error = EINVAL;
+ goto mioctl_exit;
+ }
+
+ intfp = (bmc_reqrsp_t *)mptr->b_rptr;
+ }
+
+ /* Make sure that the user passed in something */
+ if (intfp == NULL) {
+ dprintf(BMC_DEBUG_LEVEL_4, "No data passed with M_IOCTL");
+ iocp->ioc_error = EINVAL;
+ goto mioctl_exit;
+ }
+
+ /* Don't allow transparent ioctls */
+ if (iocp->ioc_count == TRANSPARENT) {
+ dprintf(BMC_DEBUG_LEVEL_4, "TRANSPARENT ioctls not allowed");
+ iocp->ioc_error = EINVAL;
+ goto mioctl_exit;
+ }
+
+ if (devp == NULL) {
+ dprintf(BMC_DEBUG_LEVEL_4, "deviceExt is NULL");
+ iocp->ioc_error = EINVAL;
+ goto mioctl_exit;
+ }
+
+
+ dprintf(BMC_DEBUG_LEVEL_4, "IOCTL cmd 0x%x count 0x%lx",
+ iocp->ioc_cmd, (ulong_t)iocp->ioc_count);
+
+ switch (iocp->ioc_cmd) {
+
+ case IOCTL_IPMI_KCS_ACTION: /* DEPRECATED */
+ /* legacy from x86 /dev/bmc */
+ case IOCTL_IPMI_INTERFACE_METHOD:
+ /*
+ * If the user has provided at least enough space to hold
+ * the interface type, then return it. Otherwise, bail
+ * out with an error.
+ */
+ if (iocp->ioc_count >= sizeof (uint8_t)) {
+
+ /* All future accesses should be via putmsg/getmsg */
+ methodp = (uint8_t *)mptr->b_rptr;
+ *methodp = BMC_PUTMSG_METHOD;
+ ack_type = M_IOCACK;
+ iocp->ioc_rval = 0;
+ iocp->ioc_count = 1;
+ } else {
+ dprintf(BMC_DEBUG_LEVEL_3,
+ "IOCTL_IPMI_INTERFACE_METHOD: Not enough data"
+ " supplied to ioctl");
+ iocp->ioc_error = ENOSPC;
+ }
+
+ break;
+
+ default:
+ iocp->ioc_error = EINVAL;
+ break;
+ }
+
+mioctl_exit:
+ mp->b_datap->db_type = ack_type;
+ qreply(q, mp);
+}
+
+static int
+bmc_wput(queue_t *q, mblk_t *mp)
+{
+ dprintf(BMC_DEBUG_LEVEL_4, "bmc_wput enter");
+ /* We're expecting a message with data here */
+ ASSERT(mp != NULL);
+ ASSERT(mp->b_datap != NULL);
+
+ switch (mp->b_datap->db_type) {
+
+ case M_DATA:
+ /* Queue for later processing */
+ if (!putq(q, mp)) {
+ dprintf(BMC_DEBUG_LEVEL_2, "putq(M_DATA) failed!");
+ freemsg(mp);
+ }
+ 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.
+ *
+ */
+ if (*mp->b_rptr & FLUSHW) {
+ dprintf(BMC_DEBUG_LEVEL_2, "Flush write queue");
+ flushq(q, FLUSHALL);
+ *mp->b_rptr &= ~FLUSHW;
+ }
+ if (*mp->b_rptr & FLUSHR) {
+ dprintf(BMC_DEBUG_LEVEL_2, "Flush read queue");
+ qreply(q, mp);
+ } else
+ /* No read processing required. Throw away message */
+ freemsg(mp);
+ break;
+
+ default:
+ dprintf(BMC_DEBUG_LEVEL_2,
+ "Message not understood. Ignoring. db_type = %d",
+ mp->b_datap->db_type);
+ freemsg(mp);
+ break;
+ }
+
+ return (0);
+
+}
+
+/*
+ * Write-size queue processing
+ *
+ * Process data messages and perform BMC operations as directed.
+ */
+static int
+bmc_wsrv(queue_t *q)
+{
+ mblk_t *mp;
+ queue_t *rq = RD(q);
+ boolean_t intr;
+
+ while (mp = getq(q)) {
+ /* We only queued M_DATA messages */
+ ASSERT(mp->b_datap->db_type == M_DATA);
+
+ /*
+ * 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 */
+ if (!putbq(q, mp))
+ freemsg(mp);
+ break;
+ }
+
+ /*
+ * Process this message. Any replies will not reuse
+ * mp, so discard it after the message is processed.
+ */
+ mp = bmc_process_msg(q, mp, &intr);
+ freemsg(mp);
+ if (intr) {
+ bmc_send_syserror(RD(q), EINTR);
+ break;
+ }
+ }
+ return (0);
+}
+
+
+/*ARGSUSED*/
+static int
+bmc_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
+{
+ int instance = getminor(*devp);
+ ipmi_state_t *ipmip;
+ int c;
+
+ if (sflag) {
+ /* Clone open NOT supported here */
+ return (ENXIO);
+ }
+
+ if ((ipmip = ddi_get_soft_state(ipmi_state, instance)) == NULL) {
+ 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++) {
+ if (casptr(&bmc_clones[c].ipmip, NULL, ipmip) == NULL) {
+ break;
+ }
+ }
+
+ if (c >= bmc_nclones)
+ return (EAGAIN);
+
+ *devp = bmc_clones[c].dev = makedevice(getemajor(*devp), c + 1);
+
+ /* Init q data pointers */
+ q->q_ptr = WR(q)->q_ptr = &bmc_clones[c];
+
+ qprocson(q); /* Turn on the q */
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+bmc_close(queue_t *q, int flag, cred_t *credp)
+{
+ bmc_clones[getminor(BMC_CLONE(q->q_ptr)->dev) - 1].ipmip = NULL;
+
+ qprocsoff(q); /* Turn the q off */
+ return (0);
+}
+
+static int
+bmc_getbmcversions(ipmi_state_t *ipmip, boolean_t can_intr, boolean_t *intr)
+{
+ bmc_req_t req;
+ bmc_rsp_t rsp;
+
+ bzero(&req, sizeof (bmc_req_t));
+ bzero(&rsp, sizeof (bmc_rsp_t));
+
+ req.fn = BMC_NETFN_APP;
+ req.cmd = BMC_GET_DEVICE_ID;
+
+ rsp.fn = BMC_NETFN_APP;
+ rsp.cmd = BMC_GET_DEVICE_ID;
+ rsp.datalength = RECV_MAX_PAYLOAD_SIZE;
+
+
+ if (do_vc2bmc(&ipmip->ipmi_dev_ext, &req, &rsp, can_intr, intr)
+ == BMC_SUCCESS) {
+
+ /* check for the version */
+ if (rsp.ccode != 0) {
+ goto getbmcversions_error;
+ }
+
+ if (rsp.data[4] == BMC_IPMI_15_VER) {
+ dprintf(BMC_DEBUG_LEVEL_3, "F/W Version: %x.%x",
+ (rsp.data[2] & 0x7F), rsp.data[3]);
+ }
+
+ return (BMC_SUCCESS);
+ }
+
+getbmcversions_error:
+
+ if (*intr)
+ dprintf(BMC_DEBUG_LEVEL_2, "getbmcversion interrupted");
+ else
+ dprintf(BMC_DEBUG_LEVEL_2, "getbmcversion failed");
+
+ return (BMC_FAILURE);
+}
+
+static int
+bmc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int instance = ddi_get_instance(dip);
+ ipmi_state_t *ipmip;
+ boolean_t intr = B_FALSE;
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ if (ddi_soft_state_zalloc(ipmi_state, instance) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ if ((ipmip = ddi_get_soft_state(ipmi_state, instance)) == NULL) {
+ ddi_soft_state_free(ipmi_state, instance);
+ return (DDI_FAILURE);
+ }
+
+ ipmip->ipmi_dip = dip;
+
+ mutex_init(&ipmip->ipmi_dev_ext.if_mutex, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&ipmip->ipmi_dev_ext.if_cv, NULL, CV_DEFAULT, NULL);
+ ipmip->ipmi_dev_ext.if_busy = B_FALSE;
+
+ /* LDI initialization */
+ if (vc_init(dip) != BMC_SUCCESS) {
+ goto cleanup_on_fail;
+ }
+
+ /* Try twice to get the BMC version */
+ if (bmc_getbmcversions(ipmip, B_FALSE, &intr) == BMC_FAILURE) {
+ if (bmc_getbmcversions(ipmip, B_FALSE, &intr) == BMC_FAILURE) {
+ goto cleanup_on_fail;
+ }
+ }
+
+ if ((ddi_create_minor_node(dip, BMC_NODENAME, S_IFCHR,
+ BMC_MINOR, DDI_PSEUDO, 0)) != DDI_SUCCESS) {
+ ddi_remove_minor_node(dip, NULL);
+ goto cleanup_on_fail;
+ }
+
+ ddi_report_dev(dip);
+ return (DDI_SUCCESS);
+
+cleanup_on_fail:
+ (void) vc_uninit();
+ cv_destroy(&ipmip->ipmi_dev_ext.if_cv);
+ mutex_destroy(&ipmip->ipmi_dev_ext.if_mutex);
+ ddi_soft_state_free(ipmi_state, instance);
+
+ return (DDI_FAILURE);
+}
+
+static int
+bmc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int instance;
+ ipmi_state_t *ipmip;
+
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ (void) vc_uninit();
+
+ instance = ddi_get_instance(dip);
+
+ ddi_remove_minor_node(dip, NULL);
+
+ if ((ipmip = ddi_get_soft_state(ipmi_state, instance)) != NULL) {
+ cv_destroy(&ipmip->ipmi_dev_ext.if_cv);
+ mutex_destroy(&ipmip->ipmi_dev_ext.if_mutex);
+ }
+ ddi_soft_state_free(ipmi_state, ddi_get_instance(dip));
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+bmc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error, instance = getminor((dev_t)arg);
+ ipmi_state_t *ipmip = ddi_get_soft_state(ipmi_state, instance);
+
+ if (ipmip == NULL)
+ return (DDI_FAILURE);
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if (ipmip->ipmi_dip == NULL) {
+ error = DDI_FAILURE;
+ } else {
+ *result = (void *)ipmip->ipmi_dip;
+ error = DDI_SUCCESS;
+ }
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(intptr_t)instance;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+
+ return (error);
+}
+
+/*PRINTFLIKE2*/
+void
+dprintf(int d, const char *format, ...)
+{
+#ifdef DEBUG
+ if (d <= bmc_debug) {
+ va_list ap;
+ va_start(ap, format);
+ vcmn_err(d < BMC_DEBUG_LEVEL_2 ? CE_WARN : CE_CONT, format, ap);
+ va_end(ap);
+ }
+#endif
+}
+
+static boolean_t
+bmc_command_requires_privilege(uint8_t command, uint8_t netFn)
+{
+
+ bmc_command_priv_level_t *command_listp;
+ 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:
+ command_listp = bmc_netfn_chassis;
+ break;
+
+ case BMC_NETFN_BRIDGE:
+ command_listp = bmc_netfn_bridge;
+ break;
+
+ case BMC_NETFN_SE:
+ command_listp = bmc_netfn_se;
+ break;
+
+ case BMC_NETFN_APP:
+ command_listp = bmc_netfn_app;
+ break;
+
+ case BMC_NETFN_STORAGE:
+ command_listp = bmc_netfn_storage;
+ break;
+
+ case BMC_NETFN_TRANSPORT:
+ command_listp = bmc_netfn_transport;
+ break;
+
+ default:
+ return (B_TRUE); /* Unknown function group */
+ }
+
+ for (i = 0; command_listp[i].req_level != BMC_END_OF_LIST; i++) {
+ if (command_listp[i].command == command)
+ return (command_listp[i].req_level == BMC_REQ_PRIV);
+ }
+
+ 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
+bmc_send_syserror(queue_t *q, uint8_t err)
+{
+ mblk_t *bp;
+
+ if ((bp = allocb(1, BPRI_HI)) != NULL) {
+
+ bp->b_datap->db_type = M_ERROR;
+ *bp->b_wptr++ = err;
+
+ putnext(q, bp);
+ }
+}
+
+/*
+ * Process a message sent from the user.
+ *
+ * q passed in is the WRITE side.
+ */
+static mblk_t *
+bmc_process_msg(queue_t *q, mblk_t *mp, boolean_t *intr)
+{
+ ipmi_state_t *ipmip = BMC_CLONE(q->q_ptr)->ipmip;
+ bmc_msg_t *msg;
+ bmc_req_t *request;
+ bmc_rsp_t *response;
+ int response_allocsz;
+ mblk_t *reply_msg = NULL;
+ int msgsize;
+ mblk_t *origmp = mp;
+
+ ASSERT(mp->b_datap->db_type == M_DATA);
+
+ dprintf(BMC_DEBUG_LEVEL_4, "bmc_process_msg enter");
+
+ /* Construct contiguous message so we can access its fields below */
+ dprintf(BMC_DEBUG_LEVEL_4, "mp = %p", (void *)mp);
+ if ((mp = msgpullup(origmp, -1)) == NULL) {
+ dprintf(BMC_DEBUG_LEVEL_4, "msgpullup failure");
+ bmc_send_syserror(RD(q), ENOSR);
+ return (origmp);
+ }
+
+ /* Done with the original message; the pulled-up message is in mp */
+ freemsg(origmp);
+
+ msgsize = msgdsize(mp);
+ msg = (bmc_msg_t *)mp->b_rptr;
+
+ /* The message must be at LEAST as large as a bmc_msg_t */
+ if (msgsize < sizeof (bmc_msg_t)) {
+ dprintf(BMC_DEBUG_LEVEL_4, "Message is smaller than min msg"
+ " size (size was %d, must be at least %lu)", msgsize,
+ (ulong_t)sizeof (bmc_msg_t));
+
+ reply_msg = bmc_build_msg(BMC_MSG_ERROR, BMC_UNKNOWN_MSG_ID,
+ EINVAL);
+ ASSERT(reply_msg != NULL);
+
+ /* Invalid message -- send an error upstream and return */
+ qreply(q, reply_msg);
+ return (mp);
+ }
+
+ *intr = B_FALSE;
+
+ switch (msg->m_type) {
+
+ 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).
+ */
+ msgsize -= offsetof(bmc_msg_t, msg);
+
+ request = (bmc_req_t *)&msg->msg[0];
+
+ /* Perform some sanity checks on the size of the message */
+ if (msgsize < sizeof (bmc_req_t) || msgsize <
+ (offsetof(bmc_req_t, data) + request->datalength)) {
+ dprintf(BMC_DEBUG_LEVEL_4, "Malformed message, msg "
+ " size=%lu, payload size=%d, expected size=%lu",
+ (ulong_t)msgdsize(mp), msgsize,
+ (ulong_t)((msgsize < sizeof (bmc_req_t)) ?
+ sizeof (bmc_req_t) :
+ (offsetof(bmc_req_t, data) +
+ request->datalength)));
+ /* Send a message to signal an error */
+ reply_msg = bmc_build_msg(BMC_MSG_ERROR, msg->m_id,
+ EINVAL);
+ break;
+ }
+
+ /* Does the command number look OK? */
+ if (request->cmd >= (BMC_NUM_CMDS - 1)) {
+ reply_msg = bmc_build_msg(BMC_MSG_RESPONSE,
+ msg->m_id, request->fn, request->lun,
+ request->cmd, BMC_IPMI_INVALID_COMMAND,
+ 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).
+ */
+ if ((DB_CRED(mp) == NULL) ||
+ (bmc_command_requires_privilege(request->cmd,
+ request->fn) && secpolicy_sys_config(DB_CRED(mp),
+ B_FALSE) != 0)) {
+ reply_msg = bmc_build_msg(BMC_MSG_ERROR, msg->m_id,
+ EACCES);
+ break;
+ }
+
+ dprintf(BMC_DEBUG_LEVEL_2,
+ "MSG type 0x%x subcmd 0x%x req_len 0x%x",
+ msg->m_type, request->cmd, request->datalength);
+
+ /* Allocate enough space for the largest response possible */
+ response_allocsz = bmc_vc_max_response_payload_size() +
+ offsetof(bmc_rsp_t, data);
+ response = (bmc_rsp_t *)kmem_zalloc(response_allocsz, KM_SLEEP);
+ response->datalength = bmc_vc_max_response_payload_size();
+
+ /*
+ * 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.
+ */
+ (void) do_vc2bmc(&ipmip->ipmi_dev_ext, request, response,
+ B_TRUE, intr);
+
+ if (!*intr) {
+ reply_msg = bmc_build_msg(BMC_MSG_RESPONSE, msg->m_id,
+ response->fn, response->lun, response->cmd,
+ response->ccode, response->datalength,
+ (uint8_t *)response->data);
+
+ dprintf(BMC_DEBUG_LEVEL_2,
+ "MSG DONE subcmd 0x%x req_len 0x%x rsp_len 0x%x "
+ "code 0x%x",
+ request->cmd,
+ request->datalength,
+ response->datalength,
+ response->ccode);
+ }
+
+ kmem_free(response, response_allocsz);
+
+ break;
+
+ default:
+ reply_msg = bmc_build_msg(BMC_MSG_ERROR, msg->m_id, EINVAL);
+ break;
+ }
+
+ ASSERT(*intr || reply_msg != NULL);
+
+ if (!*intr) {
+ /* Send the reply upstream */
+ qreply(q, reply_msg);
+ }
+ return (mp);
+}
+
+static mblk_t *
+bmc_build_msg(uint8_t mtype, uint32_t mid, ...)
+{
+ mblk_t *mp = NULL;
+ bmc_msg_t *msg = NULL;
+ va_list ap;
+ bmc_rsp_t *rsp;
+ bmc_rsp_t *response;
+ uint8_t *datap;
+ size_t msgsz = 0;
+
+ va_start(ap, mid);
+
+ switch (mtype) {
+ case BMC_MSG_ERROR:
+ /*
+ * Build an error message. The second parameter is the
+ * message ID, and the third is the error code.
+ */
+ msgsz = sizeof (bmc_msg_t);
+ mp = allocb(msgsz, BPRI_MED);
+ if (mp == NULL)
+ break;
+ msg = (bmc_msg_t *)mp->b_wptr;
+ /* First byte of msg is the error code */
+ msg->msg[0] = va_arg(ap, uint_t);
+ break;
+
+ case BMC_MSG_RESPONSE:
+ rsp = kmem_alloc(sizeof (bmc_rsp_t), KM_SLEEP);
+ rsp->fn = va_arg(ap, uint_t);
+ rsp->lun = va_arg(ap, uint_t);
+ rsp->cmd = va_arg(ap, uint_t);
+ rsp->ccode = va_arg(ap, uint_t);
+ rsp->datalength = va_arg(ap, uint_t);
+ datap = va_arg(ap, uint8_t *);
+
+
+ /*
+ * 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).
+ */
+ msgsz = offsetof(bmc_msg_t, msg) + offsetof(bmc_rsp_t, data) +
+ rsp->datalength;
+
+ mp = allocb(msgsz, BPRI_MED);
+ if (mp == NULL)
+ break;
+
+ msg = (bmc_msg_t *)mp->b_wptr;
+ response = (bmc_rsp_t *)&msg->msg[0];
+ response->fn = rsp->fn;
+ response->lun = rsp->lun;
+ response->cmd = rsp->cmd;
+ response->ccode = rsp->ccode;
+ response->datalength = rsp->datalength;
+ if (response->datalength != 0 && datap != 0)
+ bcopy(datap, response->data, response->datalength);
+
+ kmem_free(rsp, sizeof (bmc_rsp_t));
+ break;
+
+ default:
+ dprintf(BMC_DEBUG_LEVEL_2,
+ "bmc_build_msg: unknown message type 0x%x!",
+ mtype);
+ break;
+ }
+
+ if (msg != NULL) {
+ msg->m_type = mtype;
+ msg->m_id = mid;
+ }
+
+ if (mp != NULL)
+ mp->b_wptr += msgsz;
+
+ ASSERT(mp == NULL || mp->b_wptr <= mp->b_datap->db_lim);
+
+ va_end(ap);
+
+ 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 */
+};
+
+static struct qinit bmc_rinit = {
+ 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 */
+};
+
+static struct qinit bmc_winit = {
+ 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 = {
+ &bmc_rinit,
+ &bmc_winit,
+ NULL,
+ NULL
+};
+
+DDI_DEFINE_STREAM_OPS( \
+ bmc_ops, \
+ nulldev, \
+ nulldev, \
+ bmc_attach, \
+ bmc_detach, \
+ nodev, \
+ bmc_getinfo, \
+ D_MP | D_NEW, \
+ &bmc_str_info \
+);
+
+static struct modldrv modldrv = {
+ &mod_driverops, "BMC driver %I%", &bmc_ops
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modldrv, NULL
+};
+
+int
+_init(void)
+{
+ int error;
+
+ if ((error = ddi_soft_state_init(&ipmi_state,
+ sizeof (ipmi_state_t), 0)) != 0)
+ return (error);
+
+ if (bmc_nclones <= 0)
+ bmc_nclones = maxusers;
+
+ bmc_clones = kmem_zalloc(sizeof (bmc_clone_t) * bmc_nclones, KM_SLEEP);
+
+ if ((error = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini(&ipmi_state);
+ kmem_free(bmc_clones, sizeof (bmc_clone_t) * bmc_nclones);
+ }
+
+ return (error);
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ if ((error = mod_remove(&modlinkage)) == 0) {
+ ddi_soft_state_fini(&ipmi_state);
+ kmem_free(bmc_clones, sizeof (bmc_clone_t) * bmc_nclones);
+ }
+
+ return (error);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
diff --git a/usr/src/uts/sun4v/io/bmc/bmc_fe.h b/usr/src/uts/sun4v/io/bmc/bmc_fe.h
new file mode 100644
index 0000000000..2b20d13493
--- /dev/null
+++ b/usr/src/uts/sun4v/io/bmc/bmc_fe.h
@@ -0,0 +1,73 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+#ifndef _BMC_FE_H
+#define _BMC_FE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BMC_NODENAME "bmc"
+#define BMC_MINOR 0
+
+#define BMC_DEBUG_LEVEL_0 0
+#define BMC_DEBUG_LEVEL_1 1
+#define BMC_DEBUG_LEVEL_2 2
+#define BMC_DEBUG_LEVEL_3 3
+#define BMC_DEBUG_LEVEL_4 4
+
+void dprintf(int, const char *, ...);
+
+typedef struct ipmi_dev {
+ uint8_t bmcversion; /* IPMI Version */
+ kmutex_t if_mutex; /* Interface lock */
+ kcondvar_t if_cv; /* Interface condition variable */
+ boolean_t if_busy; /* Busy Bit */
+ timeout_id_t timer_handle; /* timer handle */
+ boolean_t timedout; /* timeout flag */
+} ipmi_dev_t;
+
+typedef struct ipmi_stat {
+ dev_info_t *ipmi_dip; /* driver's device pointer */
+ ipmi_dev_t ipmi_dev_ext; /* controlling the BMC interface */
+} ipmi_state_t;
+
+/* transfer time limit */
+#define DEFAULT_MSG_TIMEOUT (5 * 1000000) /* 5 seconds */
+
+int vc_init(dev_info_t *);
+void vc_uninit(void);
+int do_vc2bmc(ipmi_dev_t *, bmc_req_t *, bmc_rsp_t *, boolean_t, boolean_t *);
+int bmc_vc_max_response_payload_size(void);
+int bmc_vc_max_request_payload_size(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BMC_FE_H */
diff --git a/usr/src/uts/sun4v/io/bmc/bmc_vc.c b/usr/src/uts/sun4v/io/bmc/bmc_vc.c
new file mode 100644
index 0000000000..f0529d2dc8
--- /dev/null
+++ b/usr/src/uts/sun4v/io/bmc/bmc_vc.c
@@ -0,0 +1,718 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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: back-end to BMC access
+ */
+
+#include <sys/types.h>
+#include <sys/stropts.h>
+#include <sys/note.h>
+#include <sys/stat.h>
+#include <sys/kstat.h>
+#include <sys/devops.h>
+#include <sys/dditypes.h>
+#include <sys/stream.h>
+#include <sys/modctl.h>
+#include <sys/varargs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/conf.h>
+#include <sys/sysmacros.h>
+#include <sys/bmc_intf.h>
+#include <sys/uio.h>
+#include <sys/vldc.h>
+#include <sys/ldc.h>
+#include <sys/sunldi.h>
+#include <sys/file.h>
+
+#include "bmc_fe.h"
+
+
+/*
+ * The VLDC (Virtual Logical Domain Channel) interface is used to
+ * send command request messages to, and receive command response
+ * messages from, the Service Processor (SP).
+ *
+ * Messages are transferred over the interface using Layered Driver
+ * Interface over the vldc driver.
+ *
+ * ------------------- .
+ * | ipmitool/others | .
+ * ------------------- .
+ * | .
+ * | .
+ * ============ . ------------------
+ * # /dev/bmc # . | IPMI ILOM task |
+ * ============ . ------------------
+ * | . |
+ * | . |
+ * -------- virtual channel . --------
+ * | vldc |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| vBSC |
+ * -------- . --------
+ * .
+ * H O S T . S P
+ *
+ */
+
+/*
+ * IPMI virtual channel name
+ */
+#define IPMI_VLDC \
+ "/devices/virtual-devices@100/channel-devices@200" \
+ "/virtual-channel@3:ipmi"
+#define VLDC_MTU 0x400
+
+/*
+ * Virtual channel LDI parameters
+ */
+static ldi_handle_t vc_lh;
+static ldi_ident_t vc_li;
+
+#define VC_MAGIC_NUM 0x4B59554E
+
+#define TIMEDOUT(dev) ((dev)->timedout)
+#define BMC_DISCARD_TIMEOUT (1 * 1000 * 1000) /* 1 second */
+
+/*
+ * Default Data limits for the virtual channel interface
+ */
+#define BMC_VC_MAX_REQUEST_SIZE 263 /* 263 to allow the payload */
+ /* to be the full 255 bytes */
+#define VC_SEND_NONDATA_SIZE 8
+#define VC_SEND_MAX_PAYLOAD_SIZE (BMC_VC_MAX_REQUEST_SIZE \
+ - VC_SEND_NONDATA_SIZE)
+#define BMC_VC_MAX_RESPONSE_SIZE 266 /* Max-out receive payload */
+#define VC_RECV_NONDATA_SIZE 11
+#define VC_RECV_MAX_PAYLOAD_SIZE (BMC_VC_MAX_RESPONSE_SIZE \
+ - VC_RECV_NONDATA_SIZE)
+
+/*
+ * Tunables
+ */
+
+/*
+ * Some interfaces can handle a massive amount of request data, e.g.
+ * when they're used to transfer flash images to the system service processor.
+ * To enable faster bulk-data transfer on such BMCs, allow the max send payload
+ * to be dynamically tunable.
+ */
+int vc_max_send_payload = VC_SEND_MAX_PAYLOAD_SIZE;
+
+/*
+ * Private Data Structures
+ */
+
+/*
+ * data structure to send a message to BMC.
+ */
+typedef struct bmc_vc_send {
+ uint32_t magic_num; /* magic number */
+ uint16_t datalen; /* data length */
+ uint8_t fnlun; /* Network Function and LUN */
+ uint8_t cmd; /* command */
+ uint8_t data[1]; /* Variable-length, see vc_max_send_payload */
+} bmc_vc_send_t;
+
+/*
+ * data structure to receive a message from BMC.
+ */
+typedef struct bmc_vc_recv {
+ uint32_t magic_num; /* magic number */
+ uint16_t datalen; /* data length */
+ uint16_t reserved; /* reserved */
+ uint8_t fnlun; /* Network Function and LUN */
+ uint8_t cmd; /* command */
+ uint8_t ccode; /* completion code */
+ uint8_t data[VC_RECV_MAX_PAYLOAD_SIZE];
+} bmc_vc_recv_t;
+
+
+int
+vc_init(dev_info_t *dip)
+{
+ int ret = BMC_SUCCESS;
+
+ if (ldi_ident_from_dip(dip, &vc_li) != 0)
+ ret = BMC_FAILURE;
+
+ return (ret);
+}
+
+void
+vc_uninit()
+{
+ ldi_ident_release(vc_li);
+ vc_li = NULL;
+}
+
+void
+dump_raw_pkt(uint8_t *buf, uint_t sz)
+{
+ uint_t i;
+
+ for (i = 0; i < sz; i++) {
+ dprintf(BMC_DEBUG_LEVEL_3,
+ "%d => 0x%x", i, buf[i]);
+ }
+}
+
+static void
+vc_timer_handler(void *devp)
+{
+ ipmi_dev_t *dev = (ipmi_dev_t *)devp;
+
+ dev->timedout = B_TRUE;
+}
+
+/*
+ * If the service processor takes a reset, the connection to the vldc channel
+ * could be lost and vldc_chpoll() might always return ENOTACTIVE or ECONNRESET.
+ * This may be fixed in vldc eventually, but it needs, for now, to provide
+ * workaround by closing and reopening the channel. Refer to CR 6629230.
+ *
+ * In vc_read() and vc_write(), if ldi_read() or ldi_write() returns ENOTACTIVE
+ * or ECONNRESET, it will be eventually caught by the call to ldi_poll(); and
+ * this function will be called at that time.
+ *
+ * Called with if_busy = B_TRUE.
+ */
+static void
+vc_reopen_vldc(ipmi_dev_t *devp)
+{
+ vldc_opt_op_t channel_op;
+
+ ASSERT(devp->if_busy == B_TRUE);
+
+ (void) ldi_close(vc_lh, NULL, kcred);
+
+ delay(drv_usectohz(50000));
+
+ if (ldi_open_by_name(IPMI_VLDC, (FREAD | FWRITE | FEXCL),
+ kcred, &vc_lh, vc_li) != 0) {
+ dprintf(BMC_DEBUG_LEVEL_4,
+ "reconnect failed at ldi_open_by_name");
+ return;
+ }
+
+ channel_op.op_sel = VLDC_OP_SET;
+ channel_op.opt_sel = VLDC_OPT_MODE;
+ channel_op.opt_val = LDC_MODE_STREAM;
+
+ if (ldi_ioctl(vc_lh, VLDC_IOCTL_OPT_OP,
+ (intptr_t)&channel_op, FKIOCTL, kcred, NULL) != 0) {
+ dprintf(BMC_DEBUG_LEVEL_4, "reconnect failed at ldi_ioctl");
+ (void) ldi_close(vc_lh, NULL, kcred);
+ return;
+ }
+
+ dprintf(BMC_DEBUG_LEVEL_4,
+ "reestablished connection with %s", IPMI_VLDC);
+}
+
+static int
+vc_write(ipmi_dev_t *devp, uint8_t *send_buf, uint32_t pktsz,
+ boolean_t can_intr, boolean_t *intr)
+{
+ uint8_t *p;
+ struct uio uio;
+ struct iovec iov;
+ int chunksize;
+ int error;
+ int ret = BMC_FAILURE;
+ int anyyet = 0;
+ short reventsp;
+ struct pollhead *php;
+ int notready_count = 0;
+
+ dprintf(BMC_DEBUG_LEVEL_3, "VLDC write Len 0x%x [[START]]", pktsz);
+
+ p = (uint8_t *)send_buf;
+ while (p < (uint8_t *)&send_buf[pktsz] && !TIMEDOUT(devp)) {
+
+ /*
+ * send operations can take some time -- check to see if the
+ * user has gotten impatient.
+ */
+ if (can_intr && issig(JUSTLOOKING) && issig(FORREAL)) {
+ *intr = B_TRUE;
+ break;
+ }
+
+ if ((error = ldi_poll(vc_lh, POLLOUT,
+ anyyet, &reventsp, &php)) != 0) {
+ dprintf(BMC_DEBUG_LEVEL_3, "ldi_poll ret %d", error);
+ if (error == ENOTACTIVE || error == ECONNRESET) {
+ vc_reopen_vldc(devp);
+ }
+ continue;
+ }
+
+ if (!(reventsp & POLLOUT)) {
+
+ /*
+ * We don't always expect the virtual channel to be
+ * reliable and responsive. For example, what if the
+ * channel unexpectedly resets and we end up polling
+ * forever for some reason? This kind of problem has
+ * been observed particulary with poll for write. We
+ * should be suspicious of excessive number of polls,
+ * and should take a recovery measure.
+ */
+ if (++notready_count % 50 == 0) {
+ dprintf(BMC_DEBUG_LEVEL_3,
+ "excessive poll retries.");
+ vc_reopen_vldc(devp);
+ }
+
+ delay(drv_usectohz(100));
+ continue;
+ }
+
+ chunksize = (uint8_t *)&send_buf[pktsz] - p;
+
+ if (chunksize > VLDC_MTU) {
+ chunksize = VLDC_MTU;
+ }
+
+ /*
+ * Write to VLDC in MTU chunks at a time.
+ */
+ bzero(&uio, sizeof (uio));
+ bzero(&iov, sizeof (iov));
+ iov.iov_base = (char *)p;
+ iov.iov_len = chunksize;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_loffset = 0;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_resid = chunksize;
+
+ error = ldi_write(vc_lh, &uio, kcred);
+
+ if (error == EAGAIN) continue;
+
+ if (!error) {
+ p = (uint8_t *)iov.iov_base;
+ } else {
+ dprintf(BMC_DEBUG_LEVEL_3, "ldi_write ret %d", error);
+ break;
+ }
+ }
+
+ if (!error)
+ ret = BMC_SUCCESS;
+
+ dprintf(BMC_DEBUG_LEVEL_3, "VLDC write [[END]]");
+
+ return (ret);
+}
+
+static int
+vc_read(ipmi_dev_t *devp, uint8_t *recv_buf, uint32_t *pktsz,
+ boolean_t can_intr, boolean_t *intr)
+{
+ struct uio uio;
+ struct iovec iov;
+ int error;
+ int ret = BMC_FAILURE;
+ int anyyet = 0;
+ short reventsp;
+ struct pollhead *php;
+
+ dprintf(BMC_DEBUG_LEVEL_3, "VLDC read [[START]]");
+
+ *pktsz = 0;
+
+ while (!TIMEDOUT(devp)) {
+
+ /*
+ * receive operations can take some time -- check to see if the
+ * user has gotten impatient.
+ */
+ if (can_intr && issig(JUSTLOOKING) && issig(FORREAL)) {
+ *intr = B_TRUE;
+ break;
+ }
+
+ if ((error = ldi_poll(vc_lh, (POLLIN | POLLPRI),
+ anyyet, &reventsp, &php)) != 0) {
+ dprintf(BMC_DEBUG_LEVEL_3, "ldi_poll ret %d", error);
+ if (error == ENOTACTIVE || error == ECONNRESET) {
+ vc_reopen_vldc(devp);
+ }
+ continue;
+ }
+
+ if (!(reventsp & (POLLIN | POLLPRI))) {
+ delay(drv_usectohz(100));
+ continue;
+ }
+
+ /*
+ * Read bytes from VLDC
+ */
+ bzero(&uio, sizeof (uio));
+ bzero(&iov, sizeof (iov));
+ iov.iov_base = (char *)recv_buf;
+ iov.iov_len = BMC_VC_MAX_RESPONSE_SIZE;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_loffset = 0;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_resid = BMC_VC_MAX_RESPONSE_SIZE;
+
+ error = ldi_read(vc_lh, &uio, kcred);
+
+ if (error == EAGAIN) continue;
+
+ if (!error) {
+ *pktsz = BMC_VC_MAX_RESPONSE_SIZE - uio.uio_resid;
+
+ /* Check datalen and magic_num. */
+ if (((bmc_vc_recv_t *)recv_buf)->datalen +
+ VC_RECV_NONDATA_SIZE != *pktsz ||
+ ((bmc_vc_recv_t *)recv_buf)->magic_num
+ != VC_MAGIC_NUM) {
+ dprintf(BMC_DEBUG_LEVEL_3,
+ "garbage was received");
+ break;
+ }
+
+ ret = BMC_SUCCESS;
+ break;
+ } else {
+ dprintf(BMC_DEBUG_LEVEL_3, "ldi_read ret %d", error);
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ dump_raw_pkt(recv_buf, *pktsz);
+#endif
+ dprintf(BMC_DEBUG_LEVEL_3, "VLDC read Len 0x%x [[END]]", *pktsz);
+
+ return (ret);
+}
+
+static void
+discard_timeout_handler(void *arg)
+{
+ *(boolean_t *)arg = B_TRUE;
+}
+
+static void
+vc_discard_response()
+{
+ vldc_opt_op_t channel_op;
+ struct uio uio;
+ struct iovec iov;
+ int rc;
+ char junk[1];
+ volatile boolean_t discard_timed_out = B_FALSE;
+ timeout_id_t discard_to = realtime_timeout(discard_timeout_handler,
+ (void *)&discard_timed_out, drv_usectohz(BMC_DISCARD_TIMEOUT));
+
+ if (ldi_open_by_name(IPMI_VLDC, (FREAD | FWRITE | FEXCL),
+ kcred, &vc_lh, vc_li) != 0)
+ goto error_open;
+
+ channel_op.op_sel = VLDC_OP_SET;
+ channel_op.opt_sel = VLDC_OPT_MODE;
+ channel_op.opt_val = LDC_MODE_STREAM;
+
+ if (ldi_ioctl(vc_lh, VLDC_IOCTL_OPT_OP, (intptr_t)&channel_op,
+ FKIOCTL, kcred, NULL) != 0)
+ goto error_ioctl;
+
+ delay(drv_usectohz(50000));
+
+ while (!discard_timed_out) {
+
+ bzero(&uio, sizeof (uio));
+ bzero(&iov, sizeof (iov));
+ iov.iov_base = (char *)junk;
+ iov.iov_len = BMC_VC_MAX_RESPONSE_SIZE;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_loffset = 0;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_resid = BMC_VC_MAX_RESPONSE_SIZE;
+
+ rc = ldi_read(vc_lh, &uio, kcred);
+ dprintf(BMC_DEBUG_LEVEL_4, "junk read result: %d", rc);
+ if (!rc) {
+ dprintf(BMC_DEBUG_LEVEL_4, "recv stream had some junk");
+ break;
+ }
+
+ }
+
+error_ioctl:
+ (void) ldi_close(vc_lh, NULL, kcred);
+
+error_open:
+ (void) untimeout(discard_to);
+
+ dprintf(BMC_DEBUG_LEVEL_3, "VLDC DISCARD");
+}
+
+/*
+ * Allocates a bmc_vc_send_t with enough space in the data member to
+ * fit `datalen' bytes and initializes it with the supplied values.
+ */
+static bmc_vc_send_t *
+vc_construct_send_struct(uint8_t netfn, uint8_t lun, uint8_t cmd, int datalen,
+ uint8_t *datap, int *send_struct_length)
+{
+ bmc_vc_send_t *sendp;
+
+ ASSERT(datalen >= 0);
+ ASSERT(send_struct_length != NULL);
+
+ *send_struct_length = offsetof(bmc_vc_send_t, data) + datalen;
+ sendp = kmem_alloc(*send_struct_length, KM_SLEEP);
+
+ sendp->magic_num = VC_MAGIC_NUM;
+ sendp->datalen = datalen;
+ sendp->fnlun = FORM_NETFNLUN(netfn, lun);
+ sendp->cmd = cmd;
+ if (datalen > 0)
+ bcopy(datap, sendp->data, datalen);
+
+ return (sendp);
+}
+
+/*
+ * Deallocates the resources associated with the send structure `sendp'
+ * of size `sendp_len'.
+ */
+static void
+vc_destruct_send_struct(bmc_vc_send_t *sendp, int sendp_len)
+{
+ kmem_free(sendp, sendp_len);
+}
+
+/*
+ * The only time this is not interruptable is during attach
+ */
+int
+do_vc2bmc(ipmi_dev_t *dev, bmc_req_t *send_pkt, bmc_rsp_t *recv_pkt,
+ boolean_t interruptable, boolean_t *interrupted)
+{
+ bmc_vc_send_t *send_bmc;
+ bmc_vc_recv_t recv_bmc;
+ clock_t ticks;
+ uint_t retrycount = 5;
+ uint32_t spktsz, rpktsz;
+ int error, rval, ret = BMC_FAILURE;
+ int send_bmc_len = 0;
+ vldc_opt_op_t channel_op;
+
+ if (send_pkt->datalength > vc_max_send_payload) {
+ dprintf(BMC_DEBUG_LEVEL_3, "failed send sz: %d",
+ send_pkt->datalength);
+ recv_pkt->ccode = BMC_IPMI_DATA_LENGTH_EXCEED;
+ recv_pkt->datalength = 0;
+ return (ret);
+ }
+
+ mutex_enter(&dev->if_mutex);
+ while (dev->if_busy) {
+ if (cv_wait_sig(&dev->if_cv, &dev->if_mutex) == 0 &&
+ interruptable) {
+ *interrupted = B_TRUE;
+ mutex_exit(&dev->if_mutex);
+ return (BMC_FAILURE);
+ }
+ }
+
+ dev->if_busy = B_TRUE;
+ mutex_exit(&dev->if_mutex);
+
+ ticks = drv_usectohz(DEFAULT_MSG_TIMEOUT);
+
+ dev->timedout = B_FALSE;
+ dev->timer_handle = realtime_timeout(vc_timer_handler, dev, ticks);
+
+ send_bmc = vc_construct_send_struct(send_pkt->fn, send_pkt->lun,
+ send_pkt->cmd, send_pkt->datalength, send_pkt->data, &send_bmc_len);
+ ASSERT(send_bmc != NULL);
+
+ dprintf(BMC_DEBUG_LEVEL_4,
+ "fn 0x%x lun 0x%x cmd 0x%x fnlun 0x%x len 0x%x",
+ send_pkt->fn, send_pkt->lun,
+ send_pkt->cmd, send_bmc->cmd,
+ send_pkt->datalength);
+
+ spktsz = send_pkt->datalength + VC_SEND_NONDATA_SIZE;
+
+ /* Try to open the vldc channel. */
+ if (ldi_open_by_name(IPMI_VLDC, (FREAD | FWRITE | FEXCL),
+ kcred, &vc_lh, vc_li) != 0) {
+ dprintf(BMC_DEBUG_LEVEL_3, "ldi_open failed: %s", IPMI_VLDC);
+ goto error_open;
+ }
+
+ channel_op.op_sel = VLDC_OP_SET;
+ channel_op.opt_sel = VLDC_OPT_MODE;
+ channel_op.opt_val = LDC_MODE_STREAM;
+
+ if ((error = ldi_ioctl(vc_lh, VLDC_IOCTL_OPT_OP,
+ (intptr_t)&channel_op, FKIOCTL, kcred, &rval)) != 0) {
+ dprintf(BMC_DEBUG_LEVEL_3, "ldi_ioctl ret %d", error);
+ goto error_ioctl;
+ }
+
+ while (retrycount-- != 0) {
+
+ if (TIMEDOUT(dev)) {
+ dprintf(BMC_DEBUG_LEVEL_3, "do_vc2bmc timed out");
+ break;
+ }
+
+ if (vc_write(dev, (uint8_t *)send_bmc, spktsz, interruptable,
+ interrupted) == BMC_FAILURE && !*interrupted) {
+ dprintf(BMC_DEBUG_LEVEL_3, "send BMC failed");
+ recv_pkt->ccode = BMC_IPMI_OEM_FAILURE_SENDBMC;
+ continue;
+
+ } else if (*interrupted) {
+ dprintf(BMC_DEBUG_LEVEL_3, "send BMC interrupted");
+ break;
+ }
+
+ if (vc_read(dev, (uint8_t *)&recv_bmc, &rpktsz, interruptable,
+ interrupted) == BMC_FAILURE && !*interrupted) {
+ dprintf(BMC_DEBUG_LEVEL_3, "recv BMC failed");
+ recv_pkt->ccode = BMC_IPMI_COMMAND_TIMEOUT;
+ continue;
+
+ } else if (*interrupted) {
+ dprintf(BMC_DEBUG_LEVEL_3, "recv BMC interrupted");
+ break;
+ }
+#if DEBUG
+ dprintf(BMC_DEBUG_LEVEL_3,
+ "SUMMARY 0x%x 0x%x resp: 0x%x req: 0x%x cmd 0x%x " \
+ "CMD 0x%x len %d",
+ recv_bmc.fnlun, send_bmc->fnlun,
+ GET_NETFN(recv_bmc.fnlun),
+ RESP_NETFN(GET_NETFN(send_bmc->fnlun)),
+ recv_bmc.cmd, send_bmc->cmd, rpktsz);
+
+ /*
+ * only for driver debugging:
+ * check return data and repackage
+ */
+ if ((GET_NETFN(recv_bmc.fnlun) !=
+ RESP_NETFN(GET_NETFN(send_bmc->fnlun))) ||
+ (recv_bmc.cmd != send_bmc->cmd)) {
+ dprintf(BMC_DEBUG_LEVEL_3,
+ "return parameters are not expected");
+ dprintf(BMC_DEBUG_LEVEL_4,
+ "GET_NETFN(recv_bmc.fnlun) 0x%x " \
+ "RESP_NETFN(GET_NETFN(send_bmc->fnlun)) 0x%x " \
+ "recv cmd 0x%x send cmd 0x%x", \
+ GET_NETFN(recv_bmc.fnlun), \
+ RESP_NETFN(GET_NETFN(send_bmc->fnlun)), \
+ recv_bmc.cmd, send_bmc->cmd);
+ }
+#endif
+ /*
+ * Subtract the size of non-data fields from the receive packet
+ * size to get the amount of data in the data field.
+ */
+ rpktsz -= VC_RECV_NONDATA_SIZE;
+
+ recv_pkt->fn = GET_NETFN(recv_bmc.fnlun);
+ recv_pkt->lun = GET_LUN(recv_bmc.fnlun);
+ recv_pkt->cmd = recv_bmc.cmd;
+
+ /*
+ * If the caller didn't provide enough data space to hold
+ * the response, return failure.
+ */
+ if (rpktsz > recv_pkt->datalength) {
+ dprintf(BMC_DEBUG_LEVEL_3, "failed recv sz: %d",
+ recv_pkt->datalength);
+ recv_pkt->ccode = BMC_IPMI_DATA_LENGTH_EXCEED;
+ recv_pkt->datalength = 0;
+ } else {
+ recv_pkt->ccode = recv_bmc.ccode;
+ recv_pkt->datalength = rpktsz;
+ bcopy(&recv_bmc.data, recv_pkt->data, rpktsz);
+ ret = BMC_SUCCESS;
+ }
+ break;
+ }
+
+error_ioctl:
+ (void) ldi_close(vc_lh, NULL, kcred);
+
+error_open:
+ (void) untimeout(dev->timer_handle);
+ dev->timer_handle = 0;
+
+ /*
+ * If we were interrupted, discard the response packet which may
+ * have just arrived in response to the last request packet.
+ * There are some cases where the unread packet in the receive
+ * buffer still lingers after the channel has been closed and
+ * reopened.
+ */
+ if (*interrupted)
+ vc_discard_response();
+
+ mutex_enter(&dev->if_mutex);
+ ASSERT(dev->if_busy == B_TRUE);
+ dev->if_busy = B_FALSE;
+ cv_signal(&dev->if_cv);
+ mutex_exit(&dev->if_mutex);
+
+ vc_destruct_send_struct(send_bmc, send_bmc_len);
+
+ return (ret);
+}
+
+/*
+ * Returns the size of the largest possible response payload.
+ */
+int
+bmc_vc_max_response_payload_size(void)
+{
+ return (VC_RECV_MAX_PAYLOAD_SIZE);
+}
+
+/*
+ * Returns the size of the largest possible request payload.
+ */
+int
+bmc_vc_max_request_payload_size(void)
+{
+ return (vc_max_send_payload);
+}