Skip to content

Commit

Permalink
ipmi: Add an opal interface to the ipmi stack
Browse files Browse the repository at this point in the history
This patch adds two opal calls (opal_ipmi_send and opal_ipmi_recv)
to allow an operating system to send and receive arbitrary ipmi
messages to the BMC.

Signed-off-by: Alistair Popple <alistair@popple.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
  • Loading branch information
apopple authored and ozbenh committed Oct 30, 2014
1 parent b118964 commit b2a374d
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 14 deletions.
8 changes: 5 additions & 3 deletions core/ipmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <opal.h>
#include <device.h>

static struct ipmi_backend *ipmi_backend = NULL;
struct ipmi_backend *ipmi_backend = NULL;

void ipmi_free_msg(struct ipmi_msg *msg)
{
Expand Down Expand Up @@ -49,7 +49,7 @@ struct ipmi_msg *ipmi_mkmsg(int interface, uint32_t code,

msg->backend = ipmi_backend;
msg->cmd = IPMI_CMD(code);
msg->netfn = IPMI_NETFN(code);
msg->netfn = IPMI_NETFN(code) << 2;
msg->req_size = req_size;
msg->resp_size = resp_size;
msg->complete = complete;
Expand Down Expand Up @@ -80,10 +80,11 @@ void ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc, struct ipmi_msg *msg)
cc = IPMI_ERR_UNSPECIFIED;
}

if (msg->netfn + 1 != netfn) {
if ((msg->netfn >> 2) + 1 != (netfn >> 2)) {
prerror("IPMI: Incorrect netfn 0x%02x in response\n", netfn);
cc = IPMI_ERR_UNSPECIFIED;
}
msg->netfn = netfn;

if (cc != IPMI_CC_NO_ERROR) {
prerror("IPMI: Got error response 0x%02x\n", msg->cc);
Expand All @@ -105,6 +106,7 @@ void ipmi_register_backend(struct ipmi_backend *backend)
assert(backend->queue_msg);
assert(backend->dequeue_msg);
ipmi_backend = backend;
ipmi_backend->opal_event_ipmi_recv = opal_dynamic_event_alloc();
}

bool ipmi_present(void)
Expand Down
36 changes: 28 additions & 8 deletions hw/bt.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@
/*
* How long (in TB ticks) before a message is timed out.
*/
#define BT_MSG_TIMEOUT (secs_to_tb(1))
#define BT_MSG_TIMEOUT (secs_to_tb(3))

#define BT_QUEUE_DEBUG 0

enum bt_states {
BT_STATE_IDLE = 0,
Expand All @@ -76,7 +78,6 @@ struct bt_msg {
struct list_node link;
unsigned long tb;
uint8_t seq;
uint8_t lun;
struct ipmi_msg ipmi_msg;
};

Expand Down Expand Up @@ -132,7 +133,6 @@ static int bt_add_ipmi_msg(struct ipmi_msg *ipmi_msg)
{
struct bt_msg *bt_msg = container_of(ipmi_msg, struct bt_msg, ipmi_msg);

bt_msg->lun = 0;
lock(&bt.lock);
bt_msg->tb = mftb();
bt_msg->seq = ipmi_seq++;
Expand Down Expand Up @@ -197,7 +197,7 @@ static bool bt_try_send_msg(void)
bt_outb(ipmi_msg->req_size + BT_MIN_REQ_LEN, BT_HOST2BMC);

/* Byte 2 - NetFn/LUN */
bt_outb((ipmi_msg->netfn << 2) | (bt_msg->lun & 0x3), BT_HOST2BMC);
bt_outb(ipmi_msg->netfn, BT_HOST2BMC);

/* Byte 3 - Seq */
bt_outb(bt_msg->seq, BT_HOST2BMC);
Expand Down Expand Up @@ -290,16 +290,13 @@ static bool bt_get_resp(void)
}
ipmi_msg->resp_size = resp_len;

bt_msg->lun = netfn & 0x3;
netfn = netfn >> 2;

/* Byte 6:N - Data */
for (i = 0; i < resp_len; i++)
ipmi_msg->data[i] = bt_inb(BT_HOST2BMC);
bt_set_h_busy(false);

if (cc != IPMI_CC_NO_ERROR)
prerror("BT: Host error 0x%02x receiving BT/IPMI response\n", cc);
prerror("BT: Host error 0x%02x receiving BT/IPMI response for msg 0x%02x\n", cc, seq);

/* Make sure the other side is idle before we move to the idle state */
bt_set_state(BT_STATE_B_BUSY);
Expand All @@ -310,6 +307,10 @@ static bool bt_get_resp(void)
/*
* Call the IPMI layer to finish processing the message.
*/
#if BT_QUEUE_DEBUG
prlog(PR_DEBUG, "cmd 0x%02x done\n", seq);
#endif

ipmi_cmd_done(cmd, netfn, cc, ipmi_msg);

/* Immediately send the next message */
Expand Down Expand Up @@ -337,6 +338,25 @@ static void bt_poll(void *data __unused)
bool ret = true;

do {

#if BT_QUEUE_DEBUG
struct bt_msg *msg;
static bool printed = false;
lock(&bt.lock);
if (!list_empty(&bt.msgq)) {
printed = false;
prlog(PR_DEBUG, "-------- BT Msg Queue --------\n");
list_for_each(&bt.msgq, msg, link) {
prlog(PR_DEBUG, "Seq: 0x%02x Cmd: 0x%02x\n", msg->seq, msg->ipmi_msg.cmd);
}
prlog(PR_DEBUG, "-----------------------------\n");
} else if (!printed) {
printed = true;
prlog(PR_DEBUG, "----- BT Msg Queue Empty -----\n");
}
unlock(&bt.lock);
#endif

bt_expire_old_msg();

switch(bt.state) {
Expand Down
2 changes: 1 addition & 1 deletion hw/ipmi/Makefile.inc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
SUBDIRS += hw/ipmi

IPMI_OBJS = ipmi-rtc.o ipmi-power.o
IPMI_OBJS = ipmi-rtc.o ipmi-power.o ipmi-opal.o
IPMI = hw/ipmi/built-in.o
$(IPMI): $(IPMI_OBJS:%=hw/ipmi/%)
146 changes: 146 additions & 0 deletions hw/ipmi/ipmi-opal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/* Copyright 2013-2014 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <stdlib.h>
#include <ipmi.h>
#include <lock.h>
#include <opal.h>
#include <device.h>
#include <ccan/list/list.h>

enum {
OPAL_IPMI_MSG_FORMAT_VERSION_1 = 1,
};

struct opal_ipmi_msg {
uint8_t version;
uint8_t netfn;
uint8_t cmd;
uint8_t data[];
};

static struct lock msgq_lock = LOCK_UNLOCKED;
static struct list_head msgq = LIST_HEAD_INIT(msgq);

static void opal_send_complete(struct ipmi_msg *msg)
{
lock(&msgq_lock);
list_add_tail(&msgq, &msg->link);
opal_update_pending_evt(ipmi_backend->opal_event_ipmi_recv,
ipmi_backend->opal_event_ipmi_recv);
unlock(&msgq_lock);
}

static int64_t opal_ipmi_send(uint64_t interface,
struct opal_ipmi_msg *opal_ipmi_msg, uint64_t msg_len)
{
struct ipmi_msg *msg;

if (opal_ipmi_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) {
prerror("OPAL IPMI: Incorrect version\n");
return OPAL_UNSUPPORTED;
}

msg_len -= sizeof(struct opal_ipmi_msg);
if (msg_len > IPMI_MAX_REQ_SIZE) {
prerror("OPAL IPMI: Invalid request length\n");
return OPAL_PARAMETER;
}

prlog(PR_DEBUG, "opal_ipmi_send(cmd: 0x%02x netfn: 0x%02x len: 0x%02llx)\n",
opal_ipmi_msg->cmd, opal_ipmi_msg->netfn >> 2, msg_len);

msg = ipmi_mkmsg(interface,
IPMI_CODE(opal_ipmi_msg->netfn >> 2, opal_ipmi_msg->cmd),
opal_send_complete, NULL, opal_ipmi_msg->data,
msg_len, IPMI_MAX_RESP_SIZE);
if (!msg)
return OPAL_RESOURCE;

msg->complete = opal_send_complete;
msg->error = opal_send_complete;
return ipmi_queue_msg(msg);
}

static int64_t opal_ipmi_recv(uint64_t interface,
struct opal_ipmi_msg *opal_ipmi_msg, uint64_t *msg_len)
{
struct ipmi_msg *msg;
int64_t rc;

lock(&msgq_lock);
msg = list_top(&msgq, struct ipmi_msg, link);

if (!msg) {
rc = OPAL_EMPTY;
goto out_unlock;
}

if (opal_ipmi_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) {
prerror("OPAL IPMI: Incorrect version\n");
rc = OPAL_UNSUPPORTED;
goto out_unlock;
}

if (interface != IPMI_DEFAULT_INTERFACE) {
prerror("IPMI: Invalid interface 0x%llx in opal_ipmi_recv\n", interface);
rc = OPAL_EMPTY;
goto out_unlock;
}

if (*msg_len - sizeof(struct opal_ipmi_msg) < msg->resp_size + 1) {
rc = OPAL_RESOURCE;
goto out_unlock;
}

list_del(&msg->link);
if (list_empty(&msgq))
opal_update_pending_evt(ipmi_backend->opal_event_ipmi_recv, 0);
unlock(&msgq_lock);

opal_ipmi_msg->cmd = msg->cmd;
opal_ipmi_msg->netfn = msg->netfn;
opal_ipmi_msg->data[0] = msg->cc;
memcpy(&opal_ipmi_msg->data[1], msg->data, msg->resp_size);

prlog(PR_DEBUG, "opal_ipmi_recv(cmd: 0x%02x netfn: 0x%02x resp_size: 0x%02x)\n",
msg->cmd, msg->netfn >> 2, msg->resp_size);

/* Add one as the completion code is returned in the message data */
*msg_len = msg->resp_size + sizeof(struct opal_ipmi_msg) + 1;
ipmi_free_msg(msg);

return OPAL_SUCCESS;

out_unlock:
unlock(&msgq_lock);
return rc;
}

void ipmi_opal_init(void)
{
struct dt_node *opal_ipmi;

opal_ipmi = dt_new(opal_node, "ipmi");
dt_add_property_strings(opal_ipmi, "compatible", "ibm,opal-ipmi");
dt_add_property_cells(opal_ipmi, "ibm,ipmi-interface-id",
IPMI_DEFAULT_INTERFACE);
dt_add_property_cells(opal_ipmi, "interrupts",
ilog2(ipmi_backend->opal_event_ipmi_recv));

opal_register(OPAL_IPMI_SEND, opal_ipmi_send, 3);
opal_register(OPAL_IPMI_RECV, opal_ipmi_recv, 3);
}
13 changes: 13 additions & 0 deletions include/ipmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define __IPMI_H

#include <stdint.h>
#include <ccan/list/list.h>
#include <stdbool.h>

/*
Expand Down Expand Up @@ -108,8 +109,14 @@

#define IPMI_DEFAULT_INTERFACE 0

#define IPMI_MAX_REQ_SIZE 64
#define IPMI_MAX_RESP_SIZE 64

struct ipmi_backend;
struct ipmi_msg {
/* Can be used by command implementations to track requests */
struct list_node link;

struct ipmi_backend *backend;
uint8_t netfn;
uint8_t cmd;
Expand All @@ -128,12 +135,15 @@ struct ipmi_msg {
};

struct ipmi_backend {
uint64_t opal_event_ipmi_recv;
struct ipmi_msg *(*alloc_msg)(size_t, size_t);
void (*free_msg)(struct ipmi_msg *);
int (*queue_msg)(struct ipmi_msg *);
int (*dequeue_msg)(struct ipmi_msg *);
};

extern struct ipmi_backend *ipmi_backend;

/* Initialise the IPMI interface */
void ipmi_init(void);

Expand Down Expand Up @@ -166,4 +176,7 @@ void ipmi_register_backend(struct ipmi_backend *backend);
/* Register rtc ipmi commands with as opal callbacks. */
void ipmi_rtc_init(void);

/* Register ipmi host interface access callbacks */
void ipmi_opal_init(void);

#endif
5 changes: 4 additions & 1 deletion include/opal.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#define OPAL_HARDWARE_FROZEN -13
#define OPAL_WRONG_STATE -14
#define OPAL_ASYNC_COMPLETION -15
#define OPAL_EMPTY -16

/* API Tokens (in r0) */
#define OPAL_TEST 0
Expand Down Expand Up @@ -142,7 +143,9 @@
#define OPAL_READ_TPO 104
#define OPAL_GET_DPO_STATUS 105
#define OPAL_I2C_REQUEST 106
#define OPAL_LAST 106
#define OPAL_IPMI_SEND 107
#define OPAL_IPMI_RECV 108
#define OPAL_LAST 108

#ifndef __ASSEMBLY__

Expand Down
2 changes: 1 addition & 1 deletion platforms/astbmc/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void astbmc_init(void)
/* Register the BT interface with the IPMI layer */
bt_init();
ipmi_rtc_init();
ipmi_opal_init();

/* As soon as IPMI is up, inform BMC we are in "S0" */
ipmi_set_power_state(IPMI_PWR_SYS_S0_WORKING, IPMI_PWR_NOCHANGE);
Expand Down Expand Up @@ -228,4 +229,3 @@ void astbmc_early_init(void)
/* Setup UART and use it as console with interrupts */
uart_init(true);
}

0 comments on commit b2a374d

Please sign in to comment.