/* * QEMU remote port protocol parts. * * Copyright (c) 2013 Xilinx Inc * Written by Edgar E. Iglesias * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef REMOTE_PORT_PROTO_H__ #define REMOTE_PORT_PROTO_H__ #include #include #include #include /* * Remote-Port (RP) is an inter-simulator protocol. It assumes a reliable * point to point communcation with the remote simulation environment. * * Setup * In the SETUP phase a mandatory HELLO packet is exchanged with optional * CFG packets following. HELLO packets are useful to ensure that both * sides are speaking the same protocol and using compatible versions. * * CFG packets are used to negotiate configuration options. At the moment * these remain unimplemented. * * Once the session is up, communication can start through various other * commands. The list can be found further down this document. * Commands are carried over RP packets. Every RP packet contains a header * with length, flags and an ID to track potential responses. * The header is followed by a packet specific payload. You'll find the * details of the various commands packet layouts here. Some commands can * carry data/blobs in their payload. */ #define RP_VERSION_MAJOR 4 #define RP_VERSION_MINOR 3 #if defined(_WIN32) && defined(__MINGW32__) /* mingw GCC has a bug with packed attributes. */ #define PACKED __attribute__ ((gcc_struct, packed)) #else #define PACKED __attribute__ ((packed)) #endif /* Could be auto generated. */ enum rp_cmd { RP_CMD_nop = 0, RP_CMD_hello = 1, RP_CMD_cfg = 2, RP_CMD_read = 3, RP_CMD_write = 4, RP_CMD_interrupt = 5, RP_CMD_sync = 6, RP_CMD_ats_req = 7, RP_CMD_ats_inv = 8, RP_CMD_max = 8 }; enum { RP_OPT_quantum = 0, }; struct rp_cfg_state { uint64_t quantum; }; enum { RP_PKT_FLAGS_optional = 1 << 0, RP_PKT_FLAGS_response = 1 << 1, /* Posted hint. * When set this means that the receiver is not required to respond to * the message. Since it's just a hint, the sender must be prepared to * drop responses. Note that since flags are echoed back in responses * a response to a posted packet will be easy to identify early in the * protocol stack. */ RP_PKT_FLAGS_posted = 1 << 2, }; struct rp_pkt_hdr { uint32_t cmd; uint32_t len; uint32_t id; uint32_t flags; uint32_t dev; } PACKED; struct rp_pkt_cfg { struct rp_pkt_hdr hdr; uint32_t opt; uint8_t set; } PACKED; struct rp_version { uint16_t major; uint16_t minor; } PACKED; struct rp_capabilities { /* Offset from start of packet. */ uint32_t offset; uint16_t len; uint16_t reserved0; } PACKED; enum { CAP_BUSACCESS_EXT_BASE = 1, /* New header layout. */ CAP_BUSACCESS_EXT_BYTE_EN = 2, /* Support for Byte Enables. */ /* * Originally, all interrupt/wire updates over remote-port were posted. * This turned out to be a bad idea. To fix it without breaking backwards * compatibility, we add the WIRE Posted updates capability. * * If the peer supportes this, it will respect the RP_PKT_FLAGS_posted * flag. If the peer doesn't support this capability, senders need to * be aware that the peer will not respond to wire updates regardless * of the posted header-flag. */ CAP_WIRE_POSTED_UPDATES = 3, CAP_ATS = 4, /* Address translation services */ }; struct rp_pkt_hello { struct rp_pkt_hdr hdr; struct rp_version version; struct rp_capabilities caps; } PACKED; enum { /* Remote port responses. */ RP_RESP_OK = 0x0, RP_RESP_BUS_GENERIC_ERROR = 0x1, RP_RESP_ADDR_ERROR = 0x2, RP_RESP_MAX = 0xF, }; enum { RP_BUS_ATTR_EOP = (1 << 0), RP_BUS_ATTR_SECURE = (1 << 1), RP_BUS_ATTR_EXT_BASE = (1 << 2), RP_BUS_ATTR_PHYS_ADDR = (1 << 3), /* * Bits [11:8] are allocated for storing transaction response codes. * These new response codes are backward compatible as existing * implementations will not set/read these bits. * For existing implementations, these bits will be zero which is RESP_OKAY. */ RP_BUS_RESP_SHIFT = 8, RP_BUS_RESP_MASK = (RP_RESP_MAX << RP_BUS_RESP_SHIFT), }; struct rp_pkt_busaccess { struct rp_pkt_hdr hdr; uint64_t timestamp; uint64_t attributes; uint64_t addr; /* Length in bytes. */ uint32_t len; /* Width of each beat in bytes. Set to zero for unknown (let the remote side choose). */ uint32_t width; /* Width of streaming, must be a multiple of width. addr should repeat itself around this width. Set to same as len for incremental (normal) accesses. In bytes. */ uint32_t stream_width; /* Implementation specific source or master-id. */ uint16_t master_id; } PACKED; /* This is the new extended busaccess packet layout. */ struct rp_pkt_busaccess_ext_base { struct rp_pkt_hdr hdr; uint64_t timestamp; uint64_t attributes; uint64_t addr; /* Length in bytes. */ uint32_t len; /* Width of each beat in bytes. Set to zero for unknown (let the remote side choose). */ uint32_t width; /* Width of streaming, must be a multiple of width. addr should repeat itself around this width. Set to same as len for incremental (normal) accesses. In bytes. */ uint32_t stream_width; /* Implementation specific source or master-id. */ uint16_t master_id; /* ---- End of 4.0 base busaccess. ---- */ uint16_t master_id_31_16; /* MasterID bits [31:16]. */ uint32_t master_id_63_32; /* MasterID bits [63:32]. */ /* --------------------------------------------------- * Since hdr is 5 x 32bit, we are now 64bit aligned. */ uint32_t data_offset; /* Offset to data from start of pkt. */ uint32_t next_offset; /* Offset to next extension. 0 if none. */ uint32_t byte_enable_offset; uint32_t byte_enable_len; /* ---- End of CAP_BUSACCESS_EXT_BASE. ---- */ /* If new features are needed that may always occupy space * in the header, then add a new capability and extend the * this area with new fields. * Will help receivers find data_offset and next offset, * even those that don't know about extended fields. */ } PACKED; struct rp_pkt_interrupt { struct rp_pkt_hdr hdr; uint64_t timestamp; uint64_t vector; uint32_t line; uint8_t val; } PACKED; struct rp_pkt_sync { struct rp_pkt_hdr hdr; uint64_t timestamp; } PACKED; enum { RP_ATS_ATTR_exec = 1 << 0, RP_ATS_ATTR_read = 1 << 1, RP_ATS_ATTR_write = 1 << 2, }; enum { RP_ATS_RESULT_ok = 0, RP_ATS_RESULT_error = 1, }; struct rp_pkt_ats { struct rp_pkt_hdr hdr; uint64_t timestamp; uint64_t attributes; uint64_t addr; uint64_t len; uint32_t result; uint64_t reserved0; uint64_t reserved1; uint64_t reserved2; uint64_t reserved3; } PACKED; struct rp_pkt { union { struct rp_pkt_hdr hdr; struct rp_pkt_hello hello; struct rp_pkt_busaccess busaccess; struct rp_pkt_busaccess_ext_base busaccess_ext_base; struct rp_pkt_interrupt interrupt; struct rp_pkt_sync sync; struct rp_pkt_ats ats; }; }; 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); 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); } static inline void *__attribute__ ((deprecated)) rp_busaccess_dataptr(struct rp_pkt_busaccess *pkt) { /* Right after the packet. */ return pkt + 1; } /* * rp_busaccess_rx_dataptr * * Predicts the dataptr for a packet to be transmitted. * This should only be used if you are trying to keep * the entire packet in a linear buffer. */ static inline unsigned char * rp_busaccess_tx_dataptr(struct rp_peer_state *peer, struct rp_pkt_busaccess_ext_base *pkt) { unsigned char *p = (unsigned char *) pkt; if (peer->caps.busaccess_ext_base) { /* We always put our data right after the header. */ return p + sizeof *pkt; } else { /* Right after the old packet layout. */ return p + sizeof(struct rp_pkt_busaccess); } } /* * rp_busaccess_rx_dataptr * * Extracts the dataptr from a received packet. */ static inline unsigned char * rp_busaccess_rx_dataptr(struct rp_peer_state *peer, struct rp_pkt_busaccess_ext_base *pkt) { unsigned char *p = (unsigned char *) pkt; if (pkt->attributes & RP_BUS_ATTR_EXT_BASE) { return p + pkt->data_offset; } else { /* Right after the old packet layout. */ return p + sizeof(struct rp_pkt_busaccess); } } static inline unsigned char * rp_busaccess_byte_en_ptr(struct rp_peer_state *peer, struct rp_pkt_busaccess_ext_base *pkt) { unsigned char *p = (unsigned char *) pkt; if ((pkt->attributes & RP_BUS_ATTR_EXT_BASE) && pkt->byte_enable_len) { assert(pkt->byte_enable_offset >= sizeof *pkt); assert(pkt->byte_enable_offset + pkt->byte_enable_len <= pkt->hdr.len + sizeof pkt->hdr); return p + pkt->byte_enable_offset; } return NULL; } size_t __attribute__ ((deprecated)) rp_encode_read(uint32_t id, uint32_t dev, struct rp_pkt_busaccess *pkt, int64_t clk, uint16_t master_id, uint64_t addr, uint64_t attr, uint32_t size, uint32_t width, uint32_t stream_width); size_t __attribute__ ((deprecated)) rp_encode_read_resp(uint32_t id, uint32_t dev, struct rp_pkt_busaccess *pkt, int64_t clk, uint16_t master_id, uint64_t addr, uint64_t attr, uint32_t size, uint32_t width, uint32_t stream_width); size_t __attribute__ ((deprecated)) rp_encode_write(uint32_t id, uint32_t dev, struct rp_pkt_busaccess *pkt, int64_t clk, uint16_t master_id, uint64_t addr, uint64_t attr, uint32_t size, uint32_t width, uint32_t stream_width); size_t __attribute__ ((deprecated)) rp_encode_write_resp(uint32_t id, uint32_t dev, struct rp_pkt_busaccess *pkt, int64_t clk, uint16_t master_id, uint64_t addr, uint64_t attr, uint32_t size, uint32_t width, uint32_t stream_width); struct rp_encode_busaccess_in { uint32_t cmd; uint32_t id; uint32_t flags; uint32_t dev; int64_t clk; uint64_t master_id; uint64_t addr; uint64_t attr; uint32_t size; uint32_t width; uint32_t stream_width; uint32_t byte_enable_len; }; /* Prepare encode_busaccess input parameters for a packet response. */ static inline void rp_encode_busaccess_in_rsp_init(struct rp_encode_busaccess_in *in, struct rp_pkt *pkt) { memset(in, 0, sizeof *in); in->cmd = pkt->hdr.cmd; in->id = pkt->hdr.id; in->flags = pkt->hdr.flags | RP_PKT_FLAGS_response; in->dev = pkt->hdr.dev; /* FIXME: Propagate all master_id fields? */ in->master_id = pkt->busaccess.master_id; in->addr = pkt->busaccess.addr; in->size = pkt->busaccess.len; in->width = pkt->busaccess.width; in->stream_width = pkt->busaccess.stream_width; in->byte_enable_len = 0; } size_t rp_encode_busaccess(struct rp_peer_state *peer, struct rp_pkt_busaccess_ext_base *pkt, struct rp_encode_busaccess_in *in); size_t rp_encode_interrupt_f(uint32_t id, uint32_t dev, struct rp_pkt_interrupt *pkt, int64_t clk, uint32_t line, uint64_t vector, uint8_t val, uint32_t flags); size_t rp_encode_interrupt(uint32_t id, uint32_t dev, struct rp_pkt_interrupt *pkt, int64_t clk, uint32_t line, uint64_t vector, uint8_t val); size_t rp_encode_sync(uint32_t id, uint32_t dev, struct rp_pkt_sync *pkt, int64_t clk); size_t rp_encode_sync_resp(uint32_t id, uint32_t dev, struct rp_pkt_sync *pkt, int64_t clk); size_t rp_encode_ats_req(uint32_t id, uint32_t dev, struct rp_pkt_ats *pkt, int64_t clk, uint64_t attr, uint64_t addr, uint64_t size, uint64_t result, uint32_t flags); size_t rp_encode_ats_inv(uint32_t id, uint32_t dev, struct rp_pkt_ats *pkt, int64_t clk, uint64_t attr, uint64_t addr, uint64_t size, uint64_t result, uint32_t flags); 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); static inline int rp_get_busaccess_response(struct rp_pkt *pkt) { return (pkt->busaccess_ext_base.attributes & RP_BUS_RESP_MASK) >> RP_BUS_RESP_SHIFT; } #endif