diff options
-rw-r--r-- | core/ipmi.c | 164 | ||||
-rw-r--r-- | hw/Makefile.inc | 4 | ||||
-rw-r--r-- | hw/bt.c | 48 | ||||
-rw-r--r-- | hw/ipmi/Makefile.inc | 5 | ||||
-rw-r--r-- | hw/ipmi/ipmi-power.c | 40 | ||||
-rw-r--r-- | hw/ipmi/ipmi-rtc.c | 94 | ||||
-rw-r--r-- | include/bt.h | 16 | ||||
-rw-r--r-- | include/ipmi.h | 63 | ||||
-rw-r--r-- | platforms/bmc/palmetto.c | 7 |
9 files changed, 272 insertions, 169 deletions
diff --git a/core/ipmi.c b/core/ipmi.c index 1f9dd5f..90d8aaf 100644 --- a/core/ipmi.c +++ b/core/ipmi.c @@ -22,151 +22,79 @@ #include <time.h> #include <time-utils.h> -/* Sane default (2014/01/01) */ -static time_t time = 1388494800; +static struct ipmi_backend *ipmi_backend = NULL; -static void ipmi_process_storage_resp(struct ipmi_msg *msg) +void ipmi_free_msg(struct ipmi_msg *msg) { - uint32_t new_time; - - switch (msg->cmd) { - case IPMI_GET_SEL_TIME_CMD: - /* - * I couldn't find any mention of endianess in the IPMI spec, - * but ipmitool seemed to assume little endian? - */ - memcpy(&new_time, msg->data, 4); - time = le32_to_cpu(new_time); - break; - - case IPMI_SET_SEL_TIME_CMD: - /* Nothing to do in this case */ - break; - - default: - printf("Unsupported/invalid IPMI storage command\n"); - } + msg->backend->free_msg(msg); } -static int64_t ipmi_get_sel_time(void) +struct ipmi_msg *ipmi_mkmsg_simple(uint32_t code, void *req_data, size_t req_size) { - struct ipmi_msg *msg; - static uint32_t time_result; - - msg = bt_alloc_ipmi_msg(0, 4); - if (!msg) - return OPAL_HARDWARE; - - msg->cmd = IPMI_GET_SEL_TIME_CMD; - msg->netfn = IPMI_NETFN_STORAGE_REQUEST; - - if (bt_add_ipmi_msg_wait(msg)) - return -1; - - memcpy(&time_result, msg->data, sizeof(time_result)); - - return time_result; + return ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, code, ipmi_free_msg, NULL, + req_data, req_size, 0); } -static int64_t ipmi_set_sel_time(uint32_t tv) +struct ipmi_msg *ipmi_mkmsg(int interface, uint32_t code, + void (*complete)(struct ipmi_msg *), + void *user_data, void *req_data, size_t req_size, + size_t resp_size) { struct ipmi_msg *msg; - msg = bt_alloc_ipmi_msg(sizeof(tv), 0); - if (!msg) - return OPAL_HARDWARE; - - msg->cmd = IPMI_SET_SEL_TIME_CMD; - msg->netfn = IPMI_NETFN_STORAGE_REQUEST; - memcpy(msg->data, &tv, sizeof(tv)); + /* We don't actually support multiple interfaces at the moment. */ + assert(interface == IPMI_DEFAULT_INTERFACE); - return bt_add_ipmi_msg_wait(msg); -} + msg = ipmi_backend->alloc_msg(req_size, resp_size); + if (!msg) + return NULL; -static int64_t ipmi_opal_rtc_read(uint32_t *y_m_d, - uint64_t *h_m_s_m) -{ - struct tm tm; + msg->backend = ipmi_backend; + msg->cmd = IPMI_CMD(code); + msg->netfn = IPMI_NETFN(code); + msg->req_size = req_size; + msg->resp_size = resp_size; + msg->complete = complete; + msg->user_data = user_data; - if (ipmi_get_sel_time() < 0) - return OPAL_HARDWARE; + if (req_data) + memcpy(msg->data, req_data, req_size); - gmtime_r(&time, &tm); - tm_to_datetime(&tm, y_m_d, h_m_s_m); - return OPAL_SUCCESS; + return msg; } -static int64_t ipmi_opal_rtc_write(uint32_t year_month_day, - uint64_t hour_minute_second_millisecond) +int ipmi_sync_queue_msg(struct ipmi_msg *msg) { - time_t t; - struct tm tm; + /* Here we could choose which interface to use if we want to support + multiple interfaces. */ - datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm); - t = mktime(&tm); - t = cpu_to_le32(t); - if (ipmi_set_sel_time(t)) - return OPAL_HARDWARE; + /* We should also store the original message cmd/netfn here if we wish + to validate it when we get the response. */ - return OPAL_SUCCESS; + return msg->backend->queue_msg(msg); } -static void ipmi_rtc_init(void) -{ - struct dt_node *np = dt_new(opal_node, "rtc"); - dt_add_property_strings(np, "compatible", "ibm,opal-rtc"); - - opal_register(OPAL_RTC_READ, ipmi_opal_rtc_read, 2); - opal_register(OPAL_RTC_WRITE, ipmi_opal_rtc_write, 2); -} - -static void ipmi_cmd_done(struct ipmi_msg *msg) +void ipmi_cmd_done(struct ipmi_msg *msg) { if (msg->cc != IPMI_CC_NO_ERROR) { prerror("IPMI: Got error response 0x%02x\n", msg->cc); - goto out; - } - - switch (msg->netfn) { - case IPMI_NETFN_STORAGE_RESPONSE: - ipmi_process_storage_resp(msg); - break; - - case IPMI_NETFN_CHASSIS_RESPONSE: - break; - default: - prerror("IPMI: Invalid IPMI function code in response\n"); - } - -out: - bt_free_ipmi_msg(msg); -} - -int64_t ipmi_opal_chassis_control(uint64_t request) -{ - struct ipmi_msg *msg; - uint8_t chassis_control = request; - - msg = bt_alloc_ipmi_msg(sizeof(chassis_control), 0); - if (!msg) - return OPAL_HARDWARE; - if (request > IPMI_CHASSIS_SOFT_SHUTDOWN) - return OPAL_PARAMETER; + if (msg->error) + msg->error(msg); + } else if (msg->complete) + msg->complete(msg); - msg->cmd = IPMI_CHASSIS_CONTROL_CMD; - msg->netfn = IPMI_NETFN_CHASSIS_REQUEST; - msg->data[0] = chassis_control; - - prlog(PR_INFO, "IPMI: sending chassis control request %llu\n", - request); - - return bt_add_ipmi_msg_wait(msg); + /* At this point the message has should have been freed by the + completion functions. */ + msg = NULL; } -void ipmi_init(void) +void ipmi_register_backend(struct ipmi_backend *backend) { - bt_init(ipmi_cmd_done); - - ipmi_rtc_init(); + /* We only support one backend at the moment */ + assert(backend->alloc_msg); + assert(backend->free_msg); + assert(backend->queue_msg); + assert(backend->dequeue_msg); + ipmi_backend = backend; } diff --git a/hw/Makefile.inc b/hw/Makefile.inc index 320e33b..3f372b1 100644 --- a/hw/Makefile.inc +++ b/hw/Makefile.inc @@ -10,6 +10,6 @@ HW=hw/built-in.o include $(SRC)/hw/fsp/Makefile.inc include $(SRC)/hw/ec/Makefile.inc include $(SRC)/hw/ast-bmc/Makefile.inc +include $(SRC)/hw/ipmi/Makefile.inc -$(HW): $(HW_OBJS:%=hw/%) $(FSP) $(EC) $(AST_BMC) - +$(HW): $(HW_OBJS:%=hw/%) $(FSP) $(EC) $(AST_BMC) $(IPMI) @@ -72,7 +72,6 @@ struct bt { enum bt_states state; struct lock lock; struct list_head msgq; - void (*ipmi_cmd_done)(struct ipmi_msg *); }; static struct bt bt; @@ -151,7 +150,7 @@ static bool bt_try_send_msg(void) bt_setmask(BT_CTRL_CLR_WR_PTR, BT_CTRL); /* Byte 1 - Length */ - bt_outb(ipmi_msg->req_data_len + BT_MIN_REQ_LEN, BT_HOST2BMC); + 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); @@ -163,7 +162,7 @@ static bool bt_try_send_msg(void) bt_outb(ipmi_msg->cmd, BT_HOST2BMC); /* Byte 5:N - Data */ - for (i = 0; i < ipmi_msg->req_data_len; i++) + for (i = 0; i < ipmi_msg->req_size; i++) bt_outb(ipmi_msg->data[i], BT_HOST2BMC); bt_setmask(BT_CTRL_H2B_ATN, BT_CTRL); @@ -215,12 +214,12 @@ static bool bt_get_resp(void) * are unsigned we will also trigger this error if * bt_inb(BT_HOST2BMC) < BT_MIN_RESP_LEN (which should never occur). */ - if (resp_len > ipmi_msg->resp_data_len) { + if (resp_len > ipmi_msg->resp_size) { prerror("BT: Invalid resp_len %d for ipmi_msg->cmd = 0x%02x\n", resp_len, ipmi_msg->cmd); - resp_len = ipmi_msg->resp_data_len; + resp_len = ipmi_msg->resp_size; cc = IPMI_ERR_MSG_TRUNCATED; } - ipmi_msg->resp_data_len = resp_len; + ipmi_msg->resp_size = resp_len; /* Byte 2 - NetFn/LUN */ netfn = bt_inb(BT_HOST2BMC); @@ -253,8 +252,7 @@ static bool bt_get_resp(void) /* * Call the IPMI layer to finish processing the message. */ - if (bt.ipmi_cmd_done) - bt.ipmi_cmd_done(ipmi_msg); + ipmi_cmd_done(ipmi_msg); /* Immediately send the next message */ return false; @@ -307,28 +305,29 @@ static bool bt_wait_state(enum bt_states state) } /* - * Allocate a BT-IPMI message and return the IPMI message struct. Allocates - * enough space for the request and response data. + * Allocate an ipmi message and bt container and return the ipmi + * message struct. Allocates enough space for the request and response + * data. */ -struct ipmi_msg *bt_alloc_ipmi_msg(size_t request_size, size_t response_size) +static struct ipmi_msg *bt_alloc_ipmi_msg(size_t request_size, size_t response_size) { struct bt_msg *bt_msg; - bt_msg = malloc(sizeof(struct bt_msg) + MAX(request_size, response_size)); + bt_msg = zalloc(sizeof(struct bt_msg) + MAX(request_size, response_size)); if (!bt_msg) return NULL; - bt_msg->ipmi_msg.req_data_len = request_size; - bt_msg->ipmi_msg.resp_data_len = response_size; + bt_msg->ipmi_msg.req_size = request_size; + bt_msg->ipmi_msg.resp_size = response_size; bt_msg->ipmi_msg.data = (uint8_t *) (bt_msg + 1); return &bt_msg->ipmi_msg; } /* - * Free a previously allocated BT-IPMI message. + * Free a previously allocated ipmi message. */ -void bt_free_ipmi_msg(struct ipmi_msg *ipmi_msg) +static void bt_free_ipmi_msg(struct ipmi_msg *ipmi_msg) { struct bt_msg *bt_msg = container_of(ipmi_msg, struct bt_msg, ipmi_msg); @@ -338,7 +337,7 @@ void bt_free_ipmi_msg(struct ipmi_msg *ipmi_msg) /* * Add an ipmi message to the queue and wait for a response. */ -int bt_add_ipmi_msg_wait(struct ipmi_msg *msg) +static int bt_add_ipmi_msg_wait(struct ipmi_msg *msg) { int ret = 0; @@ -375,16 +374,24 @@ out: * will need to be freed by the caller with bt_free_ipmi_msg() as it will no * longer be in the queue of messages. */ -void bt_del_ipmi_msg(struct ipmi_msg *ipmi_msg) +static int bt_del_ipmi_msg(struct ipmi_msg *ipmi_msg) { struct bt_msg *bt_msg = container_of(ipmi_msg, struct bt_msg, ipmi_msg); lock(&bt.lock); list_del(&bt_msg->link); unlock(&bt.lock); + return 0; } -void bt_init(void (*ipmi_cmd_done)(struct ipmi_msg *)) +struct ipmi_backend bt_backend = { + .alloc_msg = bt_alloc_ipmi_msg, + .free_msg = bt_free_ipmi_msg, + .queue_msg = bt_add_ipmi_msg_wait, + .dequeue_msg = bt_del_ipmi_msg, +}; + +void bt_init(void) { struct dt_node *n; const struct dt_property *prop; @@ -413,6 +420,7 @@ void bt_init(void (*ipmi_cmd_done)(struct ipmi_msg *)) * initialised it. */ bt_set_state(BT_STATE_B_BUSY); - bt.ipmi_cmd_done = ipmi_cmd_done; list_head_init(&bt.msgq); + + ipmi_register_backend(&bt_backend); } diff --git a/hw/ipmi/Makefile.inc b/hw/ipmi/Makefile.inc new file mode 100644 index 0000000..c939f6c --- /dev/null +++ b/hw/ipmi/Makefile.inc @@ -0,0 +1,5 @@ +SUBDIRS += hw/ipmi + +IPMI_OBJS = ipmi-rtc.o ipmi-power.o +IPMI = hw/ipmi/built-in.o +$(IPMI): $(IPMI_OBJS:%=hw/ipmi/%) diff --git a/hw/ipmi/ipmi-power.c b/hw/ipmi/ipmi-power.c new file mode 100644 index 0000000..ac37d14 --- /dev/null +++ b/hw/ipmi/ipmi-power.c @@ -0,0 +1,40 @@ +/* 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 <opal.h> + +int64_t ipmi_opal_chassis_control(uint64_t request) +{ + struct ipmi_msg *msg; + uint8_t chassis_control = request; + + if (chassis_control > IPMI_CHASSIS_SOFT_SHUTDOWN) + return OPAL_PARAMETER; + + + msg = ipmi_mkmsg_simple(IPMI_CHASSIS_CONTROL, &chassis_control, + sizeof(chassis_control)); + if (!msg) + return OPAL_HARDWARE; + + + prlog(PR_INFO, "IPMI: sending chassis control request %llu\n", + request); + + return ipmi_sync_queue_msg(msg); +} diff --git a/hw/ipmi/ipmi-rtc.c b/hw/ipmi/ipmi-rtc.c new file mode 100644 index 0000000..5ddfced --- /dev/null +++ b/hw/ipmi/ipmi-rtc.c @@ -0,0 +1,94 @@ +/* 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 <time.h> +#include <time-utils.h> +#include <device.h> +#include <opal.h> + +/* Sane default (2014/01/01) */ +static time_t time = 1388494800; + +static void get_sel_time_complete(struct ipmi_msg *msg) +{ + uint32_t result; + + memcpy(&result, msg->data, 4); + time = le32_to_cpu(result); +} + + +static int64_t ipmi_get_sel_time(void) +{ + struct ipmi_msg *msg; + + msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_GET_SEL_TIME, + get_sel_time_complete, &time, NULL, 0, 4); + if (!msg) + return OPAL_HARDWARE; + + return ipmi_sync_queue_msg(msg); +} + +static int64_t ipmi_set_sel_time(uint32_t tv) +{ + struct ipmi_msg *msg; + + tv = cpu_to_le32(tv); + msg = ipmi_mkmsg_simple(IPMI_SET_SEL_TIME, &tv, sizeof(tv)); + if (!msg) + return OPAL_HARDWARE; + + return ipmi_sync_queue_msg(msg); +} + +static int64_t ipmi_opal_rtc_read(uint32_t *y_m_d, + uint64_t *h_m_s_m) +{ + struct tm tm; + + if (ipmi_get_sel_time() < 0) + return OPAL_HARDWARE; + + gmtime_r(&time, &tm); + tm_to_datetime(&tm, y_m_d, h_m_s_m); + return OPAL_SUCCESS; +} + +static int64_t ipmi_opal_rtc_write(uint32_t year_month_day, + uint64_t hour_minute_second_millisecond) +{ + time_t t; + struct tm tm; + + datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm); + t = mktime(&tm); + if (ipmi_set_sel_time(t)) + return OPAL_HARDWARE; + + return OPAL_SUCCESS; +} + +void ipmi_rtc_init(void) +{ + struct dt_node *np = dt_new(opal_node, "rtc"); + dt_add_property_strings(np, "compatible", "ibm,opal-rtc"); + + opal_register(OPAL_RTC_READ, ipmi_opal_rtc_read, 2); + opal_register(OPAL_RTC_WRITE, ipmi_opal_rtc_write, 2); +} diff --git a/include/bt.h b/include/bt.h index 5f8a91e..1763d9f 100644 --- a/include/bt.h +++ b/include/bt.h @@ -17,21 +17,7 @@ #ifndef __BT_H #define __BT_H -#include <ipmi.h> - /* Initialise the BT interface */ -void bt_init(void (*ipmi_cmd_done)(struct ipmi_msg *)); - -/* Allocate an BT-IPMI message */ -struct ipmi_msg *bt_alloc_ipmi_msg(size_t request_size, size_t response_size); - -/* Free a BT-IPMI message */ -void bt_free_ipmi_msg(struct ipmi_msg *ipmi_msg); - -/* Add an IPMI message to the BT queue and wait for a resposne */ -int bt_add_ipmi_msg_wait(struct ipmi_msg *msg); - -/* Remove an IPMI message from the BT queue */ -void bt_del_ipmi_msg(struct ipmi_msg *ipmi_msg); +void bt_init(void); #endif diff --git a/include/ipmi.h b/include/ipmi.h index 6181c53..78eb5cd 100644 --- a/include/ipmi.h +++ b/include/ipmi.h @@ -22,8 +22,6 @@ /* * IPMI codes as defined by the standard. */ -#define IPMI_NETFN_APP_REQUEST 0x06 -#define IPMI_NETFN_APP_RESPONSE 0x07 #define IPMI_GET_DEVICE_ID_CMD 0x01 #define IPMI_COLD_RESET_CMD 0x02 #define IPMI_WARM_RESET_CMD 0x03 @@ -54,8 +52,6 @@ #define IPMI_CHASSIS_GET_SYS_BOOT_OPT_CMD 0x0b #define IPMI_CHASSIS_GET_POH_COUNTER_CMD 0x0f -#define IPMI_NETFN_CHASSIS_REQUEST 0x00 -#define IPMI_NETFN_CHASSIS_RESPONSE 0x01 /* 28.3. Chassis Control Command */ #define IPMI_CHASSIS_PWR_DOWN 0x00 @@ -65,11 +61,18 @@ #define IPMI_CHASSIS_PULSE_DIAG 0x04 #define IPMI_CHASSIS_SOFT_SHUTDOWN 0x05 -#define IPMI_NETFN_STORAGE_REQUEST 0x0a -#define IPMI_NETFN_STORAGE_RESPONSE 0x0b -#define IPMI_GET_SEL_INFO_CMD 0x40 -#define IPMI_GET_SEL_TIME_CMD 0x48 -#define IPMI_SET_SEL_TIME_CMD 0x49 +#define IPMI_CODE(netfn, cmd) ((netfn) << 8 | (cmd)) +#define IPMI_CMD(code) ((code) & 0xff) +#define IPMI_NETFN(code) ((code) >> 8 & 0xff) + +#define IPMI_NETFN_CHASSIS 0x00 +#define IPMI_NETFN_STORAGE 0x0a +#define IPMI_NETFN_APP 0x06 + +#define IPMI_GET_SEL_INFO IPMI_CODE(IPMI_NETFN_STORAGE, 0x40) +#define IPMI_GET_SEL_TIME IPMI_CODE(IPMI_NETFN_STORAGE, 0x48) +#define IPMI_SET_SEL_TIME IPMI_CODE(IPMI_NETFN_STORAGE, 0x49) +#define IPMI_CHASSIS_CONTROL IPMI_CODE(IPMI_NETFN_CHASSIS, 0x02) /* * IPMI response codes. @@ -87,19 +90,57 @@ #define IPMI_NAK_ON_WRITE_ERR 0x83 #define IPMI_ERR_UNSPECIFIED 0xff +#define IPMI_DEFAULT_INTERFACE 0 + +struct ipmi_backend; struct ipmi_msg { + struct ipmi_backend *backend; uint8_t netfn; uint8_t cmd; uint8_t cc; - uint8_t req_data_len; - uint8_t resp_data_len; + + /* Called when a response is received to the ipmi message */ + void (*complete)(struct ipmi_msg *); + + /* Called if non-NULL when the ipmi layer detects an error */ + void (*error)(struct ipmi_msg *); + void *user_data; + + uint8_t req_size; + uint8_t resp_size; uint8_t *data; }; +struct ipmi_backend { + 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 *); +}; + /* Initialise the IPMI interface */ void ipmi_init(void); +void ipmi_free_msg(struct ipmi_msg *msg); + +struct ipmi_msg *ipmi_mkmsg_simple(uint32_t code, void *req_data, size_t req_size); +struct ipmi_msg *ipmi_mkmsg(int interface, uint32_t code, + void (*complete)(struct ipmi_msg *), + void *user_data, void *req_data, size_t req_size, + size_t resp_size); + +int ipmi_sync_queue_msg(struct ipmi_msg *msg); + +/* Process a completed message */ +void ipmi_cmd_done(struct ipmi_msg *msg); + /* Change the power state of the P8 */ int64_t ipmi_opal_chassis_control(uint64_t request); +/* Register a backend with the ipmi core. Currently we only support one. */ +void ipmi_register_backend(struct ipmi_backend *backend); + +/* Register rtc ipmi commands with as opal callbacks. */ +void ipmi_rtc_init(void); + #endif diff --git a/platforms/bmc/palmetto.c b/platforms/bmc/palmetto.c index c8ea281..217e542 100644 --- a/platforms/bmc/palmetto.c +++ b/platforms/bmc/palmetto.c @@ -23,6 +23,7 @@ #include <xscom.h> #include <ast.h> #include <ipmi.h> +#include <bt.h> #include "bmc.h" @@ -203,8 +204,9 @@ static bool palmetto_probe(void) /* Setup UART and use it as console with interrupts */ uart_init(true); - /* Setup IPMI */ - ipmi_init(); + /* Register the BT interface with the IPMI layer */ + bt_init(); + ipmi_rtc_init(); return true; } @@ -234,4 +236,3 @@ DECLARE_PLATFORM(palmetto) = { .cec_power_down = palmetto_ipmi_power_down, .cec_reboot = palmetto_ipmi_reboot, }; - |