From patchwork Tue Oct 9 07:32:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Jeffery X-Patchwork-Id: 981075 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42Tpsm4qv9z9s9G for ; Tue, 9 Oct 2018 18:35:08 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=aj.id.au Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=aj.id.au header.i=@aj.id.au header.b="lGBF52Q/"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="wSWDpZQj"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 42Tpsm37hvzF3Cy for ; Tue, 9 Oct 2018 18:35:08 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=aj.id.au Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=aj.id.au header.i=@aj.id.au header.b="lGBF52Q/"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="wSWDpZQj"; dkim-atps=neutral X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=aj.id.au (client-ip=66.111.4.27; helo=out3-smtp.messagingengine.com; envelope-from=andrew@aj.id.au; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=aj.id.au Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=aj.id.au header.i=@aj.id.au header.b="lGBF52Q/"; dkim=pass (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="wSWDpZQj"; dkim-atps=neutral Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42TpqN0g1hzF3Bb for ; Tue, 9 Oct 2018 18:33:04 +1100 (AEDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 84D7721D3C; Tue, 9 Oct 2018 03:32:58 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Tue, 09 Oct 2018 03:32:58 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aj.id.au; h=from :to:cc:subject:date:message-id:in-reply-to:references; s=fm3; bh=YAGoLlTUpnq2nFKqPhOUJUb+999GIfZIGA7Wq12gWeo=; b=lGBF52Q/cQl8 LNpo7oBHC6GDD/YnRzK6Jp3SYHK7sH35ZKvitNgXy4HHY7c60HXBiDcarH/PVqbz uHvl6DmV71IaH3Snm9aXDNlxBwiQcSrwI9E91clUwYNiZREuH9U7sTeK2x/ChhMe ZMXeOwLdYAhcd0QEvfpgm2aqQohVDtKViFtbXruRbD7cn9wCSUAEWXP1EtqDSkBN 4jzVl7OUUuIf14VmZKAiSGVbrn2H7Z8raAYr/8zwisWP3+gKq5SnNNKeAKiE3kwR hWqAgsYyKcYmw04Fw6XtnA+ZfHhj1ikv/ypcwMp5GP85W0lO8H/FyycJ/K+idzvM +4+ikEDrfw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-proxy:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm1; bh=YAGoLlTUpnq2nFKqPhOUJUb+999GI fZIGA7Wq12gWeo=; b=wSWDpZQj89uiwdHZC7xP5s/T3NyHemrRekw+oGNGQbCb4 j19kxHoIQcARJnPkh4y2v+jlyD/54g+SPc6MH+Z3ozLwAyTig22ZQnWbtE4lJ6DS tRvtKy9rSXyT7VhN9rxzYUjZpRGlC8QMgUnNuEd9/gjjBa7FBE4ZY0TbN0ZVYRQm gyrKLdtkyRucyPUBX5iFhKx8YrNkbDmqtGSinb3d4AIb+HYRD9LIMm3yc+cWslfC I1Su4ZrxDX/UdfaD1xNzilFtw1hgO7ihX3cDAl5NCxcM9Ssk+8WLoIVtVjnerT8U fHoJxOTuKVPbnKpqB2LIzgkF16hmoQc91It95ukIQ== X-ME-Sender: X-ME-Proxy: Received: from dave.ibm.com (50-203-181-99-static.hfc.comcastbusiness.net [50.203.181.99]) by mail.messagingengine.com (Postfix) with ESMTPA id 1EF5CE4179; Tue, 9 Oct 2018 03:32:57 -0400 (EDT) From: Andrew Jeffery To: skiboot@lists.ozlabs.org Date: Tue, 9 Oct 2018 00:32:31 -0700 Message-Id: <20181009073237.16251-6-andrew@aj.id.au> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181009073237.16251-1-andrew@aj.id.au> References: <20181009073237.16251-1-andrew@aj.id.au> Subject: [Skiboot] [PATCH v3 05/11] libflash: Add ipmi-hiomap X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: dkodihal@in.ibm.com, Andrew Jeffery , anoo@linux.ibm.com MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" ipmi-hiomap implements the PNOR access control protocol formerly known as "the mbox protocol" but uses IPMI instead of the AST LPC mailbox as a transport. As there is no-longer any mailbox involved in this alternate implementation the old protocol name is quite misleading, and so it has been renamed to "the hiomap protoocol" (Host I/O Mapping protocol). The same commands and events are used though this client-side implementation assumes v2 of the protocol is supported by the BMC. The code is a heavily-reworked copy of the mbox-flash source and is introduced this way to allow for the mbox implementation's eventual removal. mbox-flash should in theory be renamed to mbox-hiomap for consistency, but as it is on life-support effective immediately we may as well just remove it entirely when the time is right. Signed-off-by: Andrew Jeffery --- include/hiomap.h | 68 +++ include/lpc-mbox.h | 35 +- include/platform.h | 1 + libflash/Makefile.inc | 2 +- libflash/ipmi-hiomap.c | 854 ++++++++++++++++++++++++++++++++++++++ libflash/ipmi-hiomap.h | 28 ++ platforms/astbmc/common.c | 1 + 7 files changed, 971 insertions(+), 18 deletions(-) create mode 100644 include/hiomap.h create mode 100644 libflash/ipmi-hiomap.c create mode 100644 libflash/ipmi-hiomap.h diff --git a/include/hiomap.h b/include/hiomap.h new file mode 100644 index 000000000000..327055467d67 --- /dev/null +++ b/include/hiomap.h @@ -0,0 +1,68 @@ +/* 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 __HIOMAP_H +#define __HIOMAP_H + +#include +#include +#include +#include + +#define HIOMAP_V1 1 +#define HIOMAP_V2 2 + +#define HIOMAP_C_RESET 1 +#define HIOMAP_C_GET_INFO 2 +#define HIOMAP_C_GET_FLASH_INFO 3 +#define HIOMAP_C_CREATE_READ_WINDOW 4 +#define HIOMAP_C_CLOSE_WINDOW 5 +#define HIOMAP_C_CREATE_WRITE_WINDOW 6 +#define HIOMAP_C_MARK_DIRTY 7 +#define HIOMAP_C_FLUSH 8 +#define HIOMAP_C_ACK 9 +#define HIOMAP_C_ERASE 10 +#define HIOMAP_C_DEVICE_NAME 11 +#define HIOMAP_C_LOCK 12 + +#define HIOMAP_E_ACK_MASK 0x3 +#define HIOMAP_E_PROTOCOL_RESET (1 << 0) +#define HIOMAP_E_WINDOW_RESET (1 << 1) +#define HIOMAP_E_FLASH_LOST (1 << 6) +#define HIOMAP_E_DAEMON_READY (1 << 7) + +struct hiomap_v2_range { + le16 offset; + le16 size; +} __packed; + +struct hiomap_v2_info { + uint8_t block_size_shift; + le16 timeout; +} __packed; + +struct hiomap_v2_flash_info { + le16 total_size; + le16 erase_granule; +} __packed; + +struct hiomap_v2_create_window { + le16 lpc_addr; + le16 size; + le16 offset; +} __packed; + +#endif /* __HIOMAP_H */ diff --git a/include/lpc-mbox.h b/include/lpc-mbox.h index 167cc71c41cc..8ecf0c825191 100644 --- a/include/lpc-mbox.h +++ b/include/lpc-mbox.h @@ -17,6 +17,7 @@ #ifndef __LPC_MBOX_H #define __LPC_MBOX_H +#include #include #include @@ -24,18 +25,18 @@ #define BMC_MBOX_READ_REGS 16 #define BMC_MBOX_WRITE_REGS 13 -#define MBOX_C_RESET_STATE 0x01 -#define MBOX_C_GET_MBOX_INFO 0x02 -#define MBOX_C_GET_FLASH_INFO 0x03 -#define MBOX_C_CREATE_READ_WINDOW 0x04 -#define MBOX_C_CLOSE_WINDOW 0x05 -#define MBOX_C_CREATE_WRITE_WINDOW 0x06 -#define MBOX_C_MARK_WRITE_DIRTY 0x07 -#define MBOX_C_WRITE_FLUSH 0x08 -#define MBOX_C_BMC_EVENT_ACK 0x09 -#define MBOX_C_MARK_WRITE_ERASED 0x0a -#define MBOX_C_GET_FLASH_NAME 0xb /* Unimplemented */ -#define MBOX_C_MARK_LOCKED 0x0c +#define MBOX_C_RESET_STATE HIOMAP_C_RESET +#define MBOX_C_GET_MBOX_INFO HIOMAP_C_GET_INFO +#define MBOX_C_GET_FLASH_INFO HIOMAP_C_GET_FLASH_INFO +#define MBOX_C_CREATE_READ_WINDOW HIOMAP_C_CREATE_READ_WINDOW +#define MBOX_C_CLOSE_WINDOW HIOMAP_C_CLOSE_WINDOW +#define MBOX_C_CREATE_WRITE_WINDOW HIOMAP_C_CREATE_WRITE_WINDOW +#define MBOX_C_MARK_WRITE_DIRTY HIOMAP_C_MARK_DIRTY +#define MBOX_C_WRITE_FLUSH HIOMAP_C_FLUSH +#define MBOX_C_BMC_EVENT_ACK HIOMAP_C_ACK +#define MBOX_C_MARK_WRITE_ERASED HIOMAP_C_ERASE +#define MBOX_C_GET_FLASH_NAME HIOMAP_C_DEVICE_NAME +#define MBOX_C_MARK_LOCKED HIOMAP_C_LOCK #define MBOX_COMMAND_COUNT 12 #define MBOX_R_SUCCESS 0x01 @@ -48,11 +49,11 @@ #define MBOX_R_SEQ_ERROR 0x08 #define MBOX_R_LOCKED 0x09 -#define MBOX_ATTN_ACK_MASK 0x3 -#define MBOX_ATTN_BMC_REBOOT (1 << 0) -#define MBOX_ATTN_BMC_WINDOW_RESET (1 << 1) -#define MBOX_ATTN_BMC_FLASH_LOST (1 << 6) -#define MBOX_ATTN_BMC_DAEMON_READY (1 << 7) +#define MBOX_ATTN_ACK_MASK HIOMAP_E_ACK_MASK +#define MBOX_ATTN_BMC_REBOOT HIOMAP_E_PROTOCOL_RESET +#define MBOX_ATTN_BMC_WINDOW_RESET HIOMAP_E_WINDOW_RESET +#define MBOX_ATTN_BMC_FLASH_LOST HIOMAP_E_FLASH_LOST +#define MBOX_ATTN_BMC_DAEMON_READY HIOMAP_E_DAEMON_READY /* Default poll interval before interrupts are working */ #define MBOX_DEFAULT_POLL_MS 200 diff --git a/include/platform.h b/include/platform.h index fee5a76cf9e8..294e06cd08d4 100644 --- a/include/platform.h +++ b/include/platform.h @@ -43,6 +43,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..ab094b92ed0d 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-hiomap.c LIBFLASH_OBJS = $(LIBFLASH_SRCS:%.c=%.o) SUBDIRS += libflash diff --git a/libflash/ipmi-hiomap.c b/libflash/ipmi-hiomap.c new file mode 100644 index 000000000000..780ffdf2caae --- /dev/null +++ b/libflash/ipmi-hiomap.c @@ -0,0 +1,854 @@ +/* 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) "HIOMAP: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "errors.h" +#include "ipmi-hiomap.h" + +#define CMD_OP_HIOMAP_EVENT 0x0f + +enum lpc_window_state { closed_window, read_window, write_window }; + +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 */ +}; + +struct ipmi_hiomap { + /* Members protected by the blocklevel lock */ + uint8_t seq; + uint8_t version; + uint8_t block_size_shift; + uint16_t timeout; + struct blocklevel_device bl; + uint32_t total_size; + uint32_t erase_granule; + struct lpc_window current; + + /* + * update, bmc_state and window_state can be accessed by both calls + * through read/write/erase functions and the IPMI SEL handler. All + * three variables are protected by lock to avoid conflict. + */ + struct lock lock; + bool update; + uint8_t bmc_state; + enum lpc_window_state window_state; +}; + +struct ipmi_hiomap_result { + struct ipmi_hiomap *ctx; + int16_t cc; +}; + +#define RESULT_INIT(_name, _ctx) struct ipmi_hiomap_result _name = { _ctx, -1 } + +static inline uint32_t blocks_to_bytes(struct ipmi_hiomap *ctx, uint16_t blocks) +{ + return blocks << ctx->block_size_shift; +} + +static inline uint16_t bytes_to_blocks(struct ipmi_hiomap *ctx, uint32_t bytes) +{ + return bytes >> ctx->block_size_shift; +} + +/* Is the current window able perform the complete operation */ +static bool hiomap_window_valid(struct ipmi_hiomap *ctx, uint64_t pos, + uint64_t len) +{ + enum lpc_window_state window_state; + uint8_t bmc_state; + + lock(&ctx->lock); + bmc_state = ctx->bmc_state; + window_state = ctx->window_state; + unlock(&ctx->lock); + + if (bmc_state & HIOMAP_E_FLASH_LOST) + return false; + if (window_state == closed_window) + return false; + if (pos < ctx->current.cur_pos) /* start */ + return false; + if ((pos + len) > (ctx->current.cur_pos + ctx->current.size)) /* end */ + return false; + return true; +} + + +static void ipmi_hiomap_cmd_cb(struct ipmi_msg *msg) +{ + struct ipmi_hiomap_result *res = msg->user_data; + struct ipmi_hiomap *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); + res->cc = IPMI_ERR_UNSPECIFIED; + return; + } + + if (msg->data[1] != ctx->seq) { + prerror("Unmatched sequence number: wanted %u got %u\n", + ctx->seq, msg->data[1]); + res->cc = IPMI_ERR_UNSPECIFIED; + return; + } + + switch (msg->data[0]) { + case HIOMAP_C_GET_INFO: + { + struct hiomap_v2_info *parms; + + 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(); + } + + parms = (struct hiomap_v2_info *)&msg->data[3]; + ctx->block_size_shift = parms->block_size_shift; + ctx->timeout = le16_to_cpu(parms->timeout); + break; + } + case HIOMAP_C_GET_FLASH_INFO: + { + struct hiomap_v2_flash_info *parms; + + if (msg->resp_size != 6) { + prerror("%u: Unexpected response size: %u\n", msg->data[0], + msg->resp_size); + abort(); + } + + parms = (struct hiomap_v2_flash_info *)&msg->data[2]; + ctx->total_size = + blocks_to_bytes(ctx, le16_to_cpu(parms->total_size)); + ctx->erase_granule = + blocks_to_bytes(ctx, le16_to_cpu(parms->erase_granule)); + break; + } + case HIOMAP_C_CREATE_READ_WINDOW: + case HIOMAP_C_CREATE_WRITE_WINDOW: + { + struct hiomap_v2_create_window *parms; + + if (msg->resp_size != 8) { + prerror("%u: Unexpected response size: %u\n", msg->data[0], + msg->resp_size); + abort(); + } + + parms = (struct hiomap_v2_create_window *)&msg->data[2]; + ctx->current.lpc_addr = + blocks_to_bytes(ctx, le16_to_cpu(parms->lpc_addr)); + ctx->current.size = + blocks_to_bytes(ctx, le16_to_cpu(parms->size)); + ctx->current.cur_pos = + blocks_to_bytes(ctx, le16_to_cpu(parms->offset)); + + lock(&ctx->lock); + if (msg->data[0] == HIOMAP_C_CREATE_READ_WINDOW) + ctx->window_state = read_window; + else + ctx->window_state = write_window; + unlock(&ctx->lock); + + break; + } + case HIOMAP_C_CLOSE_WINDOW: + lock(&ctx->lock); + ctx->window_state = closed_window; + unlock(&ctx->lock); + 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_hiomap *ctx) +{ + RESULT_INIT(res, ctx); + unsigned char req[3]; + struct ipmi_msg *msg; + + ctx->bmc_state = 0; + + /* Negotiate protocol version 2 */ + req[0] = HIOMAP_C_GET_INFO; + req[1] = ++ctx->seq; + req[2] = HIOMAP_V2; + + msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, + bmc_platform->ipmi_oem_hiomap_cmd, ipmi_hiomap_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->bmc_state |= HIOMAP_E_DAEMON_READY; + unlock(&ctx->lock); + + return true; +} + +static bool hiomap_get_flash_info(struct ipmi_hiomap *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_hiomap_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_hiomap *ctx, uint8_t command, + uint64_t pos, uint64_t len, uint64_t *size) +{ + enum lpc_window_state want_state; + struct hiomap_v2_range *range; + RESULT_INIT(res, ctx); + unsigned char req[6]; + struct ipmi_msg *msg; + bool valid_state; + bool is_read; + + is_read = (command == HIOMAP_C_CREATE_READ_WINDOW); + want_state = is_read ? read_window : write_window; + valid_state = want_state == ctx->window_state; + if (valid_state && hiomap_window_valid(ctx, pos, len)) { + *size = len; + return true; + } + + req[0] = command; + req[1] = ++ctx->seq; + + range = (struct hiomap_v2_range *)&req[2]; + range->offset = cpu_to_le16(bytes_to_blocks(ctx, pos)); + range->size = cpu_to_le16(bytes_to_blocks(ctx, len)); + + lock(&ctx->lock); + ctx->window_state = closed_window; + unlock(&ctx->lock); + + msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, + bmc_platform->ipmi_oem_hiomap_cmd, ipmi_hiomap_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) > (ctx->current.cur_pos + ctx->current.size)) + /* Adjust size to meet current window */ + *size = (ctx->current.cur_pos + ctx->current.size) - pos; + + if (len != 0 && *size == 0) { + prerror("Invalid window properties: len: %llu, size: %llu\n", + len, *size); + abort(); + } + + prlog(PR_DEBUG, "Opened %s window from 0x%x for %u bytes at 0x%x\n", + (command == HIOMAP_C_CREATE_READ_WINDOW) ? "read" : "write", + ctx->current.cur_pos, ctx->current.size, ctx->current.lpc_addr); + + return true; +} + +static bool hiomap_mark_dirty(struct ipmi_hiomap *ctx, uint64_t offset, + uint64_t size) +{ + struct hiomap_v2_range *range; + enum lpc_window_state state; + RESULT_INIT(res, ctx); + unsigned char req[6]; + struct ipmi_msg *msg; + uint32_t pos; + + lock(&ctx->lock); + state = ctx->window_state; + unlock(&ctx->lock); + + if (state != write_window) + return false; + + req[0] = HIOMAP_C_MARK_DIRTY; + req[1] = ++ctx->seq; + + pos = offset - ctx->current.cur_pos; + range = (struct hiomap_v2_range *)&req[2]; + range->offset = cpu_to_le16(bytes_to_blocks(ctx, pos)); + range->size = cpu_to_le16(bytes_to_blocks(ctx, size)); + + msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, + bmc_platform->ipmi_oem_hiomap_cmd, ipmi_hiomap_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_hiomap *ctx) +{ + enum lpc_window_state state; + RESULT_INIT(res, ctx); + unsigned char req[2]; + struct ipmi_msg *msg; + + lock(&ctx->lock); + state = ctx->window_state; + unlock(&ctx->lock); + + if (state != write_window) + return false; + + req[0] = HIOMAP_C_FLUSH; + req[1] = ++ctx->seq; + + msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, + bmc_platform->ipmi_oem_hiomap_cmd, ipmi_hiomap_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_hiomap *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_hiomap_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_hiomap *ctx, uint64_t offset, + uint64_t size) +{ + struct hiomap_v2_range *range; + enum lpc_window_state state; + RESULT_INIT(res, ctx); + unsigned char req[6]; + struct ipmi_msg *msg; + uint32_t pos; + + lock(&ctx->lock); + state = ctx->window_state; + unlock(&ctx->lock); + + if (state != write_window) + return false; + + req[0] = HIOMAP_C_ERASE; + req[1] = ++ctx->seq; + + pos = offset - ctx->current.cur_pos; + range = (struct hiomap_v2_range *)&req[2]; + range->offset = cpu_to_le16(bytes_to_blocks(ctx, pos)); + range->size = cpu_to_le16(bytes_to_blocks(ctx, size)); + + msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, + bmc_platform->ipmi_oem_hiomap_cmd, ipmi_hiomap_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_hiomap *ctx = context; + + lock(&ctx->lock); + ctx->bmc_state = events; + ctx->update = true; + + if (events & (HIOMAP_E_PROTOCOL_RESET | HIOMAP_E_WINDOW_RESET)) + ctx->window_state = closed_window; + unlock(&ctx->lock); +} + +static int lpc_window_read(struct ipmi_hiomap *ctx, uint32_t pos, + void *buf, uint32_t len) +{ + uint32_t off = ctx->current.lpc_addr + (pos - ctx->current.cur_pos); + int rc; + + if ((ctx->current.lpc_addr + ctx->current.size) < (off + len)) + return FLASH_ERR_PARM_ERROR; + + 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_hiomap *ctx, uint32_t pos, + const void *buf, uint32_t len) +{ + uint32_t off = ctx->current.lpc_addr + (pos - ctx->current.cur_pos); + enum lpc_window_state state; + int rc; + + lock(&ctx->lock); + state = ctx->window_state; + unlock(&ctx->lock); + + if (state != write_window) + return FLASH_ERR_PARM_ERROR; + + if ((ctx->current.lpc_addr + ctx->current.size) < (off + len)) + return FLASH_ERR_PARM_ERROR; + + 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_hiomap_handle_events(struct ipmi_hiomap *ctx) +{ + uint8_t status; + bool update; + + lock(&ctx->lock); + status = ctx->bmc_state; + update = ctx->update; + if (update) { + ctx->bmc_state &= ~HIOMAP_E_ACK_MASK; + ctx->update = false; + } + unlock(&ctx->lock); + + if (!update) + return 0; + + if (!(status & HIOMAP_E_DAEMON_READY)) { + prerror("Daemon not ready\n"); + return FLASH_ERR_DEVICE_GONE; + } + + if (status & HIOMAP_E_ACK_MASK) { + /* ACK is unversioned, can send it if the daemon is ready */ + 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; + } + } + + if (status & HIOMAP_E_FLASH_LOST) { + prlog(PR_INFO, "Lost control of flash device\n"); + return FLASH_ERR_AGAIN; + } + + if (status & HIOMAP_E_PROTOCOL_RESET) { + if (!hiomap_get_info(ctx)) { + prerror("Failure to renegotiate after protocol reset\n"); + return FLASH_ERR_DEVICE_GONE; + } + + if (!hiomap_get_flash_info(ctx)) { + prerror("Failure to fetch flash info after protocol reset\n"); + return FLASH_ERR_DEVICE_GONE; + } + + prlog(PR_INFO, "Renegotiated protocol after reset\n"); + return FLASH_ERR_AGAIN; + } + + if (status & HIOMAP_E_WINDOW_RESET) { + prlog(PR_INFO, "Window was reset\n"); + return FLASH_ERR_AGAIN; + } + + return 0; +} + +static int ipmi_hiomap_read(struct blocklevel_device *bl, uint64_t pos, + void *buf, uint64_t len) +{ + struct ipmi_hiomap *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_hiomap, bl); + + rc = ipmi_hiomap_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, HIOMAP_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; + + /* Check we can trust what we read */ + if (!hiomap_window_valid(ctx, pos, size)) + return FLASH_ERR_AGAIN; + + len -= size; + pos += size; + buf += size; + } + return rc; + +} + +static int ipmi_hiomap_write(struct blocklevel_device *bl, uint64_t pos, + const void *buf, uint64_t len) +{ + struct ipmi_hiomap *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_hiomap, bl); + + rc = ipmi_hiomap_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, HIOMAP_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; + + /* + * The BMC *should* flush if the window is implicitly closed, + * but do an explicit flush here to be sure. + * + * XXX: Removing this could improve performance + */ + if (!hiomap_flush(ctx)) + return FLASH_ERR_PARM_ERROR; + + len -= size; + pos += size; + buf += size; + } + return rc; +} + +static int ipmi_hiomap_erase(struct blocklevel_device *bl, uint64_t pos, + uint64_t len) +{ + struct ipmi_hiomap *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_hiomap, bl); + + rc = ipmi_hiomap_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, HIOMAP_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_hiomap_get_flash_info(struct blocklevel_device *bl, + const char **name, uint64_t *total_size, + uint32_t *erase_granule) +{ + struct ipmi_hiomap *ctx; + int rc; + + ctx = container_of(bl, struct ipmi_hiomap, bl); + + rc = ipmi_hiomap_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_hiomap_init(struct blocklevel_device **bl) +{ + struct ipmi_hiomap *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_hiomap)); + if (!ctx) + return FLASH_ERR_MALLOC_FAILED; + + init_lock(&ctx->lock); + + ctx->bl.read = &ipmi_hiomap_read; + ctx->bl.write = &ipmi_hiomap_write; + ctx->bl.erase = &ipmi_hiomap_erase; + ctx->bl.get_info = &ipmi_hiomap_get_flash_info; + + rc = ipmi_sel_register(CMD_OP_HIOMAP_EVENT, hiomap_event, ctx); + if (rc < 0) + return rc; + + /* Ack all pending ack-able events to avoid spurious failures */ + if (!hiomap_ack(ctx, HIOMAP_E_ACK_MASK)) { + prerror("Failed to ack events: 0x%x\n", HIOMAP_E_ACK_MASK); + return FLASH_ERR_AGAIN; + } + + /* Negotiate protocol behaviour */ + if (!hiomap_get_info(ctx)) { + prerror("Failed to get hiomap parameters\n"); + return FLASH_ERR_DEVICE_GONE; + } + + /* Grab the flash parameters */ + 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_hiomap_exit(struct blocklevel_device *bl) +{ + struct ipmi_hiomap *ctx; + if (bl) { + ctx = container_of(bl, struct ipmi_hiomap, bl); + free(ctx); + } +} diff --git a/libflash/ipmi-hiomap.h b/libflash/ipmi-hiomap.h new file mode 100644 index 000000000000..4742b7d80b16 --- /dev/null +++ b/libflash/ipmi-hiomap.h @@ -0,0 +1,28 @@ +/* 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_HIOMAP_H +#define __LIBFLASH_IPMI_HIOMAP_H + +#include +#include + +#include "blocklevel.h" + +int ipmi_hiomap_init(struct blocklevel_device **bl); +void ipmi_hiomap_exit(struct blocklevel_device *bl); + +#endif /* __LIBFLASH_IPMI_HIOMAP_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), };