/* * Copyright (c) 2002, Intel Corporation * * 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 Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* Purpose: This file contains the entry point that opens the IMB device in * order to issue the IMB driver API related IOCTLs. This file implements the * IMB driver API for the Server Management Agents */ /* Use -DLINUX_DEBUG_MAX in the Makefile, resp. CFLAGS if you want a dump of the * memory to debug mmap system call in MapPhysicalMemory() below. */ #define IMB_API #ifdef WIN32 # define NO_MACRO_ARGS 1 # include # include #else /* LINUX, SCO_UW, UNIX */ # include # include # include # include # include # include # include # include # include # include #endif #include "imbapi.h" #include #include #ifdef SCO_UW # define NO_MACRO_ARGS 1 # define __FUNCTION__ "func" # define IMB_DEVICE "/dev/instru/mismic" #else # define IMB_DEVICE "/dev/imb" #endif #if !defined(PAGESIZE) && defined(PAGE_SIZE) # define PAGESIZE PAGE_SIZE #endif #if !defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE) # define _SC_PAGESIZE _SC_PAGE_SIZE #endif HANDLE AsyncEventHandle = 0; static int IpmiVersion; /* GLOBAL VARIABLES */ /* dummy place holder. See deviceiocontrol. */ IO_STATUS_BLOCK NTstatus; static HANDLE hDevice1; static HANDLE hDevice; static int fDriverTyp; /* from ipmicmd.c */ /* open_imb - Open IMB device. Called from each routine to make sure that open * is done. * * Returns: returns 0 for Fail and 1 for Success, sets hDevice to open handle. */ #ifdef WIN32 int open_imb(void) { /* This routine will be called from all other routines before doing any * interfacing with imb driver. It will open only once. */ IMBPREQUESTDATA requestData; BYTE respBuffer[16]; DWORD respLength; BYTE completionCode; if (hDevice1 != 0) { return 1; } /* Open IMB driver device */ hDevice = CreateFile("\\\\.\\Imb", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == NULL || hDevice == INVALID_HANDLE_VALUE) { return 0; } /* Detect the IPMI version for processing requests later. This * is a crude but most reliable method to differentiate between * old IPMI versions and the 1.0 version. If we had used the * version field instead then we would have had to revalidate * all the older platforms (pai 4/27/99) */ requestData.cmdType = GET_DEVICE_ID; requestData.rsSa = BMC_SA; requestData.rsLun = BMC_LUN; requestData.netFn = APP_NETFN ; requestData.busType = PUBLIC_BUS; requestData.data = NULL; requestData.dataLength = 0; respLength = 16; if ((SendTimedImbpRequest(&requestData, (DWORD)400, respBuffer, &respLength, &completionCode) != ACCESN_OK) || (completionCode != 0)) { CloseHandle(hDevice); return 0; } hDevice1 = hDevice; if (respLength < (IPMI10_GET_DEVICE_ID_RESP_LENGTH - 1)) { IpmiVersion = IPMI_09_VERSION; } else { if (respBuffer[4] == 0x51) { IpmiVersion = IPMI_15_VERSION; } else { IpmiVersion = IPMI_10_VERSION; } } return 1; } /* end open_imb for Win32 */ #else /* LINUX, SCO_UW, etc. */ int open_imb(void) { /* This routine will be called from all other routines before doing any * interfacing with imb driver. It will open only once. */ IMBPREQUESTDATA requestData; BYTE respBuffer[16]; DWORD respLength; BYTE completionCode; int my_ret_code; if (hDevice1 != 0) { return 1; } lprintf(LOG_DEBUG, "%s: opening the driver", __FUNCTION__); /* printf("open_imb: " "IOCTL_IMB_SEND_MESSAGE =%x \n" "IOCTL_IMB_GET_ASYNC_MSG=%x \n" "IOCTL_IMB_MAP_MEMORY = %x \n" "IOCTL_IMB_UNMAP_MEMORY= %x \n" "IOCTL_IMB_SHUTDOWN_CODE=%x \n" "IOCTL_IMB_REGISTER_ASYNC_OBJ =%x \n" "IOCTL_IMB_DEREGISTER_ASYNC_OBJ=%x \n" "IOCTL_IMB_CHECK_EVENT =%x \n" "IOCTL_IMB_POLL_ASYNC =%x \n", IOCTL_IMB_SEND_MESSAGE, IOCTL_IMB_GET_ASYNC_MSG, IOCTL_IMB_MAP_MEMORY, IOCTL_IMB_UNMAP_MEMORY, IOCTL_IMB_SHUTDOWN_CODE, IOCTL_IMB_REGISTER_ASYNC_OBJ, IOCTL_IMB_DEREGISTER_ASYNC_OBJ, IOCTL_IMB_CHECK_EVENT , IOCTL_IMB_POLL_ASYNC); */ /* O_NDELAY flag will cause problems later when driver makes * you wait. Hence removing it. */ if ((hDevice1 = open(IMB_DEVICE, O_RDWR)) < 0) { char buf[128]; hDevice1 = 0; if (fDriverTyp != 0) { /* not 1st time */ sprintf(buf,"%s %s: open(%s) failed", __FILE__, __FUNCTION__, IMB_DEVICE); perror(buf); } return 0; } /* Detect the IPMI version for processing requests later. * This is a crude but most reliable method to differentiate * between old IPMI versions and the 1.0 version. If we had used the * version field instead then we would have had to revalidate all * the older platforms (pai 4/27/99) */ requestData.cmdType = GET_DEVICE_ID; requestData.rsSa = BMC_SA; requestData.rsLun = BMC_LUN; requestData.netFn = APP_NETFN ; requestData.busType = PUBLIC_BUS; requestData.data = NULL; requestData.dataLength = 0; respLength = 16; lprintf(LOG_DEBUG, "%s: opened driver, getting IPMI version", __FUNCTION__); if (((my_ret_code = SendTimedImbpRequest(&requestData, (DWORD)400, respBuffer, (int *)&respLength, &completionCode)) != ACCESN_OK) || (completionCode != 0)) { printf("%s: SendTimedImbpRequest error. Ret = %d CC = 0x%X\n", __FUNCTION__, my_ret_code, completionCode); close(hDevice1); hDevice1 = 0; return 0; } if (respLength < (IPMI10_GET_DEVICE_ID_RESP_LENGTH - 1)) { IpmiVersion = IPMI_09_VERSION; } else { if (respBuffer[4] == 0x51) { IpmiVersion = IPMI_15_VERSION; } else { IpmiVersion = IPMI_10_VERSION; } } lprintf(LOG_DEBUG, "%s: IPMI version 0x%x", __FUNCTION__, IpmiVersion); return 1; } /* end open_imb() */ #endif /* ipmi_open_ia */ int ipmi_open_ia(void) { int rc = 0; /* sets hDevice1 */ rc = open_imb(); if (rc == 1) { rc = 0; } else { rc = -1; } return rc; } /* ipmi_close_ia */ int ipmi_close_ia(void) { int rc = 0; if (hDevice1 != 0) { #ifdef WIN32 CloseHandle(hDevice1); #else rc = close(hDevice1); #endif } return rc; } #ifndef WIN32 /* DeviceIoControl - Simulate NT DeviceIoControl using unix calls and structures. * * @dummy_hDevice - handle of device * @dwIoControlCode - control code of operation to perform * @lpvInBuffer, address of buffer for input data * @cbInBuffer, size of input buffer * @lpvOutBuffer, address of output buffer * @cbOutBuffer, size of output buffer * @lpcbBytesReturned, address of actual bytes of output * @lpoOverlapped address of overlapped struct * * returns - FALSE for fail and TRUE for success. Same as standarad NTOS call as * it also sets Ntstatus.status. */ static BOOL DeviceIoControl(HANDLE dummey_hDevice, DWORD dwIoControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, LPOVERLAPPED lpoOverlapped) { struct smi s; int rc; int ioctl_status; rc = open_imb(); if (rc == 0) { return FALSE; } lprintf(LOG_DEBUG, "%s: ioctl cmd = 0x%lx", __FUNCTION__, dwIoControlCode); lprintf(LOG_DEBUG, "cbInBuffer %d cbOutBuffer %d", cbInBuffer, cbOutBuffer); if (cbInBuffer > 41) { cbInBuffer = 41; /* Intel driver max buf */ } s.lpvInBuffer = lpvInBuffer; s.cbInBuffer = cbInBuffer; s.lpvOutBuffer = lpvOutBuffer; s.cbOutBuffer = cbOutBuffer; s.lpcbBytesReturned = lpcbBytesReturned; s.lpoOverlapped = lpoOverlapped; /* dummy place holder. Linux IMB driver doesnt return status or info * via it */ s.ntstatus = (LPVOID)&NTstatus; if ((ioctl_status = ioctl(hDevice1, dwIoControlCode,&s)) < 0) { lprintf(LOG_DEBUG, "%s %s: ioctl cmd = 0x%x failed", __FILE__, __FUNCTION__, dwIoControlCode); return FALSE; } lprintf(LOG_DEBUG, "%s: ioctl_status %d bytes returned = %d", __FUNCTION__, ioctl_status, *lpcbBytesReturned); if (ioctl_status == STATUS_SUCCESS) { lprintf(LOG_DEBUG, "%s returning true", __FUNCTION__); return (TRUE); } else { lprintf(LOG_DEBUG, "%s returning false", __FUNCTION__); return (FALSE); } } #endif /* Used only by UW. Left here for now. IMB driver will not accept this ioctl. */ ACCESN_STATUS StartAsyncMesgPoll() { DWORD retLength; BOOL status; lprintf(LOG_DEBUG, "%s: DeviceIoControl cmd = %x", __FUNCTION__, IOCTL_IMB_POLL_ASYNC); status = DeviceIoControl(hDevice, IOCTL_IMB_POLL_ASYNC, NULL, 0, NULL, 0, &retLength, 0); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if (status == TRUE) { return ACCESN_OK; } else { return ACCESN_ERROR; } } /* SendTimedI2cRequest - This function sends a request to a I2C device. Used by * Upper level agents (sis modules) to access dumb I2c devices. * * @reqPtr - pointer to I2C request * timeOut - how long to wait, mSec units * @respDataPtr - where to put response data * @respDataLen - size of response buffer and size of returned data * @completionCode - request status from BMC * * returns - ACCESN_OK else error status code */ ACCESN_STATUS SendTimedI2cRequest(I2CREQUESTDATA *reqPtr, int timeOut, BYTE *respDataPtr, int *respDataLen, BYTE *completionCode) { /* size of write/read request minus any data */ # define MIN_WRI2C_SIZE 3 BOOL status; BYTE responseData[MAX_IMB_RESP_SIZE]; ImbResponseBuffer *resp = (ImbResponseBuffer *)responseData; DWORD respLength = sizeof(responseData); BYTE requestData[MAX_IMB_RESP_SIZE]; ImbRequestBuffer *req = (ImbRequestBuffer *)requestData; /* format of a write/read I2C request */ struct WriteReadI2C { BYTE busType; BYTE rsSa; BYTE count; BYTE data[1]; } *wrReq = (struct WriteReadI2C *)req->req.data; /* If the IMB driver is not present return AccessFailed */ req->req.rsSa = BMC_SA; req->req.cmd = WRITE_READ_I2C; req->req.netFn = APP_NETFN; req->req.rsLun = BMC_LUN; req->req.dataLength = reqPtr->dataLength + MIN_WRI2C_SIZE; wrReq->busType = reqPtr->busType; wrReq->rsSa = reqPtr->rsSa; wrReq->count = reqPtr->numberOfBytesToRead; memcpy(wrReq->data, reqPtr->data, reqPtr->dataLength); req->flags = 0; /* convert to uSec units */ req->timeOut = timeOut * 1000; status = DeviceIoControl(hDevice, IOCTL_IMB_SEND_MESSAGE, requestData, sizeof(requestData), &responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if (status != TRUE) { DWORD error; error = GetLastError(); return error; } if (respLength == 0) { return ACCESN_ERROR; } /* give the caller his response */ *completionCode = resp->cCode; *respDataLen = respLength - 1; if ((*respDataLen) && (respDataPtr)) { memcpy(respDataPtr, resp->data, *respDataLen); } return ACCESN_OK; } /* SendTimedEmpMessageResponse - This function sends a response message to the * EMP port. * * @ptr - pointer to the original request from EMP * @responseDataBuf * @responseDataLen * @timeOut - how long to wait, in mSec units * * retruns - OK else error status code */ ACCESN_STATUS SendTimedEmpMessageResponse (ImbPacket *ptr, char *responseDataBuf, int responseDataLen, int timeOut) { BOOL status; BYTE responseData[MAX_IMB_RESP_SIZE]; DWORD respLength = sizeof(responseData); BYTE requestData[MAX_IMB_RESP_SIZE]; ImbRequestBuffer *req = (ImbRequestBuffer *)requestData; int i; int j; /* form the response packet first */ req->req.rsSa = BMC_SA; if (IpmiVersion == IPMI_09_VERSION) { req->req.cmd = WRITE_EMP_BUFFER; } else { req->req.cmd = SEND_MESSAGE; } req->req.netFn = APP_NETFN; req->req.rsLun = 0; i = 0; if (IpmiVersion != IPMI_09_VERSION) { req->req.data[i++] = EMP_CHANNEL; } req->req.data[i++] = ptr->rqSa; req->req.data[i++] = (((ptr->nfLn & 0xfc) | 0x4) | ((ptr->seqLn) & 0x3)); if (IpmiVersion == IPMI_09_VERSION) { req->req.data[i++] = ((~(req->req.data[0] + req->req.data[1])) + 1); } else { req->req.data[i++] = ((~(req->req.data[1] + req->req.data[2])) + 1); } /* though software is responding, we have to provide BMCs slave address * as responder address. */ req->req.data[i++] = BMC_SA; req->req.data[i++] = ((ptr->seqLn & 0xfc) | (ptr->nfLn & 0x3)); req->req.data[i++] = ptr->cmd; for (j = 0; j < responseDataLen; ++j, ++i) { req->req.data[i] = responseDataBuf[j]; } req->req.data[i] = 0; if (IpmiVersion == IPMI_09_VERSION) { j = 0; } else { j = 1; } for (; j < (i - 3); ++j) { req->req.data[i] += req->req.data[j + 3]; } req->req.data[i] = ~(req->req.data[i]) + 1; ++i; req->req.dataLength = i; req->flags = 0; /* convert to uSec units */ req->timeOut = timeOut * 1000; status = DeviceIoControl(hDevice, IOCTL_IMB_SEND_MESSAGE, requestData, sizeof(requestData), responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if ((status != TRUE) || (respLength != 1) || (responseData[0] != 0)) { return ACCESN_ERROR; } return ACCESN_OK; } /* SendTimedEmpMessageResponse_Ex - sends response message to the EMP port. * * @ptr - pointer to the original request from EMP * @responseDataBuf * @responseDataLen * @timeOut - how long to wait, in mSec units * @sessionHandle - This is introduced in IPMI1.5,this is required to be sent in * sendd message command as a parameter, which is then used by BMC * to identify the correct DPC session to send the mesage to. * @channelNumber - There are 3 different channels on which DPC communication * goes on: * * Emp - 1 * * Lan channel one - 6, * * Lan channel two(primary channel) - 7 * * returns - OK else error status code */ ACCESN_STATUS SendTimedEmpMessageResponse_Ex (ImbPacket *ptr, char *responseDataBuf, int responseDataLen, int timeOut, BYTE sessionHandle, BYTE channelNumber) { BOOL status; BYTE responseData[MAX_IMB_RESP_SIZE]; DWORD respLength = sizeof(responseData); BYTE requestData[MAX_IMB_RESP_SIZE]; ImbRequestBuffer *req = (ImbRequestBuffer *)requestData; int i; int j; /*form the response packet first */ req->req.rsSa = BMC_SA; if (IpmiVersion == IPMI_09_VERSION) { req->req.cmd = WRITE_EMP_BUFFER; } else { req->req.cmd = SEND_MESSAGE; } req->req.netFn = APP_NETFN; req->req.rsLun = 0; i = 0; /* checking for the IPMI version & then assigning the channel number for * EMP. Actually the channel number is same in both the versions.This is * just to maintain the consistancy with the same method for LAN. This * is the 1st byte of the SEND MESSAGE command. */ if (IpmiVersion == IPMI_10_VERSION) { req->req.data[i++] = EMP_CHANNEL; } else if (IpmiVersion == IPMI_15_VERSION) { req->req.data[i++] = channelNumber; } /* The second byte of data for SEND MESSAGE starts with session * handle */ req->req.data[i++] = sessionHandle; /* Then it is the response slave address for SEND MESSAGE. */ req->req.data[i++] = ptr->rqSa; /* Then the net function + lun for SEND MESSAGE command. */ req->req.data[i++] = (((ptr->nfLn & 0xfc) | 0x4) | ((ptr->seqLn) & 0x3)); /* Here the checksum is calculated.The checksum calculation starts after * the channel number. So for the IPMI 1.5 version its a checksum of 3 * bytes that is session handle,response slave address & netfun+lun. */ if (IpmiVersion == IPMI_09_VERSION) { req->req.data[i++] = ((~(req->req.data[0] + req->req.data[1])) +1); } else { if (IpmiVersion == IPMI_10_VERSION) { req->req.data[i++] = ((~(req->req.data[1] + req->req.data[2])) + 1); } else { req->req.data[i++] = ((~(req->req.data[2] + req->req.data[3])) + 1); } } /* This is the next byte of the message data for SEND MESSAGE command.It * is the request slave address. */ /* though software is responding, we have to provide BMCs slave address * as responder address. */ req->req.data[i++] = BMC_SA; /* This is just the sequence number,which is the next byte of data for * SEND MESSAGE */ req->req.data[i++] = ((ptr->seqLn & 0xfc) | (ptr->nfLn & 0x3)); /* The next byte is the command like get software ID(00). */ req->req.data[i++] = ptr->cmd; /* after the cmd the data, which is sent by DPC & is retrived using the * get message earlier is sent back to DPC. */ for (j = 0; j < responseDataLen; ++j, ++i) { req->req.data[i] = responseDataBuf[j]; } req->req.data[i] = 0; /* The last byte of data for SEND MESSAGE command is the check sum, which * is calculated from the next byte of the previous checksum that is the * request slave address. */ if (IpmiVersion == IPMI_09_VERSION) { j = 0; } else { if (IpmiVersion == IPMI_10_VERSION) { j = 1; } else { j = 2; } } for (; j < (i - 3); ++j) { req->req.data[i] += req->req.data[j + 3]; } req->req.data[i] = ~(req->req.data[i]) + 1; ++i; req->req.dataLength = i; /* The flags & timeouts are used by the driver internally. */ req->flags = 0; /* convert to uSec units */ req->timeOut = timeOut * 1000; status = DeviceIoControl(hDevice, IOCTL_IMB_SEND_MESSAGE, requestData, sizeof(requestData), responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if ((status != TRUE) || (respLength != 1) || (responseData[0] != 0)) { return ACCESN_ERROR; } return ACCESN_OK; } /* SendTimedLanMessageResponse - sends a response message to the DPC Over Lan * * @ptr - pointer to the original request from EMP * @responseDataBuf * @responseDataLen, * @timeOut - how long to wait, in mSec units * * returns: OK else error status code */ ACCESN_STATUS SendTimedLanMessageResponse(ImbPacket *ptr, char *responseDataBuf, int responseDataLen, int timeOut) { BOOL status; BYTE responseData[MAX_IMB_RESP_SIZE]; DWORD respLength = sizeof(responseData); BYTE requestData[MAX_IMB_RESP_SIZE]; ImbRequestBuffer *req = (ImbRequestBuffer *)requestData; int i; int j; /* Form the response packet first */ req->req.rsSa = BMC_SA; if (IpmiVersion == IPMI_09_VERSION) { req->req.cmd = WRITE_EMP_BUFFER; } else { req->req.cmd = SEND_MESSAGE; } req->req.netFn = APP_NETFN; /* After discussion with firmware team (Shailendra), the lun number * needs to stay at 0 even though the DPC over Lan firmware EPS states * that the lun should be 1 for DPC Over Lan. - Simont (5/17/00) */ req->req.rsLun = 0; i = 0; if (IpmiVersion != IPMI_09_VERSION) { req->req.data[i++] = LAN_CHANNEL; } req->req.data[i++] = ptr->rqSa; req->req.data[i++] = (((ptr->nfLn & 0xfc) | 0x4) | ((ptr->seqLn) & 0x3)); if (IpmiVersion == IPMI_09_VERSION) { req->req.data[i++] = ((~(req->req.data[0] + req->req.data[1])) + 1); } else { req->req.data[i++] = ((~(req->req.data[1] + req->req.data[2])) + 1); } /* Though software is responding, we have to provide BMCs slave address * as responder address. */ req->req.data[i++] = BMC_SA; req->req.data[i++] = ((ptr->seqLn & 0xfc) | (ptr->nfLn & 0x3)); req->req.data[i++] = ptr->cmd; for (j = 0; j < responseDataLen; ++j, ++i) { req->req.data[i] = responseDataBuf[j]; } req->req.data[i] = 0; if (IpmiVersion == IPMI_09_VERSION) { j = 0; } else { j = 1; } for (; j < (i - 3); ++j) { req->req.data[i] += req->req.data[j + 3]; } req->req.data[i] = ~(req->req.data[i]) + 1; ++i; req->req.dataLength = i; req->flags = 0; /* convert to uSec units */ req->timeOut = timeOut * 1000; status = DeviceIoControl(hDevice, IOCTL_IMB_SEND_MESSAGE, requestData, sizeof(requestData), responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if ((status != TRUE) || (respLength != 1) || (responseData[0] != 0)) { return ACCESN_ERROR; } return ACCESN_OK; } /* SendTimedLanMessageResponse_Ex - sends a response message to the DPC Over * LAN. * * @ptr - pointer to the original request from EMP * @responseDataBuf * @responseDataLen * @timeOut - how long to wait, in mSec units * @sessionHandle - This is introduced in IPMI1.5,this is required to be sent in * send message command as a parameter,which is then used by BMC to identify the * correct DPC session to send the mesage to. * @channelNumber - There are 3 different channels on which DPC communication * goes on: * * Emp - 1 * * Lan channel one - 6 * * Lan channel two(primary channel) - 7 * * returns: OK else error status code */ ACCESN_STATUS SendTimedLanMessageResponse_Ex(ImbPacket *ptr, char *responseDataBuf, int responseDataLen, int timeOut, BYTE sessionHandle, BYTE channelNumber) { BOOL status; BYTE responseData[MAX_IMB_RESP_SIZE]; DWORD respLength = sizeof(responseData); BYTE requestData[MAX_IMB_RESP_SIZE]; ImbRequestBuffer *req = (ImbRequestBuffer *)requestData; int i; int j; /* form the response packet first */ req->req.rsSa = BMC_SA; if (IpmiVersion == IPMI_09_VERSION) { req->req.cmd = WRITE_EMP_BUFFER; } else { req->req.cmd = SEND_MESSAGE; } req->req.netFn = APP_NETFN; /* After discussion with firmware team (Shailendra), the lun number * needs to stay at 0 even though the DPC over Lan firmware EPS states * that the lun should be 1 for DPC Over Lan. - Simont (5/17/00) */ req->req.rsLun = 0; i = 0; /* checking for the IPMI version & then assigning the channel number for * LAN accordingly. * This is the 1st byte of the SEND MESSAGE command. */ if (IpmiVersion == IPMI_10_VERSION) { req->req.data[i++] = LAN_CHANNEL; } else if (IpmiVersion == IPMI_15_VERSION) { req->req.data[i++] = channelNumber; } /* The second byte of data for SEND MESSAGE starts with session handle */ req->req.data[i++] = sessionHandle; /* Then it is the response slave address for SEND MESSAGE. */ req->req.data[i++] = ptr->rqSa; /* Then the net function + lun for SEND MESSAGE command. */ req->req.data[i++] = (((ptr->nfLn & 0xfc) | 0x4) | ((ptr->seqLn) & 0x3)); /* Here the checksum is calculated.The checksum calculation starts after * the channel number. So for the IPMI 1.5 version its a checksum of 3 * bytes that is session handle,response slave address & netfun+lun. */ if (IpmiVersion == IPMI_09_VERSION) { req->req.data[i++] = ((~(req->req.data[0] + req->req.data[1])) + 1); } else { if (IpmiVersion == IPMI_10_VERSION) { req->req.data[i++] = ((~(req->req.data[1] + req->req.data[2])) + 1); } else { req->req.data[i++] = ((~(req->req.data[2] + req->req.data[3])) + 1); } } /* This is the next byte of the message data for SEND MESSAGE command.It * is the request slave address. */ /* Though software is responding, we have to provide BMC's slave address * as responder address. */ req->req.data[i++] = BMC_SA; /* This is just the sequence number,which is the next byte of data for * SEND MESSAGE */ req->req.data[i++] = ((ptr->seqLn & 0xfc) | (ptr->nfLn & 0x3)); /* The next byte is the command like get software ID(00). */ req->req.data[i++] = ptr->cmd; /* After the cmd the data ,which is sent by DPC & is retrived using the * get message earlier is sent back to DPC. */ for (j = 0; j < responseDataLen; ++j, ++i) { req->req.data[i] = responseDataBuf[j]; } req->req.data[i] = 0; /* The last byte of data for SEND MESSAGE command is the check sum which * is calculated from the next byte of the previous checksum that is the * request slave address. */ if (IpmiVersion == IPMI_09_VERSION) { j = 0; } else { if (IpmiVersion == IPMI_10_VERSION) { j = 1; } else { j = 2; } } for (; j < (i - 3); ++j) { req->req.data[i] += req->req.data[j + 3]; } req->req.data[i] = ~(req->req.data[i]) + 1; ++i; req->req.dataLength = i; /* The flags & timeouts are used by the driver internally */ req->flags = 0; /* convert to uSec units */ req->timeOut = timeOut * 1000; status = DeviceIoControl(hDevice, IOCTL_IMB_SEND_MESSAGE, requestData, sizeof(requestData), responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if ((status != TRUE) || (respLength != 1) || (responseData[0] != 0)) { return ACCESN_ERROR; } return ACCESN_OK; } /* SendTimedImbpRequest - This function sends a request for BMC implemented function * * @reqPtr - request info and data * @timeOut - how long to wait, in mSec units * @respDataPtr - where to put response data * @respDataLen - how much response data there is * @completionCode - request status from dest controller * * returns: OK else error status code */ ACCESN_STATUS SendTimedImbpRequest(IMBPREQUESTDATA *reqPtr, int timeOut, BYTE *respDataPtr, int *respDataLen, BYTE *completionCode) { BYTE responseData[MAX_BUFFER_SIZE]; ImbResponseBuffer *resp = (ImbResponseBuffer *)responseData; DWORD respLength = sizeof(responseData); BYTE requestData[MAX_BUFFER_SIZE]; ImbRequestBuffer *req = (ImbRequestBuffer *)requestData; BOOL status; req->req.rsSa = reqPtr->rsSa; req->req.cmd = reqPtr->cmdType; req->req.netFn = reqPtr->netFn; req->req.rsLun = reqPtr->rsLun; req->req.dataLength = reqPtr->dataLength; lprintf(LOG_DEBUG, "cmd=%02x, pdata=%p, datalen=%x", req->req.cmd, reqPtr->data, reqPtr->dataLength); memcpy(req->req.data, reqPtr->data, reqPtr->dataLength); req->flags = 0; /* convert to uSec units */ req->timeOut = timeOut * 1000; lprintf(LOG_DEBUG, "%s: rsSa 0x%x cmd 0x%x netFn 0x%x rsLun 0x%x", __FUNCTION__, req->req.rsSa, req->req.cmd, req->req.netFn, req->req.rsLun); status = DeviceIoControl(hDevice, IOCTL_IMB_SEND_MESSAGE, requestData, sizeof(requestData), &responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl returned status = %d", __FUNCTION__, status); #ifdef DBG_IPMI /* TODO */ printf("%s: rsSa %x cmd %x netFn %x lun %x, status=%d, cc=%x, rlen=%d\n", __FUNCTION__, req->req.rsSa, req->req.cmd, req->req.netFn, req->req.rsLun, status, resp->cCode, respLength); #endif if (status != TRUE) { DWORD error; error = GetLastError(); return error; } else if (respLength == 0) { return ACCESN_ERROR; } /* give the caller his response */ *completionCode = resp->cCode; *respDataLen = 0; if ((respLength > 1) && (respDataPtr)) { *respDataLen = respLength - 1; memcpy(respDataPtr,resp->data, *respDataLen); } return ACCESN_OK; } /* SendAsyncImbpRequest - sends a request for Asynchronous IMB implemented function. * * @reqPtr - Pointer to Async IMB request * @seqNo -Sequence Munber * * returns: OK else error status code */ ACCESN_STATUS SendAsyncImbpRequest(IMBPREQUESTDATA *reqPtr, BYTE *seqNo) { BOOL status; BYTE responseData[MAX_IMB_RESP_SIZE]; ImbResponseBuffer *resp = (ImbResponseBuffer *)responseData; DWORD respLength = sizeof(responseData); BYTE requestData[MAX_IMB_RESP_SIZE]; ImbRequestBuffer *req = (ImbRequestBuffer *)requestData; req->req.rsSa = reqPtr->rsSa; req->req.cmd = reqPtr->cmdType; req->req.netFn = reqPtr->netFn; req->req.rsLun = reqPtr->rsLun; req->req.dataLength = reqPtr->dataLength; memcpy(req->req.data, reqPtr->data, reqPtr->dataLength); req->flags = NO_RESPONSE_EXPECTED; /* no timeouts for async sends */ req->timeOut = 0; status = DeviceIoControl(hDevice, IOCTL_IMB_SEND_MESSAGE, requestData, sizeof(requestData), &responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if (status != TRUE) { DWORD error; error = GetLastError(); return error; } else if (respLength != 2) { return ACCESN_ERROR; } /* give the caller his sequence number */ *seqNo = resp->data[0]; return ACCESN_OK; } /* GetAsyncImbpMessage - This function gets the next available async message * with a message ID greater than SeqNo. The message looks like an IMB packet * and the length and Sequence number is returned. * * @msgPtr - request info and data * @msgLen - IN - length of buffer, OUT - msg len * @timeOut - how long to wait for the message * @seqNo - previously returned seq number(or ASYNC_SEQ_START) * @channelNumber * * returns: OK else error status code */ ACCESN_STATUS GetAsyncImbpMessage (ImbPacket *msgPtr, DWORD *msgLen, DWORD timeOut, ImbAsyncSeq *seqNo, DWORD channelNumber) { BOOL status; BYTE responseData[MAX_ASYNC_RESP_SIZE]; BYTE lun; ImbAsyncResponse *resp = (ImbAsyncResponse *)responseData; DWORD respLength = sizeof(responseData); ImbAsyncRequest req; while (1) { if ((msgPtr == NULL) || (msgLen == NULL) || ( seqNo == NULL)) { return ACCESN_ERROR; } /* convert to uSec units */ req.timeOut = timeOut * 1000; req.lastSeq = *seqNo; status = DeviceIoControl(hDevice, IOCTL_IMB_GET_ASYNC_MSG, &req, sizeof(req), &responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if (status != TRUE) { DWORD error = GetLastError(); /* handle "msg not available" specially. it is different * from a random old error. */ switch (error) { case IMB_MSG_NOT_AVAILABLE: return ACCESN_END_OF_DATA; break; default: return ACCESN_ERROR; break; } } else if (respLength < MIN_ASYNC_RESP_SIZE) { return ACCESN_ERROR; } respLength -= MIN_ASYNC_RESP_SIZE; if (*msgLen < respLength) { return ACCESN_ERROR; } /* same code as in NT section */ if (IpmiVersion == IPMI_09_VERSION) { switch (channelNumber) { case IPMB_CHANNEL: lun = IPMB_LUN; break; case EMP_CHANNEL: lun = EMP_LUN; break; default: lun = RESERVED_LUN; break; } if ((lun == RESERVED_LUN) || (lun != ((((ImbPacket *)(resp->data))->nfLn) & 0x3 ))) { *seqNo = resp->thisSeq; continue; } memcpy(msgPtr, resp->data, respLength); *msgLen = respLength; } else { /* it is version 1.0 or better */ if (resp->data[0] != (BYTE)channelNumber) { *seqNo = resp->thisSeq; continue; } memcpy(msgPtr, &(resp->data[1]), (respLength - 1)); *msgLen = respLength - 1; } /* give the caller his sequence number */ *seqNo = resp->thisSeq; return ACCESN_OK; } } /* GetAsyncImbpMessage_Ex - gets the next available async message with a message * ID greater than SeqNo. The message looks like an IMB packet and the length * and Sequence number is returned. * * @msgPtr - request info and data * @msgLen - IN - length of buffer, OUT - msg len * @timeOut - how long to wait for the message * @seqNo - previously returned seq number(or ASYNC_SEQ_START) * @channelNumber * @sessionHandle * @privilege * * returns: OK else error status code */ ACCESN_STATUS GetAsyncImbpMessage_Ex(ImbPacket *msgPtr, DWORD *msgLen, DWORD timeOut, ImbAsyncSeq *seqNo, DWORD channelNumber, BYTE *sessionHandle, BYTE *privilege) { BOOL status; BYTE responseData[MAX_ASYNC_RESP_SIZE]; BYTE lun; ImbAsyncResponse *resp = (ImbAsyncResponse *)responseData; DWORD respLength = sizeof(responseData); ImbAsyncRequest req; while (1) { if ((msgPtr == NULL) || (msgLen == NULL) || ( seqNo == NULL)) { return ACCESN_ERROR; } /* convert to uSec units */ req.timeOut = timeOut * 1000; req.lastSeq = *seqNo; status = DeviceIoControl(hDevice, IOCTL_IMB_GET_ASYNC_MSG, &req, sizeof(req), &responseData, sizeof(responseData), &respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if (status != TRUE) { DWORD error = GetLastError(); /* handle "msg not available" specially. it is * different from a random old error. */ switch (error) { case IMB_MSG_NOT_AVAILABLE: return ACCESN_END_OF_DATA; break; default: return ACCESN_ERROR; break; } } if (respLength < MIN_ASYNC_RESP_SIZE) { return ACCESN_ERROR; } respLength -= MIN_ASYNC_RESP_SIZE; if (*msgLen < respLength) { return ACCESN_ERROR; } /* same code as in NT section */ if (IpmiVersion == IPMI_09_VERSION) { switch (channelNumber) { case IPMB_CHANNEL: lun = IPMB_LUN; break; case EMP_CHANNEL: lun = EMP_LUN; break; default: lun = RESERVED_LUN; break; } if ((lun == RESERVED_LUN) || (lun != ((((ImbPacket *)(resp->data))->nfLn) & 0x3))) { *seqNo = resp->thisSeq; continue; } memcpy(msgPtr, resp->data, respLength); *msgLen = respLength; } else { if ((sessionHandle ==NULL) || (privilege ==NULL)) { return ACCESN_ERROR; } /* With the new IPMI version the get message command * returns the channel number along with the * privileges.The 1st 4 bits of the second byte of the * response data for get message command represent the * channel number & the last 4 bits are the privileges. */ *privilege = (resp->data[0] & 0xf0)>> 4; if ((resp->data[0] & 0x0f) != (BYTE)channelNumber) { *seqNo = resp->thisSeq; continue; } /* The get message command according to IPMI 1.5 spec * now even returns the session handle.This is required * to be captured as it is required as request data for * send message command. */ *sessionHandle = resp->data[1]; memcpy(msgPtr, &(resp->data[2]), (respLength - 1)); *msgLen = respLength - 1; } /* give the caller his sequence number */ *seqNo = resp->thisSeq; return ACCESN_OK; } } /* IsAsyncMessageAvailable - Waits for an Async Message. This call will block * the calling thread if no Async events are are available in the queue. * * @dummy * @respLength * @status * * returns: OK else error status code */ ACCESN_STATUS IsAsyncMessageAvailable(unsigned int eventId) { int dummy; int respLength = 0; BOOL status; /* confirm that app is not using a bad Id */ if (AsyncEventHandle != (HANDLE)eventId) { return ACCESN_ERROR; } status = DeviceIoControl(hDevice, IOCTL_IMB_CHECK_EVENT, &AsyncEventHandle, sizeof(HANDLE), &dummy, sizeof(int), (LPDWORD)&respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if (status != TRUE) { return ACCESN_ERROR; } return ACCESN_OK; } /* RegisterForImbAsyncMessageNotification - This function Registers the calling * application for Asynchronous notification when a sms message is available * with the IMB driver. * * Notes: The calling application should use the returned handle to * get the Async messages.. * * @handleId - pointer to the registration handle * * returns: OK else error status code */ ACCESN_STATUS RegisterForImbAsyncMessageNotification(unsigned int *handleId) { BOOL status; DWORD respLength ; int dummy; /*allow only one app to register */ if ((handleId == NULL ) || (AsyncEventHandle)) { return ACCESN_ERROR; } status = DeviceIoControl(hDevice, IOCTL_IMB_REGISTER_ASYNC_OBJ, &dummy, sizeof(int), &AsyncEventHandle, (DWORD)sizeof(HANDLE), (LPDWORD)&respLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if ((respLength != sizeof(int)) || (status != TRUE)) { return ACCESN_ERROR; } /* printf("imbapi: Register handle = %x\n",AsyncEventHandle); *//*++++*/ *handleId = (unsigned int)AsyncEventHandle; lprintf(LOG_DEBUG, "handleId = %x AsyncEventHandle %x", *handleId, AsyncEventHandle); return ACCESN_OK; } /* UnRegisterForImbAsyncMessageNotification - This function un-registers the * calling application for Asynchronous notification when a sms message is * available with the IMB driver. It is used by Upper level agents to * un-register for async. notification of sms messages. * * @handleId - pointer to the registration handle * @iFlag - value used to determine where this function was called from. It is * used currently on in NetWare environment. * * returns - status */ ACCESN_STATUS UnRegisterForImbAsyncMessageNotification(unsigned int handleId, int iFlag) { BOOL status; DWORD respLength ; int dummy; /* to keep compiler happy. We are not using this flag*/ iFlag = iFlag; if (AsyncEventHandle != (HANDLE)handleId) { return ACCESN_ERROR; } status = DeviceIoControl(hDevice, IOCTL_IMB_DEREGISTER_ASYNC_OBJ, &AsyncEventHandle, (DWORD)sizeof(HANDLE ), &dummy, (DWORD)sizeof(int), (LPDWORD)&respLength, NULL ); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if (status != TRUE) { return ACCESN_ERROR; } return ACCESN_OK; } /* SetShutDownCode - To set the shutdown action code. * * @code - shutdown action code which can be either SD_NO_ACTION, SD_RESET, * SD_POWER_OFF as defined in imb_if.h * @delayTime - time to delay in 100ms units * * returns - status */ ACCESN_STATUS SetShutDownCode(int delayTime, int code) { DWORD retLength; BOOL status; ShutdownCmdBuffer cmd; /* If IMB interface isn't open, return AccessFailed */ if (hDevice == INVALID_HANDLE_VALUE) { return ACCESN_ERROR; } cmd.code = code; cmd.delayTime = delayTime; status = DeviceIoControl(hDevice, IOCTL_IMB_SHUTDOWN_CODE, &cmd, sizeof(cmd), NULL, 0, &retLength, NULL); lprintf(LOG_DEBUG, "%s: DeviceIoControl status = %d", __FUNCTION__, status); if (status == TRUE) { return ACCESN_OK; } else { return ACCESN_ERROR; } } /*///////////////////////////////////////////////////////////////////////// // MapPhysicalMemory /////////////////////////////////////////////////////////////////////////// */ /*F* // Name: MapPhysicalMemory // Purpose: This function maps specified range of physical memory in calling // pocesse's address space // Context: Used by Upper level agents (sis modules) to access // system physical memory // Returns: ACCESN_OK else error status code // Parameters: // // startAddress starting physical address of the memory to be mapped // addressLength length of the physical memory to be mapped // virtualAddress pointer to the mapped virtual address // Notes: none *F*/ /*/////////////////////////////////////////////////////////////////////////// // UnmapPhysicalMemory //////////////////////////////////////////////////////////////////////////// */ /*F* // Name: UnMapPhysicalMemory // Purpose: This function unmaps the previously mapped physical memory // Context: Used by Upper level agents (sis modules) // Returns: ACCESN_OK else error status code // Parameters: // // addressLength length of the physical memory to be mapped // virtualAddress pointer to the mapped virtual address // Notes: none *F*/ #ifdef WIN32 ACCESN_STATUS MapPhysicalMemory(int startAddress, int addressLength, int *virtualAddress) { DWORD retLength; BOOL status; PHYSICAL_MEMORY_INFO pmi; if (startAddress == 0 || addressLength <= 0) { return ACCESN_OUT_OF_RANGE; } pmi.InterfaceType = Internal; pmi.BusNumber = 0; pmi.BusAddress.HighPart = (LONG)0x0; pmi.BusAddress.LowPart = (LONG)startAddress; pmi.AddressSpace = (LONG)0; pmi.Length = addressLength; status = DeviceIoControl(hDevice, IOCTL_IMB_MAP_MEMORY, &pmi, sizeof(PHYSICAL_MEMORY_INFO), virtualAddress, sizeof(PVOID), &retLength, 0); if (status == TRUE) { return ACCESN_OK; } else { return ACCESN_ERROR; } } ACCESN_STATUS UnmapPhysicalMemory(int virtualAddress, int Length) { DWORD retLength; BOOL status; status = DeviceIoControl(hDevice, IOCTL_IMB_UNMAP_MEMORY, &virtualAddress, sizeof(PVOID), NULL, 0, &retLength, 0); if (status == TRUE) { return ACCESN_OK; } else { return ACCESN_ERROR; } } #else /* Linux, SCO, UNIX, etc. */ ACCESN_STATUS MapPhysicalMemory(int startAddress, int addressLength, int *virtualAddress) { int fd; unsigned int length = addressLength; off_t startpAddress = (off_t)startAddress; unsigned int diff; char *startvAddress; #if defined(PAGESIZE) long int pagesize = PAGESIZE; #elif defined(_SC_PAGESIZE) long int pagesize = sysconf(_SC_PAGESIZE); if (pagesize < 1) { perror("Invalid pagesize"); } #else # error PAGESIZE unsupported #endif if ((startAddress == 0) || (addressLength <= 0)) { return ACCESN_ERROR; } if ((fd = open("/dev/mem", O_RDONLY)) < 0) { char buf[128]; sprintf(buf,"%s %s: open(%s) failed", __FILE__, __FUNCTION__, IMB_DEVICE); perror(buf); return ACCESN_ERROR; } /* aliging the offset to a page boundary and adjusting the length */ diff = (int)startpAddress % pagesize; startpAddress -= diff; length += diff; if ((startvAddress = mmap(0, length, PROT_READ, MAP_SHARED, fd, startpAddress)) == MAP_FAILED) { char buf[128]; sprintf(buf, "%s %s: mmap failed", __FILE__, __FUNCTION__); perror(buf); close(fd); return ACCESN_ERROR; } lprintf(LOG_DEBUG, "%s: mmap of 0x%x success", __FUNCTION__, startpAddress); #ifdef LINUX_DEBUG_MAX for (int i = 0; i < length; i++) { printf("0x%x ", (startvAddress[i])); if(isascii(startvAddress[i])) { printf("%c ", (startvAddress[i])); } } #endif /* LINUX_DEBUG_MAX */ *virtualAddress = (long)(startvAddress + diff); close(fd); return ACCESN_OK; } ACCESN_STATUS UnmapPhysicalMemory(int virtualAddress, int Length) { unsigned int diff = 0; #if defined(PAGESIZE) long int pagesize = PAGESIZE; #elif defined(_SC_PAGESIZE) long int pagesize = sysconf(_SC_PAGESIZE); if (pagesize < 1) { perror("Invalid pagesize"); } #else # error PAGESIZE unsupported #endif /* page align the virtual address and adjust length accordingly */ diff = ((unsigned int)virtualAddress) % pagesize; virtualAddress -= diff; Length += diff; lprintf(LOG_DEBUG, "%s: calling munmap(0x%x,%d)", __FUNCTION__, virtualAddress,Length); if (munmap(&virtualAddress, Length) != 0) { char buf[128]; sprintf(buf, "%s %s: munmap failed", __FILE__, __FUNCTION__); perror(buf); return ACCESN_ERROR; } lprintf(LOG_DEBUG, "%s: munmap(0x%x,%d) success", __FUNCTION__, virtualAddress, Length); return ACCESN_OK; } #endif /* unix */ /* GetIpmiVersion - returns current IPMI version. */ BYTE GetIpmiVersion() { return IpmiVersion; }