@@ -108,7 +108,8 @@ static void ipmi_read_event_complete(struct ipmi_msg *msg)
prlog(PR_DEBUG, "IPMI read event %02x complete: %d bytes. cc: %02x\n",
msg->cmd, msg->resp_size, msg->cc);
- /* TODO: Handle power control & PNOR handshake events */
+ /* Handle power control & PNOR handshake events */
+ ipmi_parse_sel(msg);
ipmi_free_msg(msg);
}
@@ -22,6 +22,33 @@
#include <errorlog.h>
#include <pel.h>
+/* OEM SEL fields */
+#define SEL_OEM_ID_0 0x55
+#define SEL_OEM_ID_1 0x55
+#define SEL_RECORD_TYPE_OEM 0xC0
+#define SEL_RECORD_TYPE_EVENT 0x02
+#define SEL_MANUF_0 0x8E
+#define SEL_MANUF_1 0xCB
+#define SEL_MANUF_2 0xC8
+
+#define SEL_NETFN_IBM 0x3a
+
+/* OEM SEL Commands */
+#define CMD_AMI_POWER 0x04
+#define CMD_AMI_PNOR_ACCESS 0x07
+
+struct oem_sel {
+ /* SEL header */
+ uint8_t id[2];
+ uint8_t type;
+ uint8_t manuf_id[3];
+ uint8_t timestamp[4];
+ /* OEM SEL data (6 bytes) follows */
+ uint8_t netfun;
+ uint8_t cmd;
+ uint8_t data[4];
+};
+
/* As far as I can tell the size of PEL record is unbounded (due to
* the possible presence of the user defined section). We chose this
* size because it's what FSP uses, but we could probably reduce
@@ -148,3 +175,60 @@ int ipmi_elog_commit(struct errorlog *elog_buf)
return 0;
}
+
+static void dump_sel(struct oem_sel *sel)
+{
+ const int level = PR_DEBUG;
+
+ prlog(level, "\tid %02x%02x\n", sel->id[0], sel->id[1]);
+ prlog(level, "\ttype %02x\n", sel->type);
+ prlog(level, "\tmanuf %02x %02x %02x\n",
+ sel->manuf_id[0], sel->manuf_id[1], sel->manuf_id[2]);
+ prlog(level, "\ttime %02x %02x %02x %02x\n",
+ sel->timestamp[0], sel->timestamp[1],
+ sel->timestamp[2], sel->timestamp[3]);
+ prlog(level, "\tnetfun %02x\n", sel->netfun);
+ prlog(level, "\tcmd %02x\n", sel->cmd);
+ prlog(level, "\tdata %02x %02x %02x %02x\n", sel->data[0],
+ sel->data[1], sel->data[2], sel->data[3]);
+}
+
+void ipmi_parse_sel(struct ipmi_msg *msg)
+{
+ struct oem_sel sel;
+
+ prlog(PR_INFO, "SEL received (size: %d)\n", msg->resp_size);
+ assert(msg->resp_size <= 16);
+
+ memcpy(&sel, msg->data, msg->resp_size);
+
+ dump_sel(&sel);
+
+ /* We do not process system event records */
+ if (sel.type == SEL_RECORD_TYPE_EVENT) {
+ prlog(PR_INFO, "IPMI: dropping System Event Record SEL\n");
+ return;
+ }
+
+ /* Only accept OEM SEL messages */
+ if (sel.id[0] != SEL_OEM_ID_0 ||
+ sel.id[1] != SEL_OEM_ID_1 ||
+ sel.type != SEL_RECORD_TYPE_OEM ||
+ sel.manuf_id[0] != SEL_MANUF_0 ||
+ sel.manuf_id[1] != SEL_MANUF_1 ||
+ sel.manuf_id[2] != SEL_MANUF_2) {
+ prlog(PR_WARNING, "IPMI: unknown SEL %02x%02x (type %02x)\n",
+ sel.id[0], sel.id[1], sel.type);
+ return;
+ }
+
+ switch (sel.cmd) {
+ case CMD_AMI_POWER:
+ break;
+ case CMD_AMI_PNOR_ACCESS:
+ break;
+ default:
+ printf("IPMI: unknown OEM SEL command %02x received\n",
+ sel.cmd);
+ }
+}
@@ -213,4 +213,7 @@ void ipmi_fru_init(uint8_t fru_dev_id);
struct errorlog;
int ipmi_elog_commit(struct errorlog *elog_buf);
+/* Callback to parse an OEM SEL message */
+void ipmi_parse_sel(struct ipmi_msg *msg);
+
#endif
The read event mechanism is used when the BMC has an asynchronous message for the host. It sets a flag that we read with Get Message Flags, and then we read the message using Read Event. This event message contains a SEL message. There are two OEM SEL messages we expect from the AMI BMC: - graceful power operations. This is when the BMC wants the host to power down or reboot. - PNOR Locking. When the BMC wants to access the PNOR, it requests that the host not touch it. This patch implements the parsing. Signed-off-by: Joel Stanley <joel@jms.id.au> --- core/ipmi.c | 3 +- hw/ipmi/ipmi-sel.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/ipmi.h | 3 ++ 3 files changed, 89 insertions(+), 1 deletion(-)