flash: Introduce flash_validate()

Clean up the protocol_negotiate_version() mess. The existing approach
came about due to viewing the vpnor implementation as an edge case in
its own right. The code becomes much neater if we consider all backends
as equal and afford them the callbacks necessary for correct behaviour.

Change-Id: Ifaeee9da459818cf22b2f137ddc5b8d0356b9be9
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/protocol.c b/protocol.c
index 2a04d89..76bc6ea 100644
--- a/protocol.c
+++ b/protocol.c
@@ -7,8 +7,9 @@
 
 #include "common.h"
 #include "flash.h"
-#include "mboxd.h"
 #include "lpc.h"
+#include "mboxd.h"
+#include "protocol.h"
 #include "windows.h"
 
 #define BLOCK_SIZE_SHIFT_V1		12 /* 4K */
@@ -76,15 +77,18 @@
 	return context->transport->clear_events(context, bmc_event, mask);
 }
 
-int protocol_v1_reset(struct mbox_context *context)
+static int protocol_v1_reset(struct mbox_context *context)
 {
 	/* Host requested it -> No BMC Event */
 	windows_reset_all(context);
 	return lpc_reset(context);
 }
 
-int protocol_v1_get_info(struct mbox_context *context,
-			 struct protocol_get_info *io)
+static int protocol_negotiate_version(struct mbox_context *context,
+				      uint8_t requested);
+
+static int protocol_v1_get_info(struct mbox_context *context,
+				struct protocol_get_info *io)
 {
 	uint8_t old_version = context->version;
 	int rc;
@@ -120,7 +124,7 @@
 	return lpc_map_memory(context);
 }
 
-int protocol_v1_get_flash_info(struct mbox_context *context,
+static int protocol_v1_get_flash_info(struct mbox_context *context,
 			       struct protocol_get_flash_info *io)
 {
 	io->resp.v1.flash_size = context->flash_size;
@@ -149,11 +153,18 @@
 	return lpc_addr >> context->block_size_shift;
 }
 
-int protocol_v1_create_window(struct mbox_context *context,
-			      struct protocol_create_window *io)
+static int protocol_v1_create_window(struct mbox_context *context,
+				     struct protocol_create_window *io)
 {
-	int rc;
 	uint32_t offset = io->req.offset << context->block_size_shift;
+	uint32_t size = io->req.size << context->block_size_shift;
+	int rc;
+
+	rc = flash_validate(context, offset, size, io->req.ro);
+	if (rc < 0) {
+		/* Backend does not allow window to be created. */
+		return rc;
+	}
 
 	/* Close the current window if there is one */
 	if (context->current) {
@@ -202,8 +213,8 @@
 	return 0;
 }
 
-int protocol_v1_mark_dirty(struct mbox_context *context,
-			   struct protocol_mark_dirty *io)
+static int protocol_v1_mark_dirty(struct mbox_context *context,
+				  struct protocol_mark_dirty *io)
 {
 	uint32_t offset = io->req.v1.offset;
 	uint32_t size = io->req.v1.size;
@@ -305,7 +316,8 @@
 				  WINDOW_CLEAN);
 }
 
-int protocol_v1_flush(struct mbox_context *context, struct protocol_flush *io)
+static int protocol_v1_flush(struct mbox_context *context,
+			     struct protocol_flush *io)
 {
 	int rc;
 
@@ -331,7 +343,8 @@
 	return generic_flush(context);
 }
 
-int protocol_v1_close(struct mbox_context *context, struct protocol_close *io)
+static int protocol_v1_close(struct mbox_context *context,
+			     struct protocol_close *io)
 {
 	int rc;
 
@@ -355,7 +368,8 @@
 	return 0;
 }
 
-int protocol_v1_ack(struct mbox_context *context, struct protocol_ack *io)
+static int protocol_v1_ack(struct mbox_context *context,
+			   struct protocol_ack *io)
 {
 	return protocol_events_clear(context,
 				     (io->req.flags & BMC_EVENT_ACK_MASK));
@@ -380,8 +394,8 @@
 	return ret;
 }
 
-int protocol_v2_get_info(struct mbox_context *context,
-			 struct protocol_get_info *io)
+static int protocol_v2_get_info(struct mbox_context *context,
+				struct protocol_get_info *io)
 {
 	uint8_t old_version = context->version;
 	int rc;
@@ -415,8 +429,8 @@
 	return lpc_map_memory(context);
 }
 
-int protocol_v2_get_flash_info(struct mbox_context *context,
-			       struct protocol_get_flash_info *io)
+static int protocol_v2_get_flash_info(struct mbox_context *context,
+				      struct protocol_get_flash_info *io)
 {
 	io->resp.v2.flash_size =
 		context->flash_size >> context->block_size_shift;
@@ -426,8 +440,8 @@
 	return 0;
 }
 
-int protocol_v2_create_window(struct mbox_context *context,
-			      struct protocol_create_window *io)
+static int protocol_v2_create_window(struct mbox_context *context,
+				     struct protocol_create_window *io)
 {
 	int rc;
 
@@ -442,8 +456,8 @@
 	return 0;
 }
 
-int protocol_v2_mark_dirty(struct mbox_context *context,
-			   struct protocol_mark_dirty *io)
+static int protocol_v2_mark_dirty(struct mbox_context *context,
+				  struct protocol_mark_dirty *io)
 {
 	if (!(context->current && context->current_is_write)) {
 		MSG_ERR("Tried to call mark dirty without open write window\n");
@@ -458,8 +472,8 @@
 				  io->req.v2.size, WINDOW_DIRTY);
 }
 
-int protocol_v2_erase(struct mbox_context *context,
-		      struct protocol_erase *io)
+static int protocol_v2_erase(struct mbox_context *context,
+			     struct protocol_erase *io)
 {
 	size_t start, len;
 	int rc;
@@ -487,7 +501,8 @@
 	return 0;
 }
 
-int protocol_v2_flush(struct mbox_context *context, struct protocol_flush *io)
+static int protocol_v2_flush(struct mbox_context *context,
+			     struct protocol_flush *io)
 {
 	if (!(context->current && context->current_is_write)) {
 		MSG_ERR("Tried to call flush without open write window\n");
@@ -497,7 +512,8 @@
 	return generic_flush(context);
 }
 
-int protocol_v2_close(struct mbox_context *context, struct protocol_close *io)
+static int protocol_v2_close(struct mbox_context *context,
+			     struct protocol_close *io)
 {
 	int rc;
 
@@ -521,6 +537,51 @@
 	return 0;
 }
 
+static const struct protocol_ops protocol_ops_v1 = {
+	.reset = protocol_v1_reset,
+	.get_info = protocol_v1_get_info,
+	.get_flash_info = protocol_v1_get_flash_info,
+	.create_window = protocol_v1_create_window,
+	.mark_dirty = protocol_v1_mark_dirty,
+	.erase = NULL,
+	.flush = protocol_v1_flush,
+	.close = protocol_v1_close,
+	.ack = protocol_v1_ack,
+};
+
+static const struct protocol_ops protocol_ops_v2 = {
+	.reset = protocol_v1_reset,
+	.get_info = protocol_v2_get_info,
+	.get_flash_info = protocol_v2_get_flash_info,
+	.create_window = protocol_v2_create_window,
+	.mark_dirty = protocol_v2_mark_dirty,
+	.erase = protocol_v2_erase,
+	.flush = protocol_v2_flush,
+	.close = protocol_v2_close,
+	.ack = protocol_v1_ack,
+};
+
+static const struct protocol_ops *protocol_ops_map[] = {
+	[0] = NULL,
+	[1] = &protocol_ops_v1,
+	[2] = &protocol_ops_v2,
+};
+
+static int protocol_negotiate_version(struct mbox_context *context,
+				      uint8_t requested)
+{
+	/* Check we support the version requested */
+	if (requested < API_MIN_VERSION)
+		return -EINVAL;
+
+	context->version = (requested > API_MAX_VERSION) ?
+				API_MAX_VERSION : requested;
+
+	context->protocol = protocol_ops_map[context->version];
+
+	return context->version;
+}
+
 int protocol_init(struct mbox_context *context)
 {
 	protocol_negotiate_version(context, API_MAX_VERSION);