[Skiboot] [RFC PATCH 5/6] libflash: Add ipmi-flash implementation
Andrew Jeffery
andrew at aj.id.au
Thu Sep 6 16:40:14 AEST 2018
ipmi-flash adapts the "mbox" protocol to use IPMI as a transport. The
name "mbox" for the protocol is now meaningless - the ASPEED BMC mailbox
is no-longer relevant when using IPMI as a transport.
The same commands and events are used, though the implementation assumes
v2 of the protocol is supported by the BMC
Signed-off-by: Andrew Jeffery <andrew at aj.id.au>
---
include/platform.h | 1 +
libflash/Makefile.inc | 2 +-
libflash/ipmi-flash.c | 792 ++++++++++++++++++++++++++++++++++++++
libflash/ipmi-flash.h | 29 ++
platforms/astbmc/common.c | 1 +
5 files changed, 824 insertions(+), 1 deletion(-)
create mode 100644 libflash/ipmi-flash.c
create mode 100644 libflash/ipmi-flash.h
diff --git a/include/platform.h b/include/platform.h
index 1a35a86a6f8b..edcbeac9a2cf 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -42,6 +42,7 @@ struct bmc_platform {
*/
uint32_t ipmi_oem_partial_add_esel;
uint32_t ipmi_oem_pnor_access_status;
+ uint32_t ipmi_oem_hiomap_cmd;
};
/* OpenCAPI platform-specific I2C information */
diff --git a/libflash/Makefile.inc b/libflash/Makefile.inc
index 2474abfccc61..c384c4e91df9 100644
--- a/libflash/Makefile.inc
+++ b/libflash/Makefile.inc
@@ -1,4 +1,4 @@
-LIBFLASH_SRCS = libflash.c libffs.c ecc.c blocklevel.c mbox-flash.c
+LIBFLASH_SRCS = libflash.c libffs.c ecc.c blocklevel.c mbox-flash.c ipmi-flash.c
LIBFLASH_OBJS = $(LIBFLASH_SRCS:%.c=%.o)
SUBDIRS += libflash
diff --git a/libflash/ipmi-flash.c b/libflash/ipmi-flash.c
new file mode 100644
index 000000000000..02e4d817791d
--- /dev/null
+++ b/libflash/ipmi-flash.c
@@ -0,0 +1,792 @@
+/* Copyright 2018 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.
+ */
+
+#define pr_fmt(fmt) "IPMI-FLASH: " fmt
+
+#include <inttypes.h>
+#include <lock.h>
+#include <lpc.h>
+#include <lpc-mbox.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <ipmi.h>
+
+#include <ccan/container_of/container_of.h>
+
+#include "errors.h"
+#include "ipmi-flash.h"
+
+#define CMD_OP_HIOMAP_EVENT 0x0f
+
+struct lpc_window {
+ uint32_t lpc_addr; /* Offset into LPC space */
+ uint32_t cur_pos; /* Current position of the window in the flash */
+ uint32_t size; /* Size of the window into the flash */
+ bool open;
+};
+
+struct ipmi_flash_data {
+ uint8_t seq;
+ uint8_t version;
+ uint8_t block_size_shift;
+ uint16_t timeout;
+ struct blocklevel_device bl;
+ struct lpc_window read;
+ struct lpc_window write;
+ uint32_t total_size;
+ uint32_t erase_granule;
+
+ struct lock lock;
+ bool update;
+ uint8_t status;
+};
+
+struct ipmi_hiomap_result {
+ struct ipmi_flash_data *ctx;
+ int16_t cc;
+};
+
+#define RESULT_INIT(_name, _ctx) struct ipmi_hiomap_result _name = { _ctx, -1 }
+
+static uint16_t msg_get_u16(void *src)
+{
+ uint16_t arg;
+
+ memcpy(&arg, src, sizeof(arg));
+
+ return le16_to_cpu(arg);
+}
+
+static void msg_put_u16(void *dst, uint16_t val)
+{
+ val = cpu_to_le16(val);
+
+ memcpy(dst, &val, sizeof(val));
+}
+
+static uint32_t blocks_to_bytes(struct ipmi_flash_data *ctx, uint16_t blocks)
+{
+ return blocks << ctx->block_size_shift;
+}
+
+static uint16_t bytes_to_blocks(struct ipmi_flash_data *ctx, uint32_t bytes)
+{
+ return bytes >> ctx->block_size_shift;
+}
+
+#define HIOMAP_C_GET_INFO MBOX_C_GET_MBOX_INFO
+#define HIOMAP_C_GET_FLASH_INFO MBOX_C_GET_FLASH_INFO
+#define HIOMAP_C_CREATE_READ_WINDOW MBOX_C_CREATE_READ_WINDOW
+#define HIOMAP_C_CREATE_WRITE_WINDOW MBOX_C_CREATE_WRITE_WINDOW
+#define HIOMAP_C_CLOSE_WINDOW MBOX_C_CLOSE_WINDOW
+#define HIOMAP_C_MARK_DIRTY MBOX_C_MARK_WRITE_DIRTY
+#define HIOMAP_C_FLUSH MBOX_C_WRITE_FLUSH
+#define HIOMAP_C_ACK MBOX_C_BMC_EVENT_ACK
+#define HIOMAP_C_ERASE MBOX_C_MARK_WRITE_ERASED
+
+#define HIOMAP_E_ACK_MASK MBOX_ATTN_ACK_MASK
+#define HIOMAP_E_BMC_REBOOT MBOX_ATTN_BMC_REBOOT
+#define HIOMAP_E_WINDOW_RESET MBOX_ATTN_BMC_WINDOW_RESET
+#define HIOMAP_E_FLASH_LOST MBOX_ATTN_BMC_FLASH_LOST
+#define HIOMAP_E_DAEMON_READY MBOX_ATTN_BMC_DAEMON_READY
+
+/* Is the current window able perform the complete operation */
+static bool hiomap_window_valid(struct lpc_window *win, uint64_t pos,
+ uint64_t len)
+{
+ if (!win->open)
+ return false;
+ if (pos < win->cur_pos) /* start */
+ return false;
+ if ((pos + len) > (win->cur_pos + win->size)) /* end */
+ return false;
+ return true;
+}
+
+
+static void ipmi_flash_cmd_cb(struct ipmi_msg *msg)
+{
+ struct ipmi_hiomap_result *res = msg->user_data;
+ struct ipmi_flash_data *ctx = res->ctx;
+
+ res->cc = msg->cc;
+ if (msg->cc != IPMI_CC_NO_ERROR) {
+ return;
+ }
+
+ /* We at least need the command and sequence */
+ if (msg->resp_size < 2) {
+ prerror("Illegal response size: %u\n", msg->resp_size);
+ abort();
+ }
+
+ switch (msg->data[0]) {
+ case HIOMAP_C_GET_INFO:
+ if (msg->resp_size != 6) {
+ prerror("%u: Unexpected response size: %u\n", msg->data[0],
+ msg->resp_size);
+ abort();
+ }
+ ctx->version = msg->data[2];
+ if (ctx->version < 2) {
+ prerror("Failed to negotiate protocol v2 or higher: %d\n",
+ ctx->version);
+ abort();
+ }
+ ctx->block_size_shift = msg->data[3];
+ ctx->timeout = msg_get_u16(&msg->data[4]);
+ break;
+ case HIOMAP_C_GET_FLASH_INFO:
+ if (msg->resp_size != 6) {
+ prerror("%u: Unexpected response size: %u\n", msg->data[0],
+ msg->resp_size);
+ abort();
+ }
+ ctx->total_size = blocks_to_bytes(ctx, msg_get_u16(&msg->data[2]));
+ ctx->erase_granule = blocks_to_bytes(ctx, msg_get_u16(&msg->data[4]));
+ break;
+ case HIOMAP_C_CREATE_READ_WINDOW:
+ if (msg->resp_size != 8) {
+ prerror("%u: Unexpected response size: %u\n", msg->data[0],
+ msg->resp_size);
+ abort();
+ }
+ ctx->read.lpc_addr = blocks_to_bytes(ctx, msg_get_u16(&msg->data[2]));
+ ctx->read.size = blocks_to_bytes(ctx, msg_get_u16(&msg->data[4]));
+ ctx->read.cur_pos = blocks_to_bytes(ctx, msg_get_u16(&msg->data[6]));
+ ctx->read.open = true;
+ ctx->write.open = false;
+ break;
+ case HIOMAP_C_CREATE_WRITE_WINDOW:
+ if (msg->resp_size != 8) {
+ prerror("%u: Unexpected response size: %u\n", msg->data[0],
+ msg->resp_size);
+ abort();
+ }
+ ctx->write.lpc_addr = blocks_to_bytes(ctx, msg_get_u16(&msg->data[2]));
+ ctx->write.size = blocks_to_bytes(ctx, msg_get_u16(&msg->data[4]));
+ ctx->write.cur_pos = blocks_to_bytes(ctx, msg_get_u16(&msg->data[6]));
+ ctx->write.open = true;
+ ctx->read.open = false;
+ break;
+ case HIOMAP_C_CLOSE_WINDOW:
+ ctx->write.open = false;
+ ctx->read.open = false;
+ break;
+ case HIOMAP_C_MARK_DIRTY:
+ case HIOMAP_C_FLUSH:
+ case HIOMAP_C_ACK:
+ case HIOMAP_C_ERASE:
+ break;
+ default:
+ prlog(PR_WARNING, "Unimplemented command handler: %u\n",
+ msg->data[0]);
+ break;
+ };
+}
+
+static bool hiomap_get_info(struct ipmi_flash_data *ctx)
+{
+ RESULT_INIT(res, ctx);
+ unsigned char req[3];
+ struct ipmi_msg *msg;
+
+ ctx->status = 0;
+
+ /* Negotiate protocol version 2 */
+ req[0] = HIOMAP_C_GET_INFO;
+ req[1] = ctx->seq++;
+ req[2] = 2;
+
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ bmc_platform->ipmi_oem_hiomap_cmd, ipmi_flash_cmd_cb,
+ &res, req, sizeof(req), 6);
+ ipmi_queue_msg_sync(msg);
+
+ if (res.cc != IPMI_CC_NO_ERROR) {
+ prerror("%s failed: %d\n", __func__, res.cc);
+ return false;
+ }
+
+ lock(&ctx->lock);
+ ctx->status |= HIOMAP_E_DAEMON_READY;
+ unlock(&ctx->lock);
+
+ return true;
+}
+
+static bool hiomap_get_flash_info(struct ipmi_flash_data *ctx)
+{
+ RESULT_INIT(res, ctx);
+ unsigned char req[2];
+ struct ipmi_msg *msg;
+
+ req[0] = HIOMAP_C_GET_FLASH_INFO;
+ req[1] = ctx->seq++;
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ bmc_platform->ipmi_oem_hiomap_cmd, ipmi_flash_cmd_cb,
+ &res, req, sizeof(req), 2 + 2 + 2);
+ ipmi_queue_msg_sync(msg);
+
+ if (res.cc != IPMI_CC_NO_ERROR) {
+ prerror("%s failed: %d\n", __func__, res.cc);
+ return false;
+ }
+
+ return true;
+}
+
+static bool hiomap_window_move(struct ipmi_flash_data *ctx,
+ struct lpc_window *win, uint8_t command,
+ uint64_t pos, uint64_t len, uint64_t *size)
+{
+ struct lpc_window *lwin;
+ RESULT_INIT(res, ctx);
+ unsigned char req[6];
+ struct ipmi_msg *msg;
+ bool is_read;
+
+ if (hiomap_window_valid(win, pos, len)) {
+ *size = len;
+ return true;
+ }
+
+ req[0] = command;
+ req[1] = ctx->seq++;
+ msg_put_u16(&req[2], bytes_to_blocks(ctx, pos));
+ msg_put_u16(&req[4], bytes_to_blocks(ctx, len));
+
+ ctx->read.open = false;
+ ctx->write.open = false;
+
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ bmc_platform->ipmi_oem_hiomap_cmd, ipmi_flash_cmd_cb,
+ &res, req, sizeof(req), 2 + 2 + 2 + 2);
+ ipmi_queue_msg_sync(msg);
+
+ if (res.cc != IPMI_CC_NO_ERROR) {
+ prerror("%s failed: %d\n", __func__, res.cc);
+ return false;
+ }
+
+ *size = len;
+ /* Is length past the end of the window? */
+ if ((pos + len) > (win->cur_pos + win->size))
+ /* Adjust size to meet current window */
+ *size = (win->cur_pos + win->size) - pos;
+
+ if (len != 0 && *size == 0) {
+ prerror("Invalid window properties: len: %llu, size: %llu\n",
+ len, *size);
+ abort();
+ }
+
+ is_read = command == HIOMAP_C_CREATE_READ_WINDOW;
+ lwin = is_read ? &ctx->read : &ctx->write;
+ prlog(PR_DEBUG, "Opened %s window from 0x%x for %u bytes at 0x%x\n",
+ is_read ? "read" : "write", lwin->cur_pos, lwin->size,
+ lwin->lpc_addr);
+
+ return true;
+}
+
+static bool hiomap_mark_dirty(struct ipmi_flash_data *ctx, uint64_t offset,
+ uint64_t size)
+{
+ RESULT_INIT(res, ctx);
+ unsigned char req[6];
+ struct ipmi_msg *msg;
+
+ assert(ctx->write.open);
+
+ req[0] = HIOMAP_C_MARK_DIRTY;
+ req[1] = ctx->seq++;
+ msg_put_u16(&req[2], bytes_to_blocks(ctx, offset - ctx->write.cur_pos));
+ msg_put_u16(&req[4], bytes_to_blocks(ctx, size));
+
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ bmc_platform->ipmi_oem_hiomap_cmd, ipmi_flash_cmd_cb,
+ &res, req, sizeof(req), 2);
+ ipmi_queue_msg_sync(msg);
+
+ if (res.cc != IPMI_CC_NO_ERROR) {
+ prerror("%s failed: %d\n", __func__, res.cc);
+ return false;
+ }
+
+ prlog(PR_DEBUG, "Marked flash dirty at 0x%" PRIx64 " for %" PRIu64 "\n",
+ offset, size);
+
+ return true;
+}
+
+static bool hiomap_flush(struct ipmi_flash_data *ctx)
+{
+ RESULT_INIT(res, ctx);
+ unsigned char req[2];
+ struct ipmi_msg *msg;
+
+ assert(ctx->write.open);
+
+ req[0] = HIOMAP_C_FLUSH;
+ req[1] = ctx->seq++;
+
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ bmc_platform->ipmi_oem_hiomap_cmd, ipmi_flash_cmd_cb,
+ &res, req, sizeof(req), 2);
+ ipmi_queue_msg_sync(msg);
+
+ if (res.cc != IPMI_CC_NO_ERROR) {
+ prerror("%s failed: %d\n", __func__, res.cc);
+ return false;
+ }
+
+ prlog(PR_DEBUG, "Flushed writes");
+
+ return true;
+}
+
+static bool hiomap_ack(struct ipmi_flash_data *ctx, uint8_t ack)
+{
+ RESULT_INIT(res, ctx);
+ unsigned char req[3];
+ struct ipmi_msg *msg;
+
+ req[0] = HIOMAP_C_ACK;
+ req[1] = ctx->seq++;
+ req[2] = ack;
+
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ bmc_platform->ipmi_oem_hiomap_cmd, ipmi_flash_cmd_cb,
+ &res, req, sizeof(req), 2);
+ ipmi_queue_msg_sync(msg);
+
+ if (res.cc != IPMI_CC_NO_ERROR) {
+ prerror("%s failed: %d\n", __func__, res.cc);
+ return false;
+ }
+
+ prlog(PR_DEBUG, "Acked events: 0x%x\n", ack);
+
+ return true;
+}
+
+static bool hiomap_erase(struct ipmi_flash_data *ctx, uint64_t offset,
+ uint64_t size)
+{
+ RESULT_INIT(res, ctx);
+ unsigned char req[6];
+ struct ipmi_msg *msg;
+
+ assert(ctx->write.open);
+
+ req[0] = HIOMAP_C_ERASE;
+ req[1] = ctx->seq++;
+ msg_put_u16(&req[2], bytes_to_blocks(ctx, offset - ctx->write.cur_pos));
+ msg_put_u16(&req[4], bytes_to_blocks(ctx, size));
+
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE,
+ bmc_platform->ipmi_oem_hiomap_cmd, ipmi_flash_cmd_cb,
+ &res, req, sizeof(req), 2);
+ ipmi_queue_msg_sync(msg);
+
+ if (res.cc != IPMI_CC_NO_ERROR) {
+ prerror("%s failed: %d\n", __func__, res.cc);
+ return false;
+ }
+
+ prlog(PR_DEBUG, "Erased flash at 0x%" PRIx64 " for %" PRIu64 "\n",
+ offset, size);
+
+ return true;
+}
+
+static void hiomap_event(uint8_t events, void *context)
+{
+ struct ipmi_flash_data *ctx = context;
+
+ lock(&ctx->lock);
+ ctx->status = events;
+ ctx->update = true;
+ unlock(&ctx->lock);
+
+ /* FIXME: mutual exclusion */
+ if (events & (HIOMAP_E_BMC_REBOOT | HIOMAP_E_WINDOW_RESET)) {
+ ctx->read.open = false;
+ ctx->write.open = false;
+ }
+}
+
+static int lpc_window_read(struct ipmi_flash_data *ctx, uint32_t pos,
+ void *buf, uint32_t len)
+{
+ uint32_t off = ctx->read.lpc_addr + (pos - ctx->read.cur_pos);
+ int rc;
+
+ prlog(PR_TRACE, "Reading at 0x%08x for 0x%08x offset: 0x%08x\n",
+ pos, len, off);
+
+ while(len) {
+ uint32_t chunk;
+ uint32_t dat;
+
+ /* XXX: make this read until it's aligned */
+ if (len > 3 && !(off & 3)) {
+ rc = lpc_read(OPAL_LPC_FW, off, &dat, 4);
+ if (!rc)
+ *(uint32_t *)buf = dat;
+ chunk = 4;
+ } else {
+ rc = lpc_read(OPAL_LPC_FW, off, &dat, 1);
+ if (!rc)
+ *(uint8_t *)buf = dat;
+ chunk = 1;
+ }
+ if (rc) {
+ prlog(PR_ERR, "lpc_read failure %d to FW 0x%08x\n", rc, off);
+ return rc;
+ }
+ len -= chunk;
+ off += chunk;
+ buf += chunk;
+ }
+
+ return 0;
+}
+
+static int lpc_window_write(struct ipmi_flash_data *ctx, uint32_t pos,
+ const void *buf, uint32_t len)
+{
+ uint32_t off = ctx->write.lpc_addr + (pos - ctx->write.cur_pos);
+ int rc;
+
+
+ prlog(PR_TRACE, "Writing at 0x%08x for 0x%08x offset: 0x%08x\n",
+ pos, len, off);
+
+ while(len) {
+ uint32_t chunk;
+
+ if (len > 3 && !(off & 3)) {
+ rc = lpc_write(OPAL_LPC_FW, off,
+ *(uint32_t *)buf, 4);
+ chunk = 4;
+ } else {
+ rc = lpc_write(OPAL_LPC_FW, off,
+ *(uint8_t *)buf, 1);
+ chunk = 1;
+ }
+ if (rc) {
+ prlog(PR_ERR, "lpc_write failure %d to FW 0x%08x\n", rc, off);
+ return rc;
+ }
+ len -= chunk;
+ off += chunk;
+ buf += chunk;
+ }
+
+ return 0;
+}
+
+/* Best-effort asynchronous event handling by blocklevel callbacks */
+static int ipmi_flash_handle_events(struct ipmi_flash_data *ctx)
+{
+ uint8_t status;
+ bool update;
+
+ lock(&ctx->lock);
+ status = ctx->status;
+ update = ctx->update;
+ if (update) {
+ ctx->status &= ~HIOMAP_E_ACK_MASK;
+ ctx->update = false;
+ }
+ unlock(&ctx->lock);
+
+ if (!update)
+ return 0;
+
+ if (status & HIOMAP_E_FLASH_LOST) {
+ prlog(PR_INFO, "Lost control of flash device\n");
+ return FLASH_ERR_AGAIN;
+ }
+
+ if (!(status & HIOMAP_E_DAEMON_READY)) {
+ prerror("Daemon not ready\n");
+ return FLASH_ERR_DEVICE_GONE;
+ }
+
+ if (status & HIOMAP_E_BMC_REBOOT) {
+ /* Avoid handling acks if we're renegotiating, they're stale */
+ if (!hiomap_get_info(ctx)) {
+ prerror("Failure to renegotiate after BMC reboot\n");
+ return FLASH_ERR_DEVICE_GONE;
+ }
+
+ return 0;
+ }
+
+ if (status & HIOMAP_E_ACK_MASK) {
+ if (!hiomap_ack(ctx, status & HIOMAP_E_ACK_MASK)) {
+ prerror("Failed to ack events: 0x%x\n",
+ status & HIOMAP_E_ACK_MASK);
+ return FLASH_ERR_AGAIN;
+ }
+ }
+
+ return 0;
+}
+
+static int ipmi_flash_read(struct blocklevel_device *bl, uint64_t pos,
+ void *buf, uint64_t len)
+{
+ struct ipmi_flash_data *ctx;
+ uint64_t size;
+ int rc = 0;
+
+ /* LPC is only 32bit */
+ if (pos > UINT_MAX || len > UINT_MAX)
+ return FLASH_ERR_PARM_ERROR;
+
+ ctx = container_of(bl, struct ipmi_flash_data, bl);
+
+ rc = ipmi_flash_handle_events(ctx);
+ if (rc)
+ return rc;
+
+ prlog(PR_TRACE, "Flash read at %#" PRIx64 " for %#" PRIx64 "\n", pos, len);
+ while (len > 0) {
+ /* Move window and get a new size to read */
+ if (!hiomap_window_move(ctx, &ctx->read,
+ MBOX_C_CREATE_READ_WINDOW, pos, len,
+ &size))
+ return FLASH_ERR_PARM_ERROR;
+
+ /* Perform the read for this window */
+ rc = lpc_window_read(ctx, pos, buf, size);
+ if (rc)
+ return rc;
+
+ len -= size;
+ pos += size;
+ buf += size;
+ /*
+ * Ensure my window is still open, if it isn't we can't trust
+ * what we read
+ */
+#if 0
+ if (!is_valid(mbox_flash, &mbox_flash->read))
+ return FLASH_ERR_AGAIN;
+#endif
+ }
+ return rc;
+
+}
+
+static int ipmi_flash_write(struct blocklevel_device *bl, uint64_t pos,
+ const void *buf, uint64_t len)
+{
+ struct ipmi_flash_data *ctx;
+ uint64_t size;
+ int rc = 0;
+
+ /* LPC is only 32bit */
+ if (pos > UINT_MAX || len > UINT_MAX)
+ return FLASH_ERR_PARM_ERROR;
+
+ ctx = container_of(bl, struct ipmi_flash_data, bl);
+
+ rc = ipmi_flash_handle_events(ctx);
+ if (rc)
+ return rc;
+
+ prlog(PR_TRACE, "Flash write at %#" PRIx64 " for %#" PRIx64 "\n", pos, len);
+ while (len > 0) {
+ /* Move window and get a new size to read */
+ if (!hiomap_window_move(ctx, &ctx->write,
+ MBOX_C_CREATE_WRITE_WINDOW, pos, len,
+ &size)) {
+ return FLASH_ERR_PARM_ERROR;
+ }
+
+ /* Perform the write for this window */
+ rc = lpc_window_write(ctx, pos, buf, size);
+ if (rc)
+ return rc;
+
+ if (!hiomap_mark_dirty(ctx, pos, size)) {
+ return FLASH_ERR_PARM_ERROR;
+ }
+
+ /* ARJ: This comment is incorrect, the BMC must flush any dirty
+ * regions on close
+ */
+ /*
+ * Must flush here as changing the window contents
+ * without flushing entitles the BMC to throw away the
+ * data. Unlike the read case there isn't a need to explicitly
+ * validate the window, the flush command will fail if the
+ * window was compromised.
+ */
+ if (!hiomap_flush(ctx)) {
+ return FLASH_ERR_PARM_ERROR;
+ }
+
+ len -= size;
+ pos += size;
+ buf += size;
+ }
+ return rc;
+
+}
+
+static int ipmi_flash_erase(struct blocklevel_device *bl, uint64_t pos,
+ uint64_t len)
+{
+ struct ipmi_flash_data *ctx;
+ int rc;
+
+ /* LPC is only 32bit */
+ if (pos > UINT_MAX || len > UINT_MAX)
+ return FLASH_ERR_PARM_ERROR;
+
+ ctx = container_of(bl, struct ipmi_flash_data, bl);
+
+ rc = ipmi_flash_handle_events(ctx);
+ if (rc)
+ return rc;
+
+ prlog(PR_TRACE, "Flash erase at 0x%08x for 0x%08x\n", (u32) pos, (u32) len);
+ while (len > 0) {
+ uint64_t size;
+
+ /* Move window and get a new size to erase */
+ if (!hiomap_window_move(ctx, &ctx->write,
+ MBOX_C_CREATE_WRITE_WINDOW, pos, len,
+ &size))
+ return FLASH_ERR_PARM_ERROR;
+
+ if (!hiomap_erase(ctx, pos, size))
+ return FLASH_ERR_PARM_ERROR;
+
+ /*
+ * Flush directly, don't mark that region dirty otherwise it
+ * isn't clear if a write happened there or not
+ */
+
+ if (!hiomap_flush(ctx))
+ return FLASH_ERR_PARM_ERROR;
+
+ len -= size;
+ pos += size;
+ }
+
+ return 0;
+}
+
+static int ipmi_flash_get_flash_info(struct blocklevel_device *bl,
+ const char **name, uint64_t *total_size,
+ uint32_t *erase_granule)
+{
+ struct ipmi_flash_data *ctx;
+ int rc;
+
+ ctx = container_of(bl, struct ipmi_flash_data, bl);
+
+ rc = ipmi_flash_handle_events(ctx);
+ if (rc)
+ return rc;
+
+ if (!hiomap_get_flash_info(ctx)) {
+ abort();
+ }
+
+ ctx->bl.erase_mask = ctx->erase_granule - 1;
+
+ if (name)
+ *name = NULL;
+ if (total_size)
+ *total_size = ctx->total_size;
+ if (erase_granule)
+ *erase_granule = ctx->erase_granule;
+
+ return 0;
+}
+
+int ipmi_flash_init(struct blocklevel_device **bl)
+{
+ struct ipmi_flash_data *ctx;
+ int rc;
+
+ if (!bmc_platform->ipmi_oem_hiomap_cmd)
+ /* FIXME: Find a better error code */
+ return FLASH_ERR_DEVICE_GONE;
+
+ if (!bl)
+ return FLASH_ERR_PARM_ERROR;
+
+ *bl = NULL;
+
+ ctx = zalloc(sizeof(struct ipmi_flash_data));
+ if (!ctx)
+ return FLASH_ERR_MALLOC_FAILED;
+
+ init_lock(&ctx->lock);
+
+ ctx->bl.read = &ipmi_flash_read;
+ ctx->bl.write = &ipmi_flash_write;
+ ctx->bl.erase = &ipmi_flash_erase;
+ ctx->bl.get_info = &ipmi_flash_get_flash_info;
+
+ rc = ipmi_sel_register(CMD_OP_HIOMAP_EVENT, hiomap_event, ctx);
+ if (rc < 0)
+ return rc;
+
+ /* Do a LPC2AHB protocol GET_INFO */
+ if (!hiomap_get_info(ctx)) {
+ prerror("Failed to get hiomap parameters\n");
+ return FLASH_ERR_DEVICE_GONE;
+ }
+
+ if (!hiomap_get_flash_info(ctx)) {
+ prerror("Failed to get flash parameters\n");
+ return FLASH_ERR_DEVICE_GONE;
+ }
+
+ prlog(PR_NOTICE, "Negotiated hiomap protocol v%u\n", ctx->version);
+ prlog(PR_NOTICE, "Block size is %uKiB\n",
+ 1 << (ctx->block_size_shift - 10));
+ prlog(PR_NOTICE, "BMC suggested flash timeout of %us\n", ctx->timeout);
+ prlog(PR_NOTICE, "Flash size is %uMiB\n", ctx->total_size >> 20);
+ prlog(PR_NOTICE, "Erase granule size is %uKiB\n",
+ ctx->erase_granule >> 10);
+
+ ctx->bl.keep_alive = 0;
+
+ *bl = &(ctx->bl);
+
+ return 0;
+}
+
+void ipmi_flash_exit(struct blocklevel_device *bl)
+{
+ struct ipmi_flash_data *ctx;
+ if (bl) {
+ ctx = container_of(bl, struct ipmi_flash_data, bl);
+ free(ctx);
+ }
+}
diff --git a/libflash/ipmi-flash.h b/libflash/ipmi-flash.h
new file mode 100644
index 000000000000..4bec2ad8a092
--- /dev/null
+++ b/libflash/ipmi-flash.h
@@ -0,0 +1,29 @@
+/* Copyright 2018 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.
+ */
+
+#ifndef __LIBFLASH_IPMI_FLASH_H
+#define __LIBFLASH_IPMI_FLASH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "blocklevel.h"
+
+int ipmi_flash_lock(struct blocklevel_device *bl, uint64_t pos, uint64_t len);
+int ipmi_flash_init(struct blocklevel_device **bl);
+void ipmi_flash_exit(struct blocklevel_device *bl);
+
+#endif /* __LIBFLASH_IPMI_FLASH_H */
diff --git a/platforms/astbmc/common.c b/platforms/astbmc/common.c
index 2c32db7295af..23550ef37a67 100644
--- a/platforms/astbmc/common.c
+++ b/platforms/astbmc/common.c
@@ -469,4 +469,5 @@ const struct bmc_platform astbmc_ami = {
const struct bmc_platform astbmc_openbmc = {
.name = "OpenBMC",
.ipmi_oem_partial_add_esel = IPMI_CODE(0x3a, 0xf0),
+ .ipmi_oem_hiomap_cmd = IPMI_CODE(0x3a, 0x5a),
};
--
2.17.1
More information about the Skiboot
mailing list