diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/devfsadm/sparc/misc_link_sparc.c | 16 | ||||
-rw-r--r-- | usr/src/pkgdefs/SUNWcakr.v/prototype_com | 4 | ||||
-rw-r--r-- | usr/src/pkgdefs/common_files/i.minorperm_sparc | 1 | ||||
-rw-r--r-- | usr/src/pkgdefs/etc/exception_list_i386 | 1 | ||||
-rw-r--r-- | usr/src/pkgdefs/etc/exception_list_sparc | 1 | ||||
-rw-r--r-- | usr/src/uts/common/sys/Makefile | 1 | ||||
-rw-r--r-- | usr/src/uts/common/sys/bmc_cmd.h | 251 | ||||
-rw-r--r-- | usr/src/uts/sparc/os/minor_perm | 1 | ||||
-rw-r--r-- | usr/src/uts/sparc/os/name_to_major | 1 | ||||
-rw-r--r-- | usr/src/uts/sun4v/Makefile.files | 5 | ||||
-rw-r--r-- | usr/src/uts/sun4v/Makefile.rules | 9 | ||||
-rw-r--r-- | usr/src/uts/sun4v/Makefile.sun4v.shared | 1 | ||||
-rw-r--r-- | usr/src/uts/sun4v/bmc/Makefile | 96 | ||||
-rw-r--r-- | usr/src/uts/sun4v/io/bmc/bmc.conf | 28 | ||||
-rw-r--r-- | usr/src/uts/sun4v/io/bmc/bmc_fe.c | 911 | ||||
-rw-r--r-- | usr/src/uts/sun4v/io/bmc/bmc_fe.h | 73 | ||||
-rw-r--r-- | usr/src/uts/sun4v/io/bmc/bmc_vc.c | 718 |
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); +} |