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