diff --git a/include/ipmitool/hpm2.h b/include/ipmitool/hpm2.h new file mode 100644 index 0000000..1c0a612 --- /dev/null +++ b/include/ipmitool/hpm2.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2012 Pigeon Point Systems. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pigeon Point Systems nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include + +/* Global HPM.2 defines */ +#define HPM2_REVISION 0x01 +#define HPM3_REVISION 0x01 +#define HPM2_LAN_PARAMS_REV 0x01 +#define HPM2_SOL_PARAMS_REV 0x01 +#define HPM3_LAN_PARAMS_REV 0x01 + +/* HPM.2 capabilities */ +#define HPM2_CAPS_SOL_EXTENSION 0x01 +#define HPM2_CAPS_PACKET_TRACE 0x02 +#define HPM2_CAPS_EXT_MANAGEMENT 0x04 +#define HPM2_CAPS_VERSION_SENSOR 0x08 +#define HPM2_CAPS_DYNAMIC_SESSIONS 0x10 + +#if HAVE_PRAGMA_PACK +# pragma pack(push, 1) +#endif + +/* HPM.2 LAN attach capabilities */ +struct hpm2_lan_attach_capabilities { + uint8_t hpm2_revision_id; + uint16_t lan_channel_mask; + uint8_t hpm2_caps; + uint8_t hpm2_lan_params_start; + uint8_t hpm2_lan_params_rev; + uint8_t hpm2_sol_params_start; + uint8_t hpm2_sol_params_rev; +} ATTRIBUTE_PACKING; + +/* HPM.2 LAN channel capabilities */ +struct hpm2_lan_channel_capabilities { + uint8_t capabilities; + uint8_t attach_type; + uint8_t bandwidth_class; + uint16_t max_inbound_pld_size; + uint16_t max_outbound_pld_size; +} ATTRIBUTE_PACKING; + +#if HAVE_PRAGMA_PACK +# pragma pack(pop) +#endif + +/* HPM.2 command assignments */ +#define HPM2_GET_LAN_ATTACH_CAPABILITIES 0x3E + +extern int hpm2_get_capabilities(struct ipmi_intf * intf, + struct hpm2_lan_attach_capabilities * caps); +extern int hpm2_get_lan_channel_capabilities(struct ipmi_intf * intf, + uint8_t hpm2_lan_params_start, + struct hpm2_lan_channel_capabilities * caps); +extern int hpm2_detect_max_payload_size(struct ipmi_intf * intf); diff --git a/include/ipmitool/ipmi_intf.h b/include/ipmitool/ipmi_intf.h index 774c6b2..437a427 100644 --- a/include/ipmitool/ipmi_intf.h +++ b/include/ipmitool/ipmi_intf.h @@ -180,7 +180,8 @@ struct ipmi_intf { uint8_t target_channel; uint32_t transit_addr; uint8_t transit_channel; - uint8_t channel_buf_size; + uint16_t max_request_data_size; + uint16_t max_response_data_size; uint8_t devnum; @@ -193,6 +194,8 @@ struct ipmi_intf { struct ipmi_rs *(*send_sol)(struct ipmi_intf * intf, struct ipmi_v2_payload * payload); int (*keepalive)(struct ipmi_intf * intf); int (*set_my_addr)(struct ipmi_intf * intf, uint8_t addr); + void (*set_max_request_data_size)(struct ipmi_intf * intf, uint16_t size); + void (*set_max_response_data_size)(struct ipmi_intf * intf, uint16_t size); }; struct ipmi_intf * ipmi_intf_load(char * name); diff --git a/lib/Makefile.am b/lib/Makefile.am index 3422521..d878b11 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -39,7 +39,7 @@ libipmitool_la_SOURCES = helper.c ipmi_sdr.c ipmi_sel.c ipmi_sol.c ipmi_pef.c \ ipmi_oem.c ipmi_isol.c ipmi_sunoem.c ipmi_fwum.c ipmi_picmg.c \ ipmi_main.c ipmi_tsol.c ipmi_firewall.c ipmi_kontronoem.c \ ipmi_hpmfwupg.c ipmi_sdradd.c ipmi_ekanalyzer.c ipmi_gendev.c \ - ipmi_ime.c ipmi_delloem.c ipmi_dcmi.c \ + ipmi_ime.c ipmi_delloem.c ipmi_dcmi.c hpm2.c \ ../src/plugins/lan/md5.c ../src/plugins/lan/md5.h libipmitool_la_LDFLAGS = -export-dynamic diff --git a/lib/hpm2.c b/lib/hpm2.c new file mode 100644 index 0000000..6f75f03 --- /dev/null +++ b/lib/hpm2.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2012 Pigeon Point Systems. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pigeon Point Systems nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include +#include + +#if HAVE_PRAGMA_PACK +# pragma pack(push, 1) +#endif + +/* HPM.x Get Capabilities request */ +struct hpmx_cmd_get_capabilities_rq { + uint8_t picmg_id; + uint8_t hpmx_id; +} ATTRIBUTE_PACKING; + +/* HPM.2 Get Capabilities response */ +struct hpm2_cmd_get_capabilities_rp { + uint8_t picmg_id; + struct hpm2_lan_attach_capabilities caps; +} ATTRIBUTE_PACKING; + +#if HAVE_PRAGMA_PACK +# pragma pack(pop) +#endif + +/* IPMI Get LAN Configuration Parameters command */ +#define IPMI_LAN_GET_CONFIG 0x02 + +int hpm2_get_capabilities(struct ipmi_intf * intf, + struct hpm2_lan_attach_capabilities * caps) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + struct hpmx_cmd_get_capabilities_rq rq; + + /* reset result */ + memset(caps, 0, sizeof(struct hpm2_lan_attach_capabilities)); + + /* prepare request */ + rq.picmg_id = 0; + rq.hpmx_id = 2; + + /* prepare request */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_PICMG; + req.msg.cmd = HPM2_GET_LAN_ATTACH_CAPABILITIES; + req.msg.data = (uint8_t *)&rq; + req.msg.data_len = sizeof(rq); + + + /* send */ + rsp = intf->sendrecv(intf, &req); + + if (!rsp) { + lprintf(LOG_NOTICE, "Error sending request."); + return -1; + } + + if (rsp->ccode == 0xC1) { + lprintf(LOG_DEBUG, "IPM Controller is not HPM.2 compatible"); + return rsp->ccode; + } else if (rsp->ccode) { + lprintf(LOG_NOTICE, "Get HPM.x Capabilities request failed," + " compcode = %x", rsp->ccode); + return rsp->ccode; + } + + /* check response length */ + if (rsp->data_len < 2 || rsp->data_len > 10) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* check HPM.x identifier */ + if (rsp->data[1] != 2) { + lprintf(LOG_NOTICE, "Bad HPM.x ID, id=%d", rsp->data[1]); + return rsp->ccode; + } + + /* + * this hardly can happen, since completion code is already checked. + * but check for safety + */ + if (rsp->data_len < 4) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* copy HPM.2 capabilities */ + memcpy(caps, rsp->data + 2, rsp->data_len - 2); + +#if WORDS_BIGENDIAN + /* swap bytes to convert from little-endian format */ + caps->lan_channel_mask = BSWAP_16(caps->lan_channel_mask); +#endif + + /* check HPM.2 revision */ + if (caps->hpm2_revision_id != HPM2_REVISION) { + lprintf(LOG_NOTICE, "Bad HPM.2 revision, rev=%d", + caps->hpm2_revision_id); + return -1; + } + + if (!caps->lan_channel_mask) { + return -1; + } + + /* check response length */ + if (rsp->data_len < 8) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* check HPM.2 LAN parameters start */ + if (caps->hpm2_lan_params_start < 0xC0) { + lprintf(LOG_NOTICE, "Bad HPM.2 LAN params start, start=%x", + caps->hpm2_lan_params_start); + return -1; + } + + /* check HPM.2 LAN parameters revision */ + if (caps->hpm2_lan_params_rev != HPM2_LAN_PARAMS_REV) { + lprintf(LOG_NOTICE, "Bad HPM.2 LAN params revision, rev=%d", + caps->hpm2_lan_params_rev); + return -1; + } + + /* check for HPM.2 SOL extension */ + if (!(caps->hpm2_caps & HPM2_CAPS_SOL_EXTENSION)) { + /* no further checks */ + return 0; + } + + /* check response length */ + if (rsp->data_len < 10) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* check HPM.2 SOL parameters start */ + if (caps->hpm2_sol_params_start < 0xC0) { + lprintf(LOG_NOTICE, "Bad HPM.2 SOL params start, start=%x", + caps->hpm2_sol_params_start); + return -1; + } + + /* check HPM.2 SOL parameters revision */ + if (caps->hpm2_sol_params_rev != HPM2_SOL_PARAMS_REV) { + lprintf(LOG_NOTICE, "Bad HPM.2 SOL params revision, rev=%d", + caps->hpm2_sol_params_rev); + return -1; + } + + return 0; +} + +int hpm2_get_lan_channel_capabilities(struct ipmi_intf * intf, + uint8_t hpm2_lan_params_start, + struct hpm2_lan_channel_capabilities * caps) +{ + struct ipmi_rq req; + struct ipmi_rs * rsp; + uint8_t rq[4]; + + /* reset result */ + memset(caps, 0, sizeof(struct hpm2_lan_channel_capabilities)); + + /* prepare request */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = IPMI_LAN_GET_CONFIG; + req.msg.data = (uint8_t *)&rq; + req.msg.data_len = sizeof(rq); + + /* prepare request data */ + rq[0] = 0xE; /* sending channel */ + rq[1] = hpm2_lan_params_start; /* HPM.2 Channel Caps */ + rq[2] = rq[3] = 0; + + /* send */ + rsp = intf->sendrecv(intf, &req); + + if (rsp) { + lprintf(LOG_NOTICE, "Error sending request"); + return -1; + } + + if (rsp->ccode == 0x80) { + lprintf(LOG_DEBUG, "HPM.2 Channel Caps parameter is not supported"); + return rsp->ccode; + } else if (rsp->ccode) { + lprintf(LOG_NOTICE, "Get LAN Configuration Parameters request failed," + " compcode = %x", rsp->ccode); + return rsp->ccode; + } + + /* check response length */ + if (rsp->data_len != sizeof (struct hpm2_lan_channel_capabilities) + 1) { + lprintf(LOG_NOTICE, "Bad response length, len=%d", rsp->data_len); + return -1; + } + + /* check parameter revision */ + if (rsp->data[0] != HPM2_LAN_PARAMS_REV) { + lprintf(LOG_NOTICE, "Bad HPM.2 LAN parameter revision, rev=%d", + rsp->data[0]); + return -1; + } + + /* copy parameter data */ + memcpy(caps, &rsp->data[1], sizeof (struct hpm2_lan_channel_capabilities)); + +#if WORDS_BIGENDIAN + /* swap bytes to convert from little-endian format */ + caps->max_inbound_pld_size = BSWAP_16(caps->max_inbound_pld_size); + caps->max_outbound_pld_size = BSWAP_16(caps->max_outbound_pld_size); +#endif + + return 0; +} + +int hpm2_detect_max_payload_size(struct ipmi_intf * intf) +{ + struct hpm2_lan_attach_capabilities attach_caps; + struct hpm2_lan_channel_capabilities channel_caps; + int err; + + /* query HPM.2 support */ + err = hpm2_get_capabilities(intf, &attach_caps); + + /* check if HPM.2 is supported */ + if (err != 0 || !attach_caps.lan_channel_mask) { + return err; + } + + /* query channel capabilities */ + err = hpm2_get_lan_channel_capabilities(intf, + attach_caps.hpm2_lan_params_start, &channel_caps); + + /* check if succeeded */ + if (err != 0) { + return err; + } + + /* update request and response sizes */ + ipmi_intf_set_max_request_data_size(intf, + channel_caps.max_inbound_pld_size - 7); + ipmi_intf_set_max_response_data_size(intf, + channel_caps.max_outbound_pld_size - 8); + + /* print debug info */ + lprintf(LOG_DEBUG, "Set maximum request size to %d\n" + "Set maximum response size to %d", + intf->max_request_data_size, intf->max_response_data_size); + + return 0; +} diff --git a/lib/ipmi_fru.c b/lib/ipmi_fru.c index 09d5abe..6aeead0 100644 --- a/lib/ipmi_fru.c +++ b/lib/ipmi_fru.c @@ -454,12 +454,25 @@ write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, /* initialize request size only once */ if (fru->max_write_size == 0) { - if (intf->channel_buf_size != 0) { - /* subtract 1 byte for FRU ID an 2 bytes for offset */ - fru->max_write_size = intf->channel_buf_size - 3; + uint16_t max_rq_size = ipmi_intf_get_max_request_data_size(intf); + + /* validate lower bound of the maximum request data size */ + if (max_rq_size <= 3) { + lprintf(LOG_ERROR, "Maximum request size is too small to send " + "a write request"); + return -1; + } + + /* + * Write FRU Info command returns the number of written bytes in + * a single byte field. + */ + if (max_rq_size - 3 > 255) { + /* Limit the max write size with 255 bytes. */ + fru->max_write_size = 255; } else { /* subtract 1 byte for FRU ID an 2 bytes for offset */ - fru->max_write_size = 32 - 3; + fru->max_write_size = max_rq_size - 3; } /* check word access */ @@ -611,8 +624,25 @@ read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, req.msg.data_len = 4; if (fru->max_read_size == 0) { - /* subtract 1 byte for completion code and 1 for byte count */ - fru->max_read_size = 32 - 2; + uint16_t max_rs_size = ipmi_intf_get_max_response_data_size(intf) - 1; + + /* validate lower bound of the maximum response data size */ + if (max_rs_size <= 1) { + lprintf(LOG_ERROR, "Maximum response size is too small to send " + "a read request"); + return -1; + } + + /* + * Read FRU Info command may read up to 255 bytes of data. + */ + if (max_rs_size - 1 > 255) { + /* Limit the max read size with 255 bytes. */ + fru->max_read_size = 255; + } else { + /* subtract 1 byte for bytes count */ + fru->max_write_size = max_rs_size - 1; + } /* check word access */ if (fru->access) { diff --git a/lib/ipmi_hpmfwupg.c b/lib/ipmi_hpmfwupg.c index 0a56857..69950b7 100644 --- a/lib/ipmi_hpmfwupg.c +++ b/lib/ipmi_hpmfwupg.c @@ -1072,38 +1072,6 @@ HpmfwupgUpgradeStage(struct ipmi_intf *intf, return rc; } -int -get_max_rq_data_size(struct ipmi_intf *intf) -{ - int bufLength; - /* Check if we receive size in parameters */ - if(intf->channel_buf_size != 0) { - /* Plan for overhead */ - if (intf->target_addr == intf->my_addr) { - bufLength = intf->channel_buf_size - 9; - } else { - bufLength = intf->channel_buf_size - 11; - } - } else if (strstr(intf->name,"lan") != NULL) { - /* Find max buffer length according the connection - * parameters - */ - bufLength = HPMFWUPG_SEND_DATA_COUNT_LAN - 2; - if (intf->transit_addr != intf->my_addr - && intf->transit_addr != 0) { - bufLength -= 8; - } - } else if (strstr(intf->name,"open") != NULL - && intf->target_addr == intf->my_addr) { - bufLength = HPMFWUPG_SEND_DATA_COUNT_KCS - 2; - } else if (intf->target_channel == 7) { - bufLength = HPMFWUPG_SEND_DATA_COUNT_IPMBL; - } else { - bufLength = HPMFWUPG_SEND_DATA_COUNT_IPMB; - } - return bufLength; -} - int HpmFwupgActionUploadFirmware(struct HpmfwupgComponentBitMask components, struct HpmfwupgUpgradeCtx *pFwupgCtx, @@ -1138,6 +1106,8 @@ HpmFwupgActionUploadFirmware(struct HpmfwupgComponentBitMask components, unsigned char mode = 0; unsigned char componentId = 0x00; unsigned char componentIdByte = 0x00; + uint16_t max_rq_size; + /* Save component ID on which the upload is done */ componentIdByte = components.ComponentBits.byte; while ((componentIdByte>>= 1) != 0) { @@ -1150,8 +1120,19 @@ HpmFwupgActionUploadFirmware(struct HpmfwupgComponentBitMask components, pDataInitial = ((unsigned char *)pFwImage + sizeof(struct HpmfwupgFirmwareImage)); pData = pDataInitial; + /* Find max buffer length according the connection parameters */ - bufLength = get_max_rq_data_size(intf); + max_rq_size = ipmi_intf_get_max_request_data_size(intf); + + /* validate lower bound of max request size */ + if (max_rq_size <= sizeof(struct HpmfwupgUploadFirmwareBlockReq)) { + lprintf(LOG_ERROR, "Maximum request size is too small to " + "send a upload request."); + return HPMFWUPG_ERROR; + } + + bufLength = max_rq_size - sizeof(struct HpmfwupgUploadFirmwareBlockReq); + /* Get firmware length */ firmwareLength = pFwImage->length[0]; firmwareLength|= (pFwImage->length[1] << 8) & 0xff00; @@ -1178,8 +1159,7 @@ HpmFwupgActionUploadFirmware(struct HpmfwupgComponentBitMask components, if (!skip) { HpmDisplayUpgrade(0,0,1,0); /* Initialize parameters */ - uploadCmd.req = malloc(get_max_rq_data_size(intf) - + sizeof(struct HpmfwupgUploadFirmwareBlockReq)); + uploadCmd.req = malloc(max_rq_size); if (!uploadCmd.req) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return HPMFWUPG_ERROR; diff --git a/lib/ipmi_main.c b/lib/ipmi_main.c index 1885bb5..14ca183 100644 --- a/lib/ipmi_main.c +++ b/lib/ipmi_main.c @@ -982,13 +982,15 @@ ipmi_main(int argc, char ** argv, } /* Enable Big Buffer when requested */ - ipmi_main_intf->channel_buf_size = 0; if ( my_long_packet_size != 0 ) { - printf("Setting large buffer to %i\n", my_long_packet_size); - if (ipmi_kontronoem_set_large_buffer( ipmi_main_intf, my_long_packet_size ) == 0) - { + /* Enable Big Buffer when requested */ + if (!ipmi_oem_active(ipmi_main_intf, "kontron") || + ipmi_kontronoem_set_large_buffer(ipmi_main_intf, + my_long_packet_size ) == 0) { + printf("Setting large buffer to %i\n", my_long_packet_size); my_long_packet_set = 1; - ipmi_main_intf->channel_buf_size = my_long_packet_size; + ipmi_intf_set_max_request_data_size(ipmi_main_intf, + my_long_packet_size); } } @@ -1002,8 +1004,10 @@ ipmi_main(int argc, char ** argv, rc = ipmi_cmd_run(ipmi_main_intf, NULL, 0, NULL); if (my_long_packet_set == 1) { - /* Restore defaults */ - ipmi_kontronoem_set_large_buffer( ipmi_main_intf, 0 ); + if (ipmi_oem_active(ipmi_main_intf, "kontron")) { + /* Restore defaults */ + ipmi_kontronoem_set_large_buffer( ipmi_main_intf, 0 ); + } } /* clean repository caches */ diff --git a/lib/ipmi_oem.c b/lib/ipmi_oem.c index f0fd598..89495c0 100644 --- a/lib/ipmi_oem.c +++ b/lib/ipmi_oem.c @@ -67,6 +67,10 @@ static struct ipmi_oem_handle ipmi_oem_list[] = { name: "i82571spt", desc: "Intel 82571 MAC with integrated RMCP+ support in super pass-through mode", }, + { + name: "kontron", + desc: "Kontron OEM big buffer support" + }, { 0 } }; diff --git a/lib/ipmi_sdr.c b/lib/ipmi_sdr.c index d44bbbb..fa7b082 100644 --- a/lib/ipmi_sdr.c +++ b/lib/ipmi_sdr.c @@ -59,7 +59,7 @@ extern int verbose; static int use_built_in; /* Uses DeviceSDRs instead of SDRR */ -static int sdr_max_read_len = GET_SDR_ENTIRE_RECORD; +static int sdr_max_read_len = 0; static int sdr_extended = 0; static long sdriana = 0; @@ -3024,6 +3024,17 @@ ipmi_sdr_get_record(struct ipmi_intf * intf, struct sdr_get_rs * header, req.msg.data = (uint8_t *) & sdr_rq; req.msg.data_len = sizeof (sdr_rq); + /* check if max length is null */ + if ( sdr_max_read_len == 0 ) { + /* get maximum response size */ + sdr_max_read_len = ipmi_intf_get_max_response_data_size(intf) - 2; + + /* cap the number of bytes to read */ + if (sdr_max_read_len > 0xFE) { + sdr_max_read_len = 0xFE; + } + } + /* read SDR record with partial reads * because a full read usually exceeds the maximum * transport buffer size. (completion code 0xca) diff --git a/src/plugins/ipmi_intf.c b/src/plugins/ipmi_intf.c index 48e2b61..0fa76be 100644 --- a/src/plugins/ipmi_intf.c +++ b/src/plugins/ipmi_intf.c @@ -53,6 +53,8 @@ #include #include +#define IPMI_DEFAULT_PAYLOAD_SIZE 25 + #ifdef IPMI_INTF_OPEN extern struct ipmi_intf ipmi_open_intf; #endif @@ -497,3 +499,153 @@ ipmi_intf_socket_connect(struct ipmi_intf * intf) } #endif +uint16_t +ipmi_intf_get_max_request_data_size(struct ipmi_intf * intf) +{ + int16_t size; + + size = intf->max_request_data_size; + + /* check if request size is not specified */ + if (!size) { + /* + * The IPMB standard overall message length for ‘non -bridging’ + * messages is specified as 32 bytes, maximum, including slave + * address. This sets the upper limit for typical IPMI messages. + * With the exception of messages used for bridging messages to + * other busses or interfaces (e.g. Master Write-Read and Send Message) + * IPMI messages should be designed to fit within this 32-byte maximum. + * In order to support bridging, the Master Write -Read and Send Message + * commands are allowed to exceed the 32-byte maximum transaction on IPMB + */ + + size = IPMI_DEFAULT_PAYLOAD_SIZE; + + /* check if message is forwarded */ + if (intf->target_addr && intf->target_addr != intf->my_addr) { + /* add Send Message request size */ + size += 8; + } + } + + /* check if message is forwarded */ + if (intf->target_addr && intf->target_addr != intf->my_addr) { + /* subtract send message request size */ + size -= 8; + + /* + * Check that forwarded request size is not greater + * than the default payload size. + */ + if (size > IPMI_DEFAULT_PAYLOAD_SIZE) { + size = IPMI_DEFAULT_PAYLOAD_SIZE; + } + + /* check for double bridging */ + if (intf->transit_addr && intf->transit_addr != intf->target_addr) { + /* subtract inner send message request size */ + size -= 8; + } + } + + /* check for underflow */ + if (size < 0) { + return 0; + } + + return size; +} + +uint16_t +ipmi_intf_get_max_response_data_size(struct ipmi_intf * intf) +{ + int16_t size; + + size = intf->max_response_data_size; + + /* check if response size is not specified */ + if (!size) { + /* + * The IPMB standard overall message length for ‘non -bridging’ + * messages is specified as 32 bytes, maximum, including slave + * address. This sets the upper limit for typical IPMI messages. + * With the exception of messages used for bridging messages to + * other busses or interfaces (e.g. Master Write-Read and Send Message) + * IPMI messages should be designed to fit within this 32-byte maximum. + * In order to support bridging, the Master Write -Read and Send Message + * commands are allowed to exceed the 32-byte maximum transaction on IPMB + */ + + size = IPMI_DEFAULT_PAYLOAD_SIZE; /* response length with subtracted header and checksum byte */ + + /* check if message is forwarded */ + if (intf->target_addr && intf->target_addr != intf->my_addr) { + /* add Send Message header size */ + size += 7; + } + } + + /* check if message is forwarded */ + if (intf->target_addr && intf->target_addr != intf->my_addr) { + /* + * Some IPMI controllers like PICMG AMC Carriers embed responses + * to the forwarded messages into the Send Message response. + * In order to be sure that the response is not truncated, + * subtract the internal message header size. + */ + size -= 8; + + /* + * Check that forwarded response is not greater + * than the default payload size. + */ + if (size > IPMI_DEFAULT_PAYLOAD_SIZE) { + size = IPMI_DEFAULT_PAYLOAD_SIZE; + } + + /* check for double bridging */ + if (intf->transit_addr && intf->transit_addr != intf->target_addr) { + /* subtract inner send message header size */ + size -= 8; + } + } + + /* check for underflow */ + if (size < 0) { + return 0; + } + + return size; +} + +void +ipmi_intf_set_max_request_data_size(struct ipmi_intf * intf, uint16_t size) +{ + if (size < IPMI_DEFAULT_PAYLOAD_SIZE) { + lprintf(LOG_ERR, "Request size is too small (%d), leave default size", + size); + return; + } + + if (intf->set_max_request_data_size) { + intf->set_max_request_data_size(intf, size); + } else { + intf->max_request_data_size = size; + } +} + +void +ipmi_intf_set_max_response_data_size(struct ipmi_intf * intf, uint16_t size) +{ + if (size < IPMI_DEFAULT_PAYLOAD_SIZE - 1) { + lprintf(LOG_ERR, "Response size is too small (%d), leave default size", + size); + return; + } + + if (intf->set_max_response_data_size) { + intf->set_max_response_data_size(intf, size); + } else { + intf->max_response_data_size = size; + } +} diff --git a/src/plugins/lan/lan.c b/src/plugins/lan/lan.c index fc90000..fb1a633 100644 --- a/src/plugins/lan/lan.c +++ b/src/plugins/lan/lan.c @@ -52,6 +52,7 @@ #include #include #include +#include #if HAVE_CONFIG_H # include @@ -67,6 +68,13 @@ #define IPMI_LAN_PORT 0x26f #define IPMI_LAN_CHANNEL_E 0x0e +/* + * LAN interface is required to support 45 byte request transactions and + * 42 byte response transactions. + */ +#define IPMI_LAN_MAX_REQUEST_SIZE 38 /* 45 - 7 */ +#define IPMI_LAN_MAX_RESPONSE_SIZE 34 /* 42 - 8 */ + extern const struct valstr ipmi_privlvl_vals[]; extern const struct valstr ipmi_authtype_session_vals[]; extern int verbose; @@ -88,6 +96,8 @@ static int ipmi_lan_send_rsp(struct ipmi_intf * intf, struct ipmi_rs * rsp); static int ipmi_lan_open(struct ipmi_intf * intf); static void ipmi_lan_close(struct ipmi_intf * intf); static int ipmi_lan_ping(struct ipmi_intf * intf); +static void ipmi_lan_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size); +static void ipmi_lan_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size); struct ipmi_intf ipmi_lan_intf = { name: "lan", @@ -100,6 +110,8 @@ struct ipmi_intf ipmi_lan_intf = { recv_sol: ipmi_lan_recv_sol, send_sol: ipmi_lan_send_sol, keepalive: ipmi_lan_keepalive, + set_max_request_data_size: ipmi_lan_set_max_rq_data_size, + set_max_response_data_size: ipmi_lan_set_max_rp_data_size, target_addr: IPMI_BMC_SLAVE_ADDR, }; @@ -2055,6 +2067,10 @@ ipmi_lan_open(struct ipmi_intf * intf) } intf->manufacturer_id = ipmi_get_oem(intf); + + /* automatically detect interface request and response sizes */ + hpm2_detect_max_payload_size(intf); + return intf->fd; } @@ -2067,5 +2083,30 @@ ipmi_lan_setup(struct ipmi_intf * intf) return -1; } memset(intf->session, 0, sizeof(struct ipmi_session)); + + /* setup default LAN maximum request and response sizes */ + intf->max_request_data_size = IPMI_LAN_MAX_REQUEST_SIZE; + intf->max_response_data_size = IPMI_LAN_MAX_RESPONSE_SIZE; + return 0; } + +static void +ipmi_lan_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size) +{ + if (size + 7 > 0xFF) { + size = 0xFF - 7; + } + + intf->max_request_data_size = size; +} + +static void +ipmi_lan_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size) +{ + if (size + 8 > 0xFF) { + size = 0xFF - 8; + } + + intf->max_response_data_size = size; +} diff --git a/src/plugins/lanplus/lanplus.c b/src/plugins/lanplus/lanplus.c index 7b3c6be..9cf3c5e 100644 --- a/src/plugins/lanplus/lanplus.c +++ b/src/plugins/lanplus/lanplus.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,13 @@ #include "rmcp.h" #include "asf.h" +/* + * LAN interface is required to support 45 byte request transactions and + * 42 byte response transactions. + */ +#define IPMI_LAN_MAX_REQUEST_SIZE 38 /* 45 - 7 */ +#define IPMI_LAN_MAX_RESPONSE_SIZE 34 /* 42 - 8 */ + extern const struct valstr ipmi_rakp_return_codes[]; extern const struct valstr ipmi_priv_levels[]; extern const struct valstr ipmi_auth_algorithms[]; @@ -112,8 +120,10 @@ static int check_sol_packet_for_new_data( static void ack_sol_packet( struct ipmi_intf * intf, struct ipmi_rs * rsp); +static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size); +static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size); -static uint8_t bridgePossible = 0; +static uint8_t bridgePossible = 0; struct ipmi_intf ipmi_lanplus_intf = { name: "lanplus", @@ -125,6 +135,8 @@ struct ipmi_intf ipmi_lanplus_intf = { recv_sol: ipmi_lanplus_recv_sol, send_sol: ipmi_lanplus_send_sol, keepalive: ipmi_lanplus_keepalive, + set_max_request_data_size: ipmi_lanp_set_max_rq_data_size, + set_max_response_data_size: ipmi_lanp_set_max_rp_data_size, target_addr: IPMI_BMC_SLAVE_ADDR, }; @@ -3473,6 +3485,9 @@ ipmi_lanplus_open(struct ipmi_intf * intf) intf->manufacturer_id = ipmi_get_oem(intf); bridgePossible = 1; + /* automatically detect interface request and response sizes */ + hpm2_detect_max_payload_size(intf); + return intf->fd; fail: @@ -3624,5 +3639,46 @@ static int ipmi_lanplus_setup(struct ipmi_intf * intf) return -1; } memset(intf->session, 0, sizeof(struct ipmi_session)); + + /* setup default LAN maximum request and response sizes */ + intf->max_request_data_size = IPMI_LAN_MAX_REQUEST_SIZE; + intf->max_response_data_size = IPMI_LAN_MAX_RESPONSE_SIZE; + return 0; } + +static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size) +{ + if (intf->session->cipher_suite_id == 3) { + /* + * encrypted payload can only be multiple of 16 bytes + */ + size &= ~15; + + /* + * decrement payload size on confidentiality header size + * plus minimal confidentiality trailer size + */ + size -= (16 + 1); + } + + intf->max_request_data_size = size; +} + +static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size) +{ + if (intf->session->cipher_suite_id == 3) { + /* + * encrypted payload can only be multiple of 16 bytes + */ + size &= ~15; + + /* + * decrement payload size on confidentiality header size + * plus minimal confidentiality trailer size + */ + size -= (16 + 1); + } + + intf->max_response_data_size = size; +} diff --git a/src/plugins/open/open.c b/src/plugins/open/open.c index fe1fd75..92b6f37 100644 --- a/src/plugins/open/open.c +++ b/src/plugins/open/open.c @@ -65,6 +65,22 @@ # include "open.h" #endif +/** + * Maximum input message size for KCS/SMIC is 40 with 2 utility bytes and + * 38 bytes of data. + * Maximum input message size for BT is 42 with 4 utility bytes and + * 38 bytes of data. + */ +#define IPMI_OPENIPMI_MAX_RQ_DATA_SIZE 38 + +/** + * Maximum output message size for KCS/SMIC is 38 with 2 utility bytes, a byte + * for completion code and 35 bytes of data. + * Maximum output message size for BT is 40 with 4 utility bytes, a byte + * for completion code and 35 bytes of data. + */ +#define IPMI_OPENIPMI_MAX_RS_DATA_SIZE 35 + extern int verbose; static int @@ -401,9 +417,19 @@ ipmi_openipmi_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req) return &rsp; } +int ipmi_openipmi_setup(struct ipmi_intf * intf) +{ + /* set default payload size */ + intf->max_request_data_size = IPMI_OPENIPMI_MAX_RQ_DATA_SIZE; + intf->max_response_data_size = IPMI_OPENIPMI_MAX_RS_DATA_SIZE; + + return 0; +} + struct ipmi_intf ipmi_open_intf = { name: "open", desc: "Linux OpenIPMI Interface", + setup: ipmi_openipmi_setup, open: ipmi_openipmi_open, close: ipmi_openipmi_close, sendrecv: ipmi_openipmi_send_cmd, diff --git a/src/plugins/serial/serial_basic.c b/src/plugins/serial/serial_basic.c index 23c98b7..5f4b926 100644 --- a/src/plugins/serial/serial_basic.c +++ b/src/plugins/serial/serial_basic.c @@ -189,6 +189,10 @@ serial_bm_setup(struct ipmi_intf * intf) return -1; } memset(intf->session, 0, sizeof(struct ipmi_session)); + + /* setup default LAN maximum request and response sizes */ + intf->max_request_data_size = SERIAL_BM_MAX_RQ_SIZE; + intf->max_response_data_size = SERIAL_BM_MAX_RS_SIZE; return 0; } diff --git a/src/plugins/serial/serial_terminal.c b/src/plugins/serial/serial_terminal.c index c82073e..41d3753 100644 --- a/src/plugins/serial/serial_terminal.c +++ b/src/plugins/serial/serial_terminal.c @@ -894,6 +894,10 @@ ipmi_serial_term_setup(struct ipmi_intf * intf) } memset(intf->session, 0, sizeof(struct ipmi_session)); + + /* setup default LAN maximum request and response sizes */ + intf->max_request_data_size = IPMI_SERIAL_MAX_RQ_SIZE; + intf->max_response_data_size = IPMI_SERIAL_MAX_RS_SIZE; return 0; }