vpnor: Hijack protocol rather than transport

By hijacking the transport the changes in behaviour were limited to the
mailbox interface. Now that we have a DBus interface as well this would
lead to inconsistent behaviour dependent on the transport.

Instead of hooking the transport, push the hook down to the protocol
level where we will achieve consistent behaviour across all transports.

Change-Id: I437866a6dbda107149336c15a00ee1aa058f5875
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/vpnor/protocol.cpp b/vpnor/protocol.cpp
new file mode 100644
index 0000000..57b35a7
--- /dev/null
+++ b/vpnor/protocol.cpp
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2018 IBM Corp.
+#include "config.h"
+
+extern "C" {
+#include "mbox.h"
+#include "protocol.h"
+#include "vpnor/protocol.h"
+}
+
+#include "vpnor/pnor_partition_table.hpp"
+
+/* XXX: Maybe this should be a method on a class? */
+static bool vpnor_partition_is_readonly(const pnor_partition &part)
+{
+    return part.data.user.data[1] & PARTITION_READONLY;
+}
+
+typedef int (*create_window_fn)(struct mbox_context *context,
+                                struct protocol_create_window *io);
+
+static int generic_vpnor_create_window(struct mbox_context *context,
+                                       struct protocol_create_window *io,
+                                       create_window_fn create_window)
+{
+    if (io->req.ro)
+    {
+        return create_window(context, io);
+    }
+
+    /* Only allow write windows on regions mapped by the ToC as writeable */
+    size_t offset = io->req.offset;
+    offset <<= context->block_size_shift;
+    try
+    {
+        const pnor_partition &part = context->vpnor->table->partition(offset);
+        if (vpnor_partition_is_readonly(part))
+        {
+            return -EPERM;
+        }
+    }
+    catch (const openpower::virtual_pnor::UnmappedOffset &e)
+    {
+        /*
+         * Writes to unmapped areas are not meaningful, so deny the request.
+         * This removes the ability for a compromised host to abuse unused
+         * space if any data was to be persisted (which it isn't).
+         */
+        return -EACCES;
+    }
+
+    return create_window(context, io);
+}
+
+int protocol_v1_vpnor_create_window(struct mbox_context *context,
+                                    struct protocol_create_window *io)
+{
+    return generic_vpnor_create_window(context, io, protocol_v1_create_window);
+}
+
+int protocol_v2_vpnor_create_window(struct mbox_context *context,
+                                    struct protocol_create_window *io)
+{
+    return generic_vpnor_create_window(context, io, protocol_v2_create_window);
+}