/* Copyright 2014-2015 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 /* * Under the hood, FSP IPMI component implements the KCS (Keyboard Controller * Style) interface * * KCS interface request message format * * BYTE 1 BYTE 2 BYTE 3:N * ------------------------------------- * | NetFn/LUN | Cmd | Data | * ------------------------------------- * * KCS interface response message format * * BYTE 1 BYTE 2 BYTE 3 BYTE 4:N * ------------------------------------------------ * | NetFn/LUN | Cmd | CompCode | Data | * ------------------------------------------------ */ #define FSP_IPMI_REQ_MIN_LEN 2 /* NetFn + Cmd */ #define FSP_IPMI_RESP_MIN_LEN 3 /* NetFn + Cmd + Completion code */ DEFINE_LOG_ENTRY(OPAL_RC_IPMI_REQ, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_IPMI_RESP, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI, OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_IPMI_DMA_ERROR_RESP, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); struct fsp_ipmi_msg { struct list_node link; struct ipmi_msg ipmi_msg; }; static struct fsp_ipmi { struct list_head msg_queue; void *ipmi_req_buf; void *ipmi_resp_buf; /* There can only be one outstanding request whose reference is stored * in 'cur_msg' and the 'lock' protects against the concurrent updates * of it through request and response. The same 'lock' also protects * the list manipulation. */ struct fsp_ipmi_msg *cur_msg; struct lock lock; } fsp_ipmi; static int fsp_ipmi_send_request(void); static void fsp_ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc) { struct fsp_ipmi_msg *fsp_ipmi_msg = fsp_ipmi.cur_msg; lock(&fsp_ipmi.lock); list_del(&fsp_ipmi_msg->link); fsp_ipmi.cur_msg = NULL; unlock(&fsp_ipmi.lock); ipmi_cmd_done(cmd, netfn, cc, &fsp_ipmi_msg->ipmi_msg); } static void fsp_ipmi_req_complete(struct fsp_msg *msg) { uint8_t status = (msg->resp->word1 >> 8) & 0xff; uint32_t length = msg->resp->data.words[0]; struct fsp_ipmi_msg *fsp_ipmi_msg = msg->user_data; struct ipmi_msg *ipmi_msg; fsp_freemsg(msg); if (status != FSP_STATUS_SUCCESS) { assert(fsp_ipmi_msg == fsp_ipmi.cur_msg); ipmi_msg = &fsp_ipmi_msg->ipmi_msg; if (length != (ipmi_msg->req_size + FSP_IPMI_REQ_MIN_LEN)) prlog(PR_DEBUG, "IPMI: Length mismatch in req completion " "(%d, %d)\n", ipmi_msg->req_size, length); log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Request " "failed with status:0x%02x\n", status); /* FSP will not send the response now, so clear the current * outstanding request */ fsp_ipmi_cmd_done(ipmi_msg->cmd, IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn), IPMI_ERR_UNSPECIFIED); /* Send the next request in the queue */ fsp_ipmi_send_request(); } } static int fsp_ipmi_send_request(void) { uint8_t *req_buf = fsp_ipmi.ipmi_req_buf; struct ipmi_msg *ipmi_msg; struct fsp_msg *msg; int rc; lock(&fsp_ipmi.lock); /* An outstanding request is still pending */ if (fsp_ipmi.cur_msg) { unlock(&fsp_ipmi.lock); return OPAL_SUCCESS; } fsp_ipmi.cur_msg = list_top(&fsp_ipmi.msg_queue, struct fsp_ipmi_msg, link); unlock(&fsp_ipmi.lock); if (!fsp_ipmi.cur_msg) return OPAL_SUCCESS; ipmi_msg = &fsp_ipmi.cur_msg->ipmi_msg; prlog(PR_TRACE, "IPMI: Send request, netfn:0x%02x, cmd:0x%02x, " "req_len:%d\n", ipmi_msg->netfn, ipmi_msg->cmd, ipmi_msg->req_size); /* KCS request message format */ *req_buf++ = ipmi_msg->netfn; /* BYTE 1 */ *req_buf++ = ipmi_msg->cmd; /* BYTE 2 */ if (ipmi_msg->req_size) memcpy(req_buf, ipmi_msg->data, ipmi_msg->req_size); msg = fsp_mkmsg(FSP_CMD_FETCH_PLAT_DATA, 5, 0, PSI_DMA_PLAT_REQ_BUF, 0, PSI_DMA_PLAT_RESP_BUF, ipmi_msg->req_size + FSP_IPMI_REQ_MIN_LEN); if (!msg) { log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Failed to " "allocate request message\n"); fsp_ipmi_cmd_done(ipmi_msg->cmd, IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn), IPMI_ERR_UNSPECIFIED); return OPAL_NO_MEM; } msg->user_data = fsp_ipmi.cur_msg; rc = fsp_queue_msg(msg, fsp_ipmi_req_complete); if (rc) { log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Failed to " "queue request message (%d)\n", rc); fsp_freemsg(msg); fsp_ipmi_cmd_done(ipmi_msg->cmd, IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn), IPMI_ERR_UNSPECIFIED); return OPAL_INTERNAL_ERROR; } return OPAL_SUCCESS; } static struct ipmi_msg *fsp_ipmi_alloc_msg(size_t req_size, size_t resp_size) { struct fsp_ipmi_msg *fsp_ipmi_msg; struct ipmi_msg *ipmi_msg; fsp_ipmi_msg = zalloc(sizeof(*fsp_ipmi_msg) + MAX(req_size, resp_size)); if (!fsp_ipmi_msg) return NULL; ipmi_msg = &fsp_ipmi_msg->ipmi_msg; ipmi_msg->req_size = req_size; ipmi_msg->resp_size = resp_size; ipmi_msg->data = (uint8_t *)(fsp_ipmi_msg + 1); return ipmi_msg; } static void fsp_ipmi_free_msg(struct ipmi_msg *ipmi_msg) { struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg, struct fsp_ipmi_msg, ipmi_msg); free(fsp_ipmi_msg); } static int fsp_ipmi_queue_msg(struct ipmi_msg *ipmi_msg) { struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg, struct fsp_ipmi_msg, ipmi_msg); lock(&fsp_ipmi.lock); list_add_tail(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link); unlock(&fsp_ipmi.lock); return fsp_ipmi_send_request(); } static int fsp_ipmi_queue_msg_head(struct ipmi_msg *ipmi_msg) { struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg, struct fsp_ipmi_msg, ipmi_msg); lock(&fsp_ipmi.lock); list_add(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link); unlock(&fsp_ipmi.lock); return fsp_ipmi_send_request(); } static int fsp_ipmi_dequeue_msg(struct ipmi_msg *ipmi_msg) { struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg, struct fsp_ipmi_msg, ipmi_msg); lock(&fsp_ipmi.lock); list_del_from(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link); unlock(&fsp_ipmi.lock); return 0; } static struct ipmi_backend fsp_ipmi_backend = { .alloc_msg = fsp_ipmi_alloc_msg, .free_msg = fsp_ipmi_free_msg, .queue_msg = fsp_ipmi_queue_msg, .queue_msg_head = fsp_ipmi_queue_msg_head, .dequeue_msg = fsp_ipmi_dequeue_msg, }; static bool fsp_ipmi_send_response(uint32_t cmd) { struct fsp_msg *resp; int rc; resp = fsp_mkmsg(cmd, 0); if (!resp) { log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: Failed to " "allocate response message\n"); return false; } rc = fsp_queue_msg(resp, fsp_freemsg); if (rc) { fsp_freemsg(resp); log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: Failed to " "queue response message\n"); return false; } return true; } static bool fsp_ipmi_read_response(struct fsp_msg *msg) { uint8_t *resp_buf = fsp_ipmi.ipmi_resp_buf; uint32_t status = msg->data.words[3]; uint32_t length = msg->data.words[2]; struct ipmi_msg *ipmi_msg; uint8_t netfn, cmd, cc; assert(fsp_ipmi.cur_msg); ipmi_msg = &fsp_ipmi.cur_msg->ipmi_msg; /* Response TCE token */ assert(msg->data.words[1] == PSI_DMA_PLAT_RESP_BUF); if (status != FSP_STATUS_SUCCESS) { if(status == FSP_STATUS_DMA_ERROR) log_simple_error(&e_info(OPAL_RC_IPMI_DMA_ERROR_RESP), "IPMI: Received " "DMA ERROR response from FSP, this may be due to FSP " "is in termination state:0x%02x\n", status); else log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: FSP response " "received with bad status:0x%02x\n", status); fsp_ipmi_cmd_done(ipmi_msg->cmd, IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn), IPMI_ERR_UNSPECIFIED); return fsp_ipmi_send_response(FSP_RSP_PLAT_DATA | FSP_STATUS_SUCCESS); } /* KCS response message format */ netfn = *resp_buf++; cmd = *resp_buf++; cc = *resp_buf++; length -= FSP_IPMI_RESP_MIN_LEN; prlog(PR_TRACE, "IPMI: fsp response received, netfn:0x%02x, cmd:0x%02x," " cc:0x%02x, length:%d\n", netfn, cmd, cc, length); if (length > ipmi_msg->resp_size) { prlog(PR_DEBUG, "IPMI: Length mismatch in response (%d, %d)\n", length, ipmi_msg->resp_size); length = ipmi_msg->resp_size; /* Truncate */ cc = IPMI_ERR_MSG_TRUNCATED; } ipmi_msg->resp_size = length; if (length) memcpy(ipmi_msg->data, resp_buf, length); fsp_ipmi_cmd_done(cmd, netfn, cc); return fsp_ipmi_send_response(FSP_RSP_PLAT_DATA); } static bool fsp_ipmi_response(uint32_t cmd_sub_mod, struct fsp_msg *msg) { bool rc; switch (cmd_sub_mod) { case FSP_CMD_SEND_PLAT_DATA: prlog(PR_TRACE, "FSP_CMD_SEND_PLAT_DATA command received\n"); rc = fsp_ipmi_read_response(msg); break; default: return false; }; /* If response sent successfully, pick the next request */ if (rc == true) fsp_ipmi_send_request(); return rc; } static struct fsp_client fsp_ipmi_client = { .message = fsp_ipmi_response, }; void fsp_ipmi_init(void) { fsp_tce_map(PSI_DMA_PLAT_REQ_BUF, fsp_ipmi.ipmi_req_buf, PSI_DMA_PLAT_REQ_BUF_SIZE); fsp_tce_map(PSI_DMA_PLAT_RESP_BUF, fsp_ipmi.ipmi_resp_buf, PSI_DMA_PLAT_RESP_BUF_SIZE); list_head_init(&fsp_ipmi.msg_queue); init_lock(&fsp_ipmi.lock); fsp_register_client(&fsp_ipmi_client, FSP_MCLASS_FETCH_SPDATA); ipmi_register_backend(&fsp_ipmi_backend); }