[PATCH 03/29] hw/core: Add Remote Port session state and hello protocol

Ruslan Ruslichenko posted 29 patches 1 day, 11 hours ago
[PATCH 03/29] hw/core: Add Remote Port session state and hello protocol
Posted by Ruslan Ruslichenko 1 day, 11 hours ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

Implement the session management for Remote Port protocol.
Key features implemented are following:

- Hello packet handling and capabilities negotiation
- Dynamic packet management routines
- rp_peer_state struct definition to hold negotiated
capabilities.

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
Signed-off-by: Takahiro Nakata <takahiro.nakata.wr@renesas.com>
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/core/remote-port-proto.c         | 134 +++++++++++++++++++++++++++-
 include/hw/core/remote-port-proto.h |  72 +++++++++++++++
 2 files changed, 205 insertions(+), 1 deletion(-)

diff --git a/hw/core/remote-port-proto.c b/hw/core/remote-port-proto.c
index 1653fe7b78..969dfc3cb2 100644
--- a/hw/core/remote-port-proto.c
+++ b/hw/core/remote-port-proto.c
@@ -64,7 +64,43 @@ int rp_decode_payload(struct rp_pkt *pkt)
     int used = 0;
 
     switch (pkt->hdr.cmd) {
-    /* TBD */
+    case RP_CMD_hello:
+        assert(pkt->hdr.len >= sizeof pkt->hello.version);
+        pkt->hello.version.major = be16_to_cpu(pkt->hello.version.major);
+        pkt->hello.version.minor = be16_to_cpu(pkt->hello.version.minor);
+        used += sizeof pkt->hello.version;
+
+        if ((pkt->hdr.len - used) >= sizeof pkt->hello.caps) {
+            void *offset;
+            int i;
+
+            pkt->hello.caps.offset = be32_to_cpu(pkt->hello.caps.offset);
+            pkt->hello.caps.len = be16_to_cpu(pkt->hello.caps.len);
+
+            offset = (char *)pkt + pkt->hello.caps.offset;
+            for (i = 0; i < pkt->hello.caps.len; i++) {
+                uint32_t cap;
+
+                /*
+                 * We don't know if offset is 32bit aligned so use
+                 * memcpy to do the endian conversion.
+                 */
+                memcpy(&cap, offset + i * sizeof cap, sizeof cap);
+                cap = be32_to_cpu(cap);
+                memcpy(offset + i * sizeof cap, &cap, sizeof cap);
+            }
+            used += sizeof pkt->hello.caps;
+        } else {
+            pkt->hello.caps.offset = 0;
+            pkt->hello.caps.len = 0;
+        }
+
+        /*
+         * Consume everything ignoring additional headers we do not yet
+         * know about.
+         */
+        used = pkt->hdr.len;
+        break;
     default:
         break;
     }
@@ -80,3 +116,99 @@ void rp_encode_hdr(struct rp_pkt_hdr *hdr, uint32_t cmd, uint32_t id,
     hdr->dev = cpu_to_be32(dev);
     hdr->flags = cpu_to_be32(flags);
 }
+
+size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
+                            uint16_t version_major, uint16_t version_minor,
+                            uint32_t *caps, uint32_t *caps_out,
+                            uint32_t caps_len)
+{
+    size_t psize = sizeof *pkt + sizeof caps[0] * caps_len;
+    unsigned int i;
+
+    rp_encode_hdr(&pkt->hdr, RP_CMD_hello, id, dev,
+                  psize - sizeof pkt->hdr, 0);
+    pkt->version.major = cpu_to_be16(version_major);
+    pkt->version.minor = cpu_to_be16(version_minor);
+
+    /* Feature list is appeneded right after the hello packet.  */
+    pkt->caps.offset = cpu_to_be32(sizeof *pkt);
+    pkt->caps.len = cpu_to_be16(caps_len);
+
+    for (i = 0; i < caps_len; i++) {
+        uint32_t cap;
+
+        cap = caps[i];
+        caps_out[i] = cpu_to_be32(cap);
+    }
+    return sizeof *pkt;
+}
+
+void rp_process_caps(struct rp_peer_state *peer,
+                     void *caps, size_t caps_len)
+{
+    int i;
+
+    assert(peer->caps.busaccess_ext_base == false);
+
+    for (i = 0; i < caps_len; i++) {
+        uint32_t cap;
+
+        memcpy(&cap, caps + i * sizeof cap, sizeof cap);
+
+        switch (cap) {
+        case CAP_BUSACCESS_EXT_BASE:
+            peer->caps.busaccess_ext_base = true;
+            break;
+        case CAP_BUSACCESS_EXT_BYTE_EN:
+            peer->caps.busaccess_ext_byte_en = true;
+            break;
+        case CAP_WIRE_POSTED_UPDATES:
+            peer->caps.wire_posted_updates = true;
+            break;
+        case CAP_ATS:
+            peer->caps.ats = true;
+            break;
+        }
+    }
+}
+
+void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size)
+{
+    if (dpkt->size < size) {
+        char *u8;
+        dpkt->pkt = realloc(dpkt->pkt, size);
+        u8 = (void *) dpkt->pkt;
+        memset(u8 + dpkt->size, 0, size - dpkt->size);
+        dpkt->size = size;
+    }
+}
+
+void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b)
+{
+    struct rp_pkt *tmp_pkt;
+    size_t tmp_size;
+
+    tmp_pkt = a->pkt;
+    tmp_size = a->size;
+    a->pkt = b->pkt;
+    a->size = b->size;
+    b->pkt = tmp_pkt;
+    b->size = tmp_size;
+}
+
+bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt)
+{
+    return dpkt->size > 0 && dpkt->pkt->hdr.len;
+}
+
+void rp_dpkt_invalidate(RemotePortDynPkt *dpkt)
+{
+    assert(rp_dpkt_is_valid(dpkt));
+    dpkt->pkt->hdr.len = 0;
+}
+
+inline void rp_dpkt_free(RemotePortDynPkt *dpkt)
+{
+    dpkt->size = 0;
+    free(dpkt->pkt);
+}
diff --git a/include/hw/core/remote-port-proto.h b/include/hw/core/remote-port-proto.h
index 1d8e1b25da..ca1ab2f1d3 100644
--- a/include/hw/core/remote-port-proto.h
+++ b/include/hw/core/remote-port-proto.h
@@ -302,6 +302,28 @@ struct rp_pkt {
     };
 };
 
+struct rp_peer_state {
+    void *opaque;
+
+    struct rp_pkt pkt;
+    bool hdr_used;
+
+    struct rp_version version;
+
+    struct {
+        bool busaccess_ext_base;
+        bool busaccess_ext_byte_en;
+        bool wire_posted_updates;
+        bool ats;
+    } caps;
+
+    /* Used to normalize our clk.  */
+    int64_t clk_base;
+
+    struct rp_cfg_state local_cfg;
+    struct rp_cfg_state peer_cfg;
+};
+
 const char *rp_cmd_to_string(enum rp_cmd cmd);
 int rp_decode_hdr(struct rp_pkt *pkt);
 int rp_decode_payload(struct rp_pkt *pkt);
@@ -310,4 +332,54 @@ void rp_encode_hdr(struct rp_pkt_hdr *hdr,
                    uint32_t cmd, uint32_t id, uint32_t dev, uint32_t len,
                    uint32_t flags);
 
+/*
+ * caps is a an array of supported capabilities by the implementor.
+ * caps_out is the encoded (network byte order) version of the
+ * same array. It should be sent to the peer after the hello packet.
+ */
+size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
+                            uint16_t version_major, uint16_t version_minor,
+                            uint32_t *caps, uint32_t *features_out,
+                            uint32_t features_len);
+
+/* rp_encode_hello is deprecated in favor of hello_caps.  */
+static inline size_t __attribute__ ((deprecated))
+rp_encode_hello(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
+                uint16_t version_major, uint16_t version_minor) {
+    return rp_encode_hello_caps(id, dev, pkt, version_major, version_minor,
+                                NULL, NULL, 0);
+}
+
+void rp_process_caps(struct rp_peer_state *peer,
+                     void *caps, size_t caps_len);
+
+/* Dynamically resizable remote port pkt.  */
+
+typedef struct RemotePortDynPkt {
+    struct rp_pkt *pkt;
+    size_t size;
+} RemotePortDynPkt;
+
+/*
+ * Make sure dpkt is allocated and has enough room.
+ */
+
+void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size);
+
+void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b);
+
+/*
+ * Check if the dpkt is valid. Used for debugging purposes.
+ */
+
+bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt);
+
+/*
+ * Invalidate the dpkt. Used for debugging purposes.
+ */
+
+void rp_dpkt_invalidate(RemotePortDynPkt *dpkt);
+
+void rp_dpkt_free(RemotePortDynPkt *dpkt);
+
 #endif
-- 
2.43.0