common/recipes-core/ipmbd/files/ipmbd.c (740 lines of code) (raw):
/*
* Copyright 2015-present Facebook. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* This module handles all the IPMB communication protocol
* Refer http://www.intel.com/content/www/us/en/servers/ipmi/ipmp-spec-v1-0.html
* for more information.
*
* IPMB packet format is described here for quick reference
* Request:
* <Responder Slave Address(rsSA)>
* <NetFn/ResponderLUN(Netfn/rsLUN)>
* <Header Checksum(hdrCksum)>
* <Requester Slave Address(rqSA)>
* <Requester Sequence Number/RequesterLUN(rqSeq/rqLUN>
* <Command>
* <Data[0..n]>
* <Data Checksum(dataCksum)>
* Response:
* <Requester Slave Address(rqSA)>
* <NetFn/RequesterLUN(Netfn/rqLUN)>
* <Header Checksum(hdrCksum)>
* <Responder Slave Address(rsSA)>
* <Requester Sequence Number/ResponderLUN(rqSeq/rsLUN>
* <Command>
* <Completion Code(CC)>
* <Data[0..n]>
* <Data Checksum(dataCksum)>
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <syslog.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdint.h>
#include <mqueue.h>
#include <semaphore.h>
#include <poll.h>
#include <assert.h>
#include <getopt.h>
#include <stddef.h>
#include <linux/limits.h>
#include <linux/version.h>
#include <openbmc/log.h>
#include <openbmc/obmc-i2c.h>
#include <openbmc/obmc-pal.h>
#include <openbmc/ipc.h>
#include <openbmc/ipmi.h>
#include <openbmc/ipmb.h>
#include <openbmc/misc-utils.h>
#include <openbmc/kv.h>
/*
* IPMB packet sizes.
*/
#define IPMB_PKT_MIN_SIZE 6
#define IPMB_PKT_MAX_SIZE 300
/*
* I2C transaction retry parameters.
*/
#define I2C_RETRIES_MAX 15
#define I2C_RETRY_DELAY 20 /* unit: millisecond */
/*
* Message queue definitions.
*/
#define MQ_DESC_INVALID ((mqd_t)-1)
#define MQ_IPMB_REQ "/mq_ipmb_req"
#define MQ_IPMB_RES "/mq_ipmb_res"
#define MQ_MAX_NUM_MSGS 256
#define MQ_DFT_FLAGS (O_RDONLY | O_CREAT)
#define MQ_DFT_MODES (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define MQ_DFT_ATTR_INITIALIZER { \
.mq_flags = 0, \
.mq_maxmsg = MQ_MAX_NUM_MSGS, \
.mq_msgsize = IPMB_PKT_MAX_SIZE, \
.mq_curmsgs = 0, \
}
#define SEQ_NUM_MAX 64
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(_a) (sizeof(_a) / sizeof((_a)[0]))
#endif /* ARRAY_SIZE */
#define IPMBD_RX_THREAD "rx_handler"
#define IPMBD_REQ_THREAD "req_handler"
#define IPMBD_RES_THREAD "res_handler"
#define IPMBD_SVC_THREAD "svc_handler"
#define __VERBOSE(fmt, args...) \
do { \
if (ipmbd_config.verbose_enabled) \
OBMC_INFO(fmt, ##args); \
} while (0)
#define IPMBD_VERBOSE(fmt, args...) __VERBOSE(fmt, ##args)
#define RX_VERBOSE(fmt, args...) __VERBOSE(IPMBD_RX_THREAD ": " fmt, ##args)
#define REQ_VERBOSE(fmt, args...) __VERBOSE(IPMBD_REQ_THREAD ": " fmt, ##args)
#define RES_VERBOSE(fmt, args...) __VERBOSE(IPMBD_RES_THREAD ": " fmt, ##args)
#define SVC_VERBOSE(fmt, args...) __VERBOSE(IPMBD_SVC_THREAD ": " fmt, ##args)
// Structure for sequence number and buffer
typedef struct {
bool in_use; // seq# is being used
uint8_t len; // buffer size
uint8_t *p_buf; // pointer to buffer
sem_t seq_sem; // semaphore for thread sync.
} seq_buf_t;
// Structure for holding currently used sequence number and
// array of all possible sequence number
static struct {
pthread_mutex_t seq_mutex;
uint8_t curr_seq; // currently used seq#
seq_buf_t seq[SEQ_NUM_MAX]; //array of all possible seq# struct.
} ipmb_seq_buf = {
.seq_mutex = PTHREAD_MUTEX_INITIALIZER,
};
static pthread_mutex_t i2c_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct {
int bus_id;
int payload_id;
/* global flags */
unsigned int bic_update_enabled:1;
unsigned int verbose_enabled:1;
} ipmbd_config = {
.bus_id = -1,
.payload_id = -1,
};
/*
* Name format: ipmbd_<bus-id>. Mainly for logging purpose.
*/
static char ipmbd_name[NAME_MAX];
static void
ipmbd_name_init(int bus_num)
{
snprintf(ipmbd_name, sizeof(ipmbd_name), "ipmbd_%d", bus_num);
}
static char*
ipc_name_gen(char *buf, size_t size, const char *prefix, int bus_num)
{
snprintf(buf, size, "%s_%d", prefix, bus_num);
return buf;
}
// Calculate checksum
static inline uint8_t
calc_cksum(uint8_t *buf, uint8_t len) {
uint8_t i = 0;
uint8_t cksum = 0;
for (i = 0; i < len; i++) {
cksum += buf[i];
}
return (ZERO_CKSUM_CONST - cksum);
}
static void ipmb_seq_buf_init(void) {
int i;
for (i = 0; i < ARRAY_SIZE(ipmb_seq_buf.seq); i++) {
ipmb_seq_buf.seq[i].in_use = false;
assert(sem_init(&ipmb_seq_buf.seq[i].seq_sem, 0, 0) == 0);
ipmb_seq_buf.seq[i].len = 0;
ipmb_seq_buf.seq[i].p_buf = NULL;
}
}
static int seq_put(uint8_t seq, uint8_t *buf, uint8_t len)
{
seq_buf_t *s;
int rc = -1;
if (seq >= ARRAY_SIZE(ipmb_seq_buf.seq)) {
return -1;
}
// Check if the response is being waited for
pthread_mutex_lock(&ipmb_seq_buf.seq_mutex);
s = &ipmb_seq_buf.seq[seq];
if (s->in_use && s->p_buf) {
// Copy the response to the requester's buffer
memcpy(s->p_buf, buf, len);
s->len = len;
// Wake up the worker thread to receive the response
sem_post(&s->seq_sem);
rc = 0;
}
pthread_mutex_unlock(&ipmb_seq_buf.seq_mutex);
return rc;
}
// Returns an unused seq# from all possible seq#
static int8_t
seq_get_new(unsigned char *resp) {
int8_t ret = -1;
uint8_t index;
pthread_mutex_lock(&ipmb_seq_buf.seq_mutex);
// Search for unused sequence number
index = ipmb_seq_buf.curr_seq;
do {
if (!ipmb_seq_buf.seq[index].in_use) {
// Found it!
ret = index;
ipmb_seq_buf.seq[index].in_use = true;
ipmb_seq_buf.seq[index].len = 0;
ipmb_seq_buf.seq[index].p_buf = resp;
break;
}
if (++index == ARRAY_SIZE(ipmb_seq_buf.seq)) {
index = 0;
}
} while (index != ipmb_seq_buf.curr_seq);
// Update the current seq num
if (ret >= 0) {
if (++index == ARRAY_SIZE(ipmb_seq_buf.seq)) {
index = 0;
}
ipmb_seq_buf.curr_seq = index;
}
pthread_mutex_unlock(&ipmb_seq_buf.seq_mutex);
return ret;
}
static int
ipmb_write_satellite(int fd, uint8_t *buf, uint16_t len) {
struct i2c_rdwr_ioctl_data data;
struct i2c_msg msg;
int rc;
int i = 0;
memset(&msg, 0, sizeof(msg));
msg.addr = buf[0] >> 1;
msg.flags = 0;
msg.len = len - 1; // 1st byte in addr
msg.buf = &buf[1];
data.msgs = &msg;
data.nmsgs = 1;
pthread_mutex_lock(&i2c_mutex);
while ((rc = ioctl(fd, I2C_RDWR, &data)) < 0 &&
++i < I2C_RETRIES_MAX) {
msleep(I2C_RETRY_DELAY);
}
if (rc < 0) {
OBMC_ERROR(errno, "Failed to send %u bytes to device @%#x",
len, msg.addr);
}
pthread_mutex_unlock(&i2c_mutex);
return (rc < 0 ? -1 : 0);
}
// Thread to handle new requests
static void*
ipmb_req_handler(void *args) {
int bus_num = *((int*)args);
mqd_t mq;
int i, fd;
int16_t rlen = 0;
uint16_t tlen = 0;
char mq_name_req[NAME_MAX];
uint16_t addr=0;
int ret=0;
char flag_name[NAME_MAX] = {0};
//Buffers for IPMB transport
uint8_t rxbuf[IPMB_PKT_MAX_SIZE] = {0};
uint8_t txbuf[IPMB_PKT_MAX_SIZE] = {0};
ipmb_req_t *p_ipmb_req = (ipmb_req_t*)rxbuf;
ipmb_res_t *p_ipmb_res = (ipmb_res_t*)txbuf;
//Buffers for IPMI Stack
uint8_t rbuf[IPMB_PKT_MAX_SIZE] = {0};
uint8_t tbuf[IPMB_PKT_MAX_SIZE] = {0};
ipmi_mn_req_t *p_ipmi_mn_req = (ipmi_mn_req_t*)rbuf;
ipmi_res_t *p_ipmi_res = (ipmi_res_t*)tbuf;
REQ_VERBOSE("thread starts execution");
// Open Queue to receive requests
ipc_name_gen(mq_name_req, sizeof(mq_name_req), MQ_IPMB_REQ, bus_num);
mq = mq_open(mq_name_req, O_RDONLY);
if (mq == MQ_DESC_INVALID) {
return NULL;
}
ret = pal_get_bmc_ipmb_slave_addr(&addr, bus_num);
if(ret < 0) {
return NULL;
}
#ifdef DEBUG
syslog(LOG_WARNING, "%s ADDR=%x BUS_ID=%x\n", __func__, addr, bus_num);
#endif
// Open the i2c bus for sending response
fd = i2c_cdev_slave_open(bus_num, addr<<1, 0);
if (fd < 0) {
mq_close(mq);
return NULL;
}
REQ_VERBOSE("bic opened successfully, fd=%d", fd);
// set flag to notice BMC ipmbd ipmb_req_handler is ready
snprintf(flag_name, sizeof(flag_name), "flag_ipmbd_req_%d", bus_num);
kv_set(flag_name, "1", 0, 0);
// Loop to process incoming requests
while (1) {
if ((rlen = mq_receive(mq, (char *)rxbuf, IPMB_PKT_MAX_SIZE, NULL)) <= 0) {
sleep(1);
continue;
}
REQ_VERBOSE("received %d bytes from message queue", rlen);
if (ipmbd_config.bic_update_enabled) {
continue;
}
pal_ipmb_processing(bus_num, rxbuf, rlen);
#ifdef DEBUG
syslog(LOG_WARNING, "Received Request of %d bytes\n", rlen);
for (i = 0; i < rlen; i++) {
syslog(LOG_WARNING, "0x%X", rxbuf[i]);
}
#endif
// Create IPMI request from IPMB data
p_ipmi_mn_req->payload_id = (unsigned char)ipmbd_config.payload_id;
p_ipmi_mn_req->netfn_lun = p_ipmb_req->netfn_lun;
p_ipmi_mn_req->cmd = p_ipmb_req->cmd;
if ( rlen > offsetof(ipmb_req_t, data) ) {
// did not copy the checksum
memcpy(p_ipmi_mn_req->data, p_ipmb_req->data, rlen - offsetof(ipmb_req_t, data) - 1 );
}
else {
syslog(LOG_ERR, "ignore malformed (len = %d) ipmb request\n", rlen);
continue;
}
// Send to IPMI stack and get response
// Additional byte as we are adding and passing payload ID for MN support
tlen = 0; // init tlen output incase lib_ipmi_handle did not set this value
lib_ipmi_handle(rbuf, rlen - IPMB_HDR_SIZE + 1, tbuf, &tlen);
// Populate IPMB response data from IPMB request
p_ipmb_res->req_slave_addr = p_ipmb_req->req_slave_addr;
p_ipmb_res->res_slave_addr = p_ipmb_req->res_slave_addr;
p_ipmb_res->cmd = p_ipmb_req->cmd;
p_ipmb_res->seq_lun = p_ipmb_req->seq_lun;
p_ipmb_res->netfn_lun = p_ipmb_req->netfn_lun | (1 << LUN_OFFSET);
// Add IPMI response data
size_t res_data_len = 0;
if (tlen >= offsetof(ipmi_res_t, data)) {
// Add IPMI response data
p_ipmb_res->cc = p_ipmi_res->cc;
res_data_len = tlen - offsetof(ipmi_res_t, data);
memcpy(p_ipmb_res->data, p_ipmi_res->data, res_data_len);
}
else {
// malformed response get from ipmid
syslog(LOG_ERR, "Req( %02X - %02X ) receive malformed (len = %d) response from ipmid\n",
p_ipmb_req->netfn_lun >> 2, p_ipmb_req->cmd, tlen);
p_ipmb_res->cc = CC_NOT_SUPP_IN_CURR_STATE;
}
// Calculate Header Checksum
p_ipmb_res->hdr_cksum = p_ipmb_res->req_slave_addr +
p_ipmb_res->netfn_lun;
p_ipmb_res->hdr_cksum = ZERO_CKSUM_CONST - p_ipmb_res->hdr_cksum;
// Calculate Data Checksum
uint8_t data_chk_sum = p_ipmb_res->res_slave_addr +
p_ipmb_res->seq_lun +
p_ipmb_res->cmd +
p_ipmb_res->cc;
for (i = 0; i < res_data_len; i++) {
data_chk_sum += p_ipmb_res->data[i];
}
p_ipmb_res->data[res_data_len] = ZERO_CKSUM_CONST - data_chk_sum;
// include data-checksum +1
size_t txlen = offsetof(ipmb_res_t, data) + res_data_len + 1;
#ifdef DEBUG
syslog(LOG_WARNING, "Sending Response of %d bytes\n", txlen);
for (i = 0; i < txlen; i++) {
syslog(LOG_WARNING, "0x%X:", txbuf[i]);
}
#endif
// Send response back
ipmb_write_satellite(fd, txbuf, txlen);
pal_ipmb_finished(bus_num, txbuf, txlen);
}
}
// Thread to handle the incoming responses
static void*
ipmb_res_handler(void *args) {
int bus_num = *((int*)args);
uint8_t buf[IPMB_PKT_MAX_SIZE] = { 0 };
int16_t len = 0;
mqd_t mq;
ipmb_res_t *p_res;
uint8_t index;
char mq_name_res[NAME_MAX];
char flag_name[NAME_MAX] = {0};
RES_VERBOSE("thread starts execution");
// Open the message queue
ipc_name_gen(mq_name_res, sizeof(mq_name_res), MQ_IPMB_RES, bus_num);
mq = mq_open(mq_name_res, O_RDONLY);
if (mq == MQ_DESC_INVALID) {
OBMC_ERROR(errno, "failed to open message queue %s", mq_name_res);
return NULL;
}
// set flag to notice BMC ipmbd ipmb_res_handler is ready
snprintf(flag_name, sizeof(flag_name), "flag_ipmbd_res_%d", bus_num);
kv_set(flag_name, "1", 0, 0);
// Loop to wait for incomng response messages
while (1) {
if ((len = mq_receive(mq, (char *)buf, IPMB_PKT_MAX_SIZE, NULL)) <= 0) {
sleep(1);
continue;
}
RES_VERBOSE("received %d bytes from message queue", len);
p_res = (ipmb_res_t *) buf;
// Check the seq# of response
index = p_res->seq_lun >> LUN_OFFSET;
if (seq_put(index, buf, len)) {
// Either the IPMB packet is corrupted or arrived late after client exits
OBMC_WARN("%s: WRONG packet received with seq #%d\n",
IPMBD_RES_THREAD, index);
}
#ifdef DEBUG
syslog(LOG_WARNING, "Received Response of %d bytes\n", len);
int i;
for (i = 0; i < len; i++) {
syslog(LOG_WARNING, "0x%X:", buf[i]);
}
#endif
}
}
/*
* Determine poll() timeout value based on kernel versions:
* - kernel 4.1:
* poll() cannot return when BMC slave buffer is filled with data (due
* to the implementation BMC slave transfer), thus we need a smaller
* timeout (polling every 10 milliseconds) so ipmbd can obtain ipmi
* messages from its peer timely.
* - kernel 5.0 (or higher versions):
* poll() returns when BMC slave buffer is filled with data, so it can
* be a negative value (infinite timeout) technically. Setting it to 3
* seconds is a little conservative, so feel free to adjust the value.
*/
static int
determine_poll_timeout(void)
{
int timeout;
k_version_t cur_ver;
cur_ver = get_kernel_version();
if (cur_ver <= KERNEL_VERSION(4, 1, 51)) {
timeout = 10; /* 10 milliseconds */
} else {
timeout = 3000; /* 3 seconds */
}
return timeout;
}
// Thread to receive the IPMB messages over i2c bus as a slave
static void*
ipmb_rx_handler(void *args) {
i2c_mslave_t *bmc_slave;
mqd_t mq_req = MQ_DESC_INVALID;
mqd_t mq_res = MQ_DESC_INVALID;
struct timespec req = {
.tv_sec = 0,
.tv_nsec = 10000000, //10mSec
};
char mq_name_req[NAME_MAX], mq_name_res[NAME_MAX];
int bus_num = *((int*)args);
uint16_t addr=0;
int ret=0;
int poll_timeout = determine_poll_timeout();
char flag_name[NAME_MAX] = {0};
RX_VERBOSE("thread starts execution");
ret = pal_get_bmc_ipmb_slave_addr(&addr, bus_num);
if (ret < 0) {
return NULL;
}
#ifdef DEBUG
syslog(LOG_WARNING, "%s ADDR=%x BUS_ID=%x\n", __func__, addr, ipmbd_config.bus_id);
#endif
// Open the i2c bus as a slave
bmc_slave = i2c_mslave_open(bus_num, addr);
if (bmc_slave == NULL) {
OBMC_ERROR(errno, "%s: failed to open bmc as slave",
IPMBD_RX_THREAD);
goto cleanup;
}
RX_VERBOSE("opened bmc i2c-%d master as slave successfully",
bus_num);
// Open the message queues for post processing
ipc_name_gen(mq_name_req, sizeof(mq_name_req), MQ_IPMB_REQ, bus_num);
mq_req = mq_open(mq_name_req, O_WRONLY);
if (mq_req == MQ_DESC_INVALID) {
OBMC_ERROR(errno, "%s: failed to open message queue %s",
IPMBD_RX_THREAD, mq_name_req);
goto cleanup;
}
RX_VERBOSE("message queue %s opened", mq_name_req);
ipc_name_gen(mq_name_res, sizeof(mq_name_res), MQ_IPMB_RES, bus_num);
mq_res = mq_open(mq_name_res, O_WRONLY);
if (mq_res == MQ_DESC_INVALID) {
OBMC_ERROR(errno, "%s: failed to open message queue %s",
IPMBD_RX_THREAD, mq_name_res);
goto cleanup;
}
RX_VERBOSE("message queue %s opened", mq_name_res);
// set flag to notice BMC ipmbd ipmb_rx_handler is ready
snprintf(flag_name, sizeof(flag_name), "flag_ipmbd_rx_%d", bus_num);
kv_set(flag_name, "1", 0, 0);
// Loop that retrieves messages
while (1) {
int ret;
ipmb_req_t *p_req;
uint8_t len, tlun, fbyte;
uint8_t buf[IPMB_PKT_MAX_SIZE], tbuf[IPMB_PKT_MAX_SIZE];
// Read messages from i2c driver
ret = i2c_mslave_read(bmc_slave, buf, sizeof(buf));
if (ret <= 0) {
i2c_mslave_poll(bmc_slave, poll_timeout);
continue;
}
len = (uint8_t)ret;
RX_VERBOSE("read %u bytes from ipmb bus %d", len, bus_num);
// TODO: HACK: Due to i2cdriver issues, we are seeing two different type of packet corruptions
// 1. The firstbyte(BMC's slave address) byte is same as second byte
// Workaround: Replace the first byte with correct slave address
// 2. The missing slave address as first byte
// Workaround: move the buffer by one byte and add the correct slave address
// Verify the IPMB hdr cksum: first two bytes are hdr and 3-rd byte cksum
if (len < IPMB_PKT_MIN_SIZE) {
OBMC_WARN("%s: IPMB Packet invalid size %d", IPMBD_RX_THREAD, len);
continue;
}
if (buf[2] != calc_cksum(buf, 2)) {
//handle wrong slave address
if (buf[0] != addr<<1) {
// Store the first byte
fbyte = buf[0];
// Update the first byte with correct slave address
buf[0] = addr<<1;
// Check again if the cksum passes
if (buf[2] != calc_cksum(buf,2)) {
//handle missing slave address
// restore the first byte
buf[0] = fbyte;
//copy the buffer to temporary
memcpy(tbuf, buf, len);
// correct the slave address
buf[0] = addr<<1;
// copy back from temp buffer
memcpy(&buf[1], tbuf, len);
// increase length as we added slave address byte
len++;
// Check if the above hacks corrected the header
if (buf[2] != calc_cksum(buf,2)) {
OBMC_WARN("%s: IPMB Header cksum error after fixup",
IPMBD_RX_THREAD);
continue;
}
}
} else {
OBMC_WARN("%s: IPMB Header cksum does not match", IPMBD_RX_THREAD);
continue;
}
}
// Verify the IPMB data cksum: data starts from 4-th byte
if (buf[len-1] != calc_cksum(&buf[3], len-4)) {
OBMC_WARN("%s: IPMB Data cksum does not match\n", IPMBD_RX_THREAD);
continue;
}
// Check if the messages is request or response
// Even NetFn: Request, Odd NetFn: Response
// Post message to approriate Queue for further processing
p_req = (ipmb_req_t*)buf;
tlun = p_req->netfn_lun >> LUN_OFFSET;
if (tlun % 2) {
RX_VERBOSE("sending packet to %s", mq_name_res);
ret = mq_timedsend(mq_res, (char *)buf, len, 0, &req);
} else {
RX_VERBOSE("sending packet to %s", mq_name_req);
ret = mq_timedsend(mq_req, (char *)buf, len, 0, &req);
}
if (ret != 0) {
//syslog(LOG_WARNING, "mq_send failed for queue %d\n", tmq);
msleep(10);
continue;
}
}
cleanup:
if (bmc_slave != NULL) {
i2c_mslave_close(bmc_slave);
}
if (mq_req != MQ_DESC_INVALID) {
mq_close(mq_req);
}
if (mq_res != MQ_DESC_INVALID) {
mq_close(mq_req);
}
return NULL;
}
/*
* Function to handle all IPMB requests
*/
static void
ipmb_handle (int fd, unsigned char *request, unsigned short req_len,
unsigned char *response, unsigned char *res_len)
{
ipmb_req_t *req = (ipmb_req_t *) request;
int i, ret;
int8_t index;
struct timespec ts;
uint16_t addr=0;
// Allocate right sequence Number
index = seq_get_new(response);
if (index < 0) {
*res_len = 0;
return ;
}
ret = pal_get_bmc_ipmb_slave_addr(&addr, ipmbd_config.bus_id);
if (ret < 0) {
return ;
}
#ifdef DEBUG
syslog(LOG_WARNING, "%s ADDR=%x BUS_ID=%x\n", __func__, addr, ipmbd_config.bus_id);
#endif
req->seq_lun = index << LUN_OFFSET;
req->req_slave_addr = addr << 1;
// Calculate/update header Cksum
req->hdr_cksum = req->res_slave_addr +
req->netfn_lun;
req->hdr_cksum = ZERO_CKSUM_CONST - req->hdr_cksum;
// Calculate/update dataCksum
// Note: dataCkSum byte is last byte
request[req_len-1] = 0;
for (i = IPMB_DATA_OFFSET; i < req_len-1; i++) {
request[req_len-1] += request[i];
}
request[req_len-1] = ZERO_CKSUM_CONST - request[req_len-1];
if (pal_ipmb_processing(ipmbd_config.bus_id, request, req_len)) {
goto ipmb_handle_out;
}
// Send request over i2c bus
if (ipmb_write_satellite(fd, request, req_len)) {
goto ipmb_handle_out;
}
// Wait on semaphore for that sequence Number
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += TIMEOUT_IPMB;
ret = sem_timedwait(&ipmb_seq_buf.seq[index].seq_sem, &ts);
if (ret == -1) {
IPMBD_VERBOSE("No response for sequence number: %d\n", index);
*res_len = 0;
}
ipmb_handle_out:
// Reply to user with data
pthread_mutex_lock(&ipmb_seq_buf.seq_mutex);
*res_len = ipmb_seq_buf.seq[index].len;
ipmb_seq_buf.seq[index].in_use = false;
ipmb_seq_buf.seq[index].p_buf = NULL;
pthread_mutex_unlock(&ipmb_seq_buf.seq_mutex);
pal_ipmb_finished(ipmbd_config.bus_id, request, *res_len);
return;
}
struct ipmb_svc_cookie {
int i2c_fd;
};
static int
conn_handler(client_t *cli) {
struct ipmb_svc_cookie *svc = (struct ipmb_svc_cookie *)cli->svc_cookie;
unsigned char req_buf[MAX_IPMB_REQ_LEN];
unsigned char res_buf[MAX_IPMB_RES_LEN];
size_t req_len = MAX_IPMB_REQ_LEN;
unsigned char res_len=0;
SVC_VERBOSE("entering svc handler");
if (ipc_recv_req(cli, req_buf, &req_len, TIMEOUT_IPMB)) {
OBMC_ERROR(errno, "%s: ipc_recv_req() failed", IPMBD_SVC_THREAD);
return -1;
}
if (req_len == IPMB_PING_LEN) { // get IANA then sends back
res_len = IPMB_PING_LEN;
res_buf[0] = req_buf[0];
res_buf[1] = req_buf[1];
res_buf[2] = req_buf[2];
if (ipc_send_resp(cli, res_buf, res_len) != 0) {
OBMC_ERROR(errno, "%s: ipc_send_resp() failed", IPMBD_SVC_THREAD);
return -1;
}
syslog(LOG_WARNING, "%s IPMB BUS_ID=%x ping scuess\n", __func__,ipmbd_config.bus_id);
return 0;
}
if(ipmbd_config.bic_update_enabled) {
if(!((req_buf[1] == 0xe0) &&
(req_buf[5] == CMD_OEM_1S_ENABLE_BIC_UPDATE))) {
return -1;
}
}
ipmb_handle(svc->i2c_fd, req_buf,
(unsigned int)req_len, res_buf, &res_len);
if(ipc_send_resp(cli, res_buf, res_len) != 0) {
OBMC_ERROR(errno, "%s: ipc_send_resp() failed", IPMBD_SVC_THREAD);
return -1;
}
return 0;
}
// Thread to receive the IPMB lib messages from various apps
static int
start_ipmb_lib_handler(int bus_num) {
char sock_path[64];
struct ipmb_svc_cookie *svc = calloc(1, sizeof(*svc));
if (!svc) {
OBMC_ERROR(errno, "failed to allocate svc cookie");
return -1;
}
// Open the i2c bus for sending request
svc->i2c_fd = i2c_cdev_slave_open(bus_num, BRIDGE_SLAVE_ADDR, 0);
if (svc->i2c_fd < 0) {
free(svc);
return -1;
}
IPMBD_VERBOSE("bic opened successfully, fd=%d", svc->i2c_fd);
ipc_name_gen(sock_path, sizeof(sock_path), SOCK_PATH_IPMB, bus_num);
if (ipc_start_svc(sock_path, conn_handler, SEQ_NUM_MAX, svc, NULL)) {
OBMC_ERROR(errno, "failed to start svc thread");
free(svc);
return -1;
}
return 0;
}
static void
dump_usage(const char *prog_name)
{
int i;
struct {
const char *opt;
const char *desc;
} options[] = {
{"-h|--help", "print this help message"},
{"-v|--verbose", "enable verbose logging"},
{"-u|--enable-bic-update", "enable/allow bic update"},
{NULL, NULL},
};
printf("Usage: %s [options] <bus-id> <payload-id>\n", prog_name);
for (i = 0; options[i].opt != NULL; i++) {
printf(" %-24s - %s\n", options[i].opt, options[i].desc);
}
}
static int
parse_cmdline_args(int argc, char* const argv[])
{
struct option long_opts[] = {
{"help", no_argument, NULL, 'h'},
{"verbose", no_argument, NULL, 'v'},
{"enable-bic-update", no_argument, NULL, 'u'},
{NULL, 0, NULL, 0},
};
while (1) {
int opt_index = 0;
int ret = getopt_long(argc, argv, "hvu", long_opts, &opt_index);
if (ret == -1)
break; /* end of arguments */
switch (ret) {
case 'h':
dump_usage(argv[0]);
exit(0);
case 'v':
ipmbd_config.verbose_enabled = true;
break;
case 'u':
ipmbd_config.bic_update_enabled = true;
break;
default:
return -1;
}
} /* while */
if ((optind + 1) >= argc) {
fprintf(stderr, "Error: <bus-id> and/or <payload-id> is missing!\n\n");
dump_usage(argv[0]);
return -1;
}
ipmbd_config.bus_id = (int)strtoul(argv[optind], NULL, 0);
ipmbd_config.payload_id = (int)strtoul(argv[optind + 1], NULL, 0);
ipmbd_name_init(ipmbd_config.bus_id);
return 0;
}
int
main(int argc, char * const argv[]) {
int i, rc = 0;
mqd_t mqd_req = MQ_DESC_INVALID;
mqd_t mqd_res = MQ_DESC_INVALID;
struct mq_attr attr = MQ_DFT_ATTR_INITIALIZER;
char mq_name_req[NAME_MAX], mq_name_res[NAME_MAX];
struct {
const char *name;
void* (*handler)(void *args);
bool initialized;
pthread_t tid;
} ipmb_threads[3] = {
{
.name = IPMBD_RX_THREAD,
.handler = ipmb_rx_handler,
.initialized = false,
},
{
.name = IPMBD_REQ_THREAD,
.handler = ipmb_req_handler,
.initialized = false,
},
{
.name = IPMBD_RES_THREAD,
.handler = ipmb_res_handler,
.initialized = false,
},
};
/*
* Parse command line arguments.
*/
if (parse_cmdline_args(argc, argv) != 0) {
return -1;
}
/*
* Initializing logging facility.
*/
rc = obmc_log_init(ipmbd_name, LOG_INFO, 0);
if (rc != 0) {
fprintf(stderr, "%s: failed to initialize logger: %s\n",
ipmbd_name, strerror(errno));
return -1;
}
rc = obmc_log_set_syslog(0, LOG_DAEMON);
if (rc != 0) {
fprintf(stderr, "%s: failed to setup syslog: %s\n",
ipmbd_name, strerror(errno));
return -1;
}
obmc_log_unset_std_stream();
if (ipmbd_config.verbose_enabled) {
obmc_log_set_prio(LOG_DEBUG); /* ignore errors */
}
OBMC_INFO("%s started: bus#:%d, payload#:%d",
ipmbd_name, ipmbd_config.bus_id, ipmbd_config.payload_id);
/*
* Initialize message queues.
*/
ipc_name_gen(mq_name_req, sizeof(mq_name_req),
MQ_IPMB_REQ, ipmbd_config.bus_id);
mq_unlink(mq_name_req);
mqd_req = mq_open(mq_name_req, MQ_DFT_FLAGS, MQ_DFT_MODES, &attr);
if (mqd_req == MQ_DESC_INVALID) {
rc = errno;
OBMC_ERROR(rc, "failed to open message queue %s", mq_name_req);
goto cleanup;
}
IPMBD_VERBOSE("message queue %s created", mq_name_req);
ipc_name_gen(mq_name_res, sizeof(mq_name_res),
MQ_IPMB_RES, ipmbd_config.bus_id);
mq_unlink(mq_name_res);
mqd_res = mq_open(mq_name_res, MQ_DFT_FLAGS, MQ_DFT_MODES, &attr);
if (mqd_res == MQ_DESC_INVALID) {
rc = errno;
OBMC_ERROR(rc, "failed to open message queue %s", mq_name_res);
goto cleanup;
}
IPMBD_VERBOSE("message queue %s created", mq_name_res);
ipmb_seq_buf_init();
IPMBD_VERBOSE("sequence buffer initialized");
for (i = 0; i < ARRAY_SIZE(ipmb_threads); i++) {
IPMBD_VERBOSE("creating thread %s", ipmb_threads[i].name);
rc = pthread_create(&ipmb_threads[i].tid, NULL,
ipmb_threads[i].handler, &ipmbd_config.bus_id);
if (rc != 0) {
OBMC_ERROR(rc, "ipmbd: failed to create %s thread: %s\n",
ipmb_threads[i].name, strerror(rc));
goto cleanup;
}
ipmb_threads[i].initialized = true;
}
// Create thread to receive ipmb library requests from apps
if (start_ipmb_lib_handler(ipmbd_config.bus_id) < 0) {
rc = errno;
goto cleanup;
}
IPMBD_VERBOSE("ipmb lib handler started successfully");
cleanup:
for (i = ARRAY_SIZE(ipmb_threads) - 1; i >= 0; i--) {
if (ipmb_threads[i].initialized) {
pthread_join(ipmb_threads[i].tid, NULL);
ipmb_threads[i].initialized = false;
}
}
if (mqd_res != MQ_DESC_INVALID) {
mq_close(mqd_res);
mq_unlink(mq_name_res);
}
if (mqd_req != MQ_DESC_INVALID) {
mq_close(mqd_req);
mq_unlink(mq_name_req);
}
return rc;
}