/* 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 #include #include #include #include #include #include #include #include #include #include struct ipmi_backend *ipmi_backend = NULL; static struct lock sync_lock = LOCK_UNLOCKED; static struct ipmi_msg *sync_msg = NULL; void ipmi_free_msg(struct ipmi_msg *msg) { /* ipmi_free_msg frees messages allocated by the * backend. Without a backend we couldn't have allocated * messages to free (we don't support removing backends * yet). */ if (!ipmi_present()) { prerror("IPMI: Trying to free message without backend\n"); return; } msg->backend->free_msg(msg); } void ipmi_init_msg(struct ipmi_msg *msg, int interface, uint32_t code, void (*complete)(struct ipmi_msg *), void *user_data, size_t req_size, size_t resp_size) { /* We don't actually support multiple interfaces at the moment. */ assert(interface == IPMI_DEFAULT_INTERFACE); msg->backend = ipmi_backend; msg->cmd = IPMI_CMD(code); msg->netfn = IPMI_NETFN(code) << 2; msg->req_size = req_size; msg->resp_size = resp_size; msg->complete = complete; msg->user_data = user_data; } struct ipmi_msg *ipmi_mkmsg_simple(uint32_t code, void *req_data, size_t req_size) { return ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, code, ipmi_free_msg, NULL, req_data, req_size, 0); } 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; if (!ipmi_present()) return NULL; msg = ipmi_backend->alloc_msg(req_size, resp_size); if (!msg) return NULL; ipmi_init_msg(msg, interface, code, complete, user_data, req_size, resp_size); /* Commands are free to over ride this if they want to handle errors */ msg->error = ipmi_free_msg; if (req_data) memcpy(msg->data, req_data, req_size); return msg; } int ipmi_queue_msg_head(struct ipmi_msg *msg) { if (!ipmi_present()) return OPAL_HARDWARE; if (!msg) { prerror("%s: Attempting to queue NULL message\n", __func__); return OPAL_PARAMETER; } return msg->backend->queue_msg_head(msg); } int ipmi_queue_msg(struct ipmi_msg *msg) { /* Here we could choose which interface to use if we want to support multiple interfaces. */ if (!ipmi_present()) return OPAL_HARDWARE; if (!msg) { prerror("%s: Attempting to queue NULL message\n", __func__); return OPAL_PARAMETER; } return msg->backend->queue_msg(msg); } int ipmi_dequeue_msg(struct ipmi_msg *msg) { if (!ipmi_present()) return OPAL_HARDWARE; if (!msg) { prerror("%s: Attempting to dequeue NULL message\n", __func__); return OPAL_PARAMETER; } return msg->backend->dequeue_msg(msg); } void ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc, struct ipmi_msg *msg) { msg->cc = cc; if (msg->cmd != cmd) { prerror("IPMI: Incorrect cmd 0x%02x in response\n", cmd); cc = IPMI_ERR_UNSPECIFIED; } if ((msg->netfn >> 2) + 1 != (netfn >> 2)) { prerror("IPMI: Incorrect netfn 0x%02x in response\n", netfn >> 2); cc = IPMI_ERR_UNSPECIFIED; } msg->netfn = netfn; if (cc != IPMI_CC_NO_ERROR) { prlog(PR_DEBUG, "IPMI: Got error response. cmd=0x%x, netfn=0x%x," " rc=0x%02x\n", msg->cmd, msg->netfn >> 2, msg->cc); assert(msg->error); msg->error(msg); } else if (msg->complete) msg->complete(msg); /* At this point the message has should have been freed by the completion functions. */ /* If this is a synchronous message flag that we are done */ if (msg == sync_msg) sync_msg = NULL; } void ipmi_queue_msg_sync(struct ipmi_msg *msg) { if (!ipmi_present()) return; if (!msg) { prerror("%s: Attempting to queue NULL message\n", __func__); return; } lock(&sync_lock); while (sync_msg); sync_msg = msg; if (msg->backend->disable_retry && !opal_booting()) msg->backend->disable_retry(msg); ipmi_queue_msg_head(msg); unlock(&sync_lock); /* * BT response handling relies on a timer. Run timers once in * a while. * * TODO (clg): implement a timeout for IPMI synchronous messages */ while (sync_msg == msg) { check_timers(0); time_wait_ms(10); } } 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); /* Handle power control & PNOR handshake events */ ipmi_parse_sel(msg); ipmi_free_msg(msg); } static void ipmi_get_message_flags_complete(struct ipmi_msg *msg) { uint8_t flags = msg->data[0]; ipmi_free_msg(msg); prlog(PR_DEBUG, "IPMI Get Message Flags: %02x\n", flags); /* Once we see an interrupt we assume the payload has * booted. We disable the wdt and let the OS setup its own * wdt. * * This is also where we consider the OS to be booted, so we set * the boot count sensor */ if (flags & IPMI_MESSAGE_FLAGS_WATCHDOG_PRE_TIMEOUT) { ipmi_wdt_stop(); ipmi_set_boot_count(); } /* Message available in the event buffer? Queue a Read Event command * to retrieve it. The flag is cleared by performing a read */ if (flags & IPMI_MESSAGE_FLAGS_EVENT_BUFFER) { msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_READ_EVENT, ipmi_read_event_complete, NULL, NULL, 0, 16); ipmi_queue_msg(msg); } } void ipmi_sms_attention(void) { struct ipmi_msg *msg; if (!ipmi_present()) return; /* todo: when we handle multiple IPMI interfaces, we'll need to * ensure that this message is associated with the appropriate * backend. */ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_GET_MESSAGE_FLAGS, ipmi_get_message_flags_complete, NULL, NULL, 0, 1); ipmi_queue_msg(msg); } void ipmi_register_backend(struct ipmi_backend *backend) { /* 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; ipmi_backend->opal_event_ipmi_recv = opal_dynamic_event_alloc(); } bool ipmi_present(void) { return ipmi_backend != NULL; }